android中UIColletionView瀑布流布局实现思路以及封装的实现

 更新时间:2024年04月09日 16:39:44   作者:Deft_MKJing宓珂璟  
本篇文章主要介绍了android中UIColletionView瀑布流布局实现思路以及封装的实现,具有一定的参考价值,有兴趣的可以了解一下。<BR>

瀑布流实现思路

  • 第一种就是用ScrollView来进行实现,由于它不具备复用的功能,因此我们需要自己写一套类似复用的模块来进行优化
  • 第二种就是利用apple做好的复用模块,自定义UIColletionLayout来实现瀑布流,想想也是第二种实现起来更快更优,OK,封装一个小小的框架来试试

默认两列 

其他案例

  

上面的动画切换布局也是自定义UICollectionLayout来进行布局的,简单的静态图片布局展示其实就重写几个方法就可以了

1.prepareLayout 每次重新刷新collectionView的时候会调用一次,做一些初始化的工作

2.layoutAttributesForElementsInRect 返回已经制定好之后的每个cell对应的attribute属性对象进行布局

3.layoutAttributesForItemAtIndexPath 该方法会一直调用,每次cell出来就会根据对应的indexpath来进行方法调用,因此关键布局代码就可以放置在这里进行重新计算

4.collectionViewContentSize 计算整体的大小,实现滚动

上面插入样式实现的传送门

瀑布流实现分析

1.基本变量的声明

// 每一列的间距
static const CGFloat MKJDefaultColumnMargin = 10;
// 每一行间距
static const CGFloat MKJDefaultRowMargin = 10;
// 整体的上间距,左间距,下间距,右间距
static const UIEdgeInsets MKJDefaultEdgeInsets = {10,10,10,10};
// 默认是多少列
static const NSUInteger MKJDefaultColumnCounts = 2;

@interface MKJWaterFallLayout ()

@property (nonatomic,strong) NSMutableArray *attributeArr; // cell属性的数组
@property (nonatomic,strong) NSMutableArray *columnHeightArr; // 每列的高度数组

@end

2.初始化

// 每次刷新会调用一次
- (void)prepareLayout
{
  [super prepareLayout];

  // 每次重新刷新的时候清除之前的所有高度值,默认就是UIEdg给定的top
  [self.columnHeightArr removeAllObjects];
  for (NSInteger i = 0; i < [self columnCount]; i++) {
    [self.columnHeightArr addObject:@([self insetMargin].top)];
  }
  // 每次刷新把对应的att属性清空
  [self.attributeArr removeAllObjects];
  // 初始化一次每个cell对应的attribute属性
  NSInteger count = [self.collectionView numberOfItemsInSection:0];
  for (NSInteger i = 0; i < count; i++) {
    NSIndexPath *indexpath = [NSIndexPath indexPathForItem:i inSection:0];
    UICollectionViewLayoutAttributes *attribute = [self layoutAttributesForItemAtIndexPath:indexpath];
    [self.attributeArr addObject:attribute];
  }
}

3.关键计算代码

// 返回attribute属性数组决定最后的排布
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
  return self.attributeArr;
}

// 返回对应的indexpath下每个cell的属性 cell的出现会一直刷新该方法
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
  // 初始化布局属性---> 对应的indexpath
  UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];

  CGFloat collectionW = self.collectionView.frame.size.width;

  // 宽度是根据列数和间距固定算出来的
  CGFloat width = (collectionW - [self insetMargin].left - [self insetMargin].right - ([self columnCount] - 1) * [self columnMargin]) / [self columnCount];

  // 高度是根据代理的数据源返回比例计算的
  CGFloat height = [self.delegate MKJWaterFallLayout:self heightForItemAtIndexPath:indexPath] * width;
  // X 和 Y值都是在找出最小column之后才能确定,核心就是根据列数,找出最小高度的那一列
  // 先取出第一个默认是最小的
  CGFloat minColumnHeight = [self.columnHeightArr[0] doubleValue];
  // 默认最小的是第0列
  NSUInteger finalCol = 0;
  for (NSInteger i = 1 ; i < [self columnCount]; i++) {
    CGFloat currentColHeight = [self.columnHeightArr[i] doubleValue];
    if (minColumnHeight > currentColHeight) {
      minColumnHeight = currentColHeight;
      finalCol = i;
    }
  }

  // x,y值是根据最小高度列算出来的
  CGFloat x = [self insetMargin].left + (width + [self columnMargin]) * finalCol;
  CGFloat y = minColumnHeight;
  // 当你是一个行排布的时候 默认是top值,不需要加间距
  NSInteger count = indexPath.item;
  if ((count / ([self columnCount])) >= 1) {
    y += [self rowMargin];
  }
  att.frame = CGRectMake(x, y, width, height);
  self.columnHeightArr[finalCol] = @(CGRectGetMaxY(att.frame));
  return att;

}

这里的计算简概括为就是对每个cell进行frame的计算

1.宽度的计算是根据列间距和整体左右间距以及行数进行限制,这些参数可以是固定值,也可以是代理传进去的

2.高度的计算必定是根据外部代理进行计算的

3.X值的计算是根据这个框架内部的每一列的高度数组进行之前的缓存高度,进行最小值计算,然后拿出最小值对应的列数,根据上面算出来的高度进行X值的计算

4.Y值的计算和X值一样,根据给定的数组,比出最小高度列的列数,根据数组的高度,计算出对应的Y值,最终再进行数组对应列数的高度更新

4.获取实际能容大小,让其可以滚动

// 计算出滚动区域的大小
- (CGSize)collectionViewContentSize
{
  CGFloat maxColumHeight = [self.columnHeightArr[0] doubleValue];
  for (NSInteger i = 1; i < [self columnCount]; i++) {
    CGFloat currentColHeight = [self.columnHeightArr[i] doubleValue];
    if (maxColumHeight < currentColHeight) {
      maxColumHeight = currentColHeight;
    }
  }
  return CGSizeMake(0, maxColumHeight + [self insetMargin].bottom);

}

5.这个小框架已经封装好了,简单介绍下用法

// 声明如下
- (UICollectionView *)colletionView
{
  if (_colletionView == nil) {
    MKJWaterFallLayout *layout = [[MKJWaterFallLayout alloc] init];
    layout.delegate = self;
    UICollectionView *collectionV = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
    collectionV.backgroundColor = [UIColor redColor];
    [collectionV registerNib:[UINib nibWithNibName:productID bundle:nil] forCellWithReuseIdentifier:productID];
    collectionV.delegate = self;
    collectionV.dataSource = self;
    _colletionView = collectionV;

  }
  return _colletionView;
}

// 内部行数,间距的控制
#pragma mark - waterfallLayoutDelegate
- (CGFloat)MKJWaterFallLayout:(MKJWaterFallLayout *)layout heightForItemAtIndexPath:(NSIndexPath *)indexPath
{
  // 返回宽度和高度比例
  MKJProductModel *product = self.dataSource[indexPath.item];
  return product.h / product.w;
}

// 控制列间距
- (CGFloat)columnMarginForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
  return 10;
}
// 控制行间距
- (CGFloat)rowMarginForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
  return 30;
}
// 控制列数
- (NSUInteger)columnCountForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
//  if (self.dataSource.count > 50) {
//    return 3;
//  }
  return 3;
}
// 控制整体上左下右间距
- (UIEdgeInsets)insetForWaterFallLayout:(MKJWaterFallLayout *)collectionViewLayout
{
  return UIEdgeInsetsMake(10, 10, 10, 10);
}

Demo地址: http://xiazai.jb51.net/201702/yuanma/MKJWaterFallLayout_jb51.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • Android的文本和输入之创建输入法教程

    Android的文本和输入之创建输入法教程

    输入法编辑器(IME)是让用户输入文本的控件,Android提供了一个可扩展的的输入法的框架,它允许应用程序给用户提供另外的输入法,如软键盘或语音输入,需要的朋友可以了解下
    2012-12-12
  • Android Webview使用小结

    Android Webview使用小结

    这篇文章主要针对Android Webview的使用方法为大家进行了详细的总结,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Android中BroadcastReceiver案例讲解

    Android中BroadcastReceiver案例讲解

    今天小编就为大家分享一篇关于Android中BroadcastReceiver案例讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • Android编程实现Gallery中每次滑动只显示一页的方法

    Android编程实现Gallery中每次滑动只显示一页的方法

    这篇文章主要介绍了Android编程实现Gallery中每次滑动只显示一页的方法,涉及Android扩展Gallery控件实现翻页效果控制的功能,涉及Android事件响应及属性控制的相关技巧,需要的朋友可以参考下
    2015-11-11
  • Android中LinearLayout布局的常用属性总结

    Android中LinearLayout布局的常用属性总结

    这篇文章主要介绍了Android中LinearLayout布局的常用属性总结,包括居中、重心、比例等线性布局中的基本设置,需要的朋友可以参考下
    2016-04-04
  • Android使用自定义View实现饼状图的实例代码

    Android使用自定义View实现饼状图的实例代码

    这篇文章主要介绍了Android使用自定义View实现饼状图的实例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Flutter WillPopScope拦截返回事件原理示例详解

    Flutter WillPopScope拦截返回事件原理示例详解

    这篇文章主要为大家介绍了Flutter WillPopScope拦截返回事件原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Android编程实现大图滚动显示的方法

    Android编程实现大图滚动显示的方法

    这篇文章主要介绍了Android编程实现大图滚动显示的方法,涉及Android使用imageView配合onTouch事件操作图片显示的相关技巧,需要的朋友可以参考下
    2016-10-10
  • Android 8.0中一些坑以及对应的解决方法

    Android 8.0中一些坑以及对应的解决方法

    这篇文章主要给大家介绍了关于Android 8.0中一些坑以及对应的解决方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-09-09
  • Flutter中渐变色的使用案例分享

    Flutter中渐变色的使用案例分享

    在日常的开发中,UI为了让界面更加吸引人往往会在界面上用到大量的渐变色,本文将通过几个案例更好的去了解Flutter中渐变色的使用,需要的可以参考一下
    2023-06-06

最新评论