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

瀑布流实现思路

  • 第一种就是用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

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

(0)

相关推荐

  • Android瀑布流照片墙实现 体验不规则排列的美感

    传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳.这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面. 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于"墙"上的每张图片大小都相同的情况,如果图片的大小参差不齐,

  • Android中Spinner(下拉框)控件的使用详解

    android给我们提供了一个spinner控件,这个控件主要就是一个列表,那么我们就来说说这个控件吧,这个控件在以前的也看见过,但今天还是从新介绍一遍吧. Spinner位于 android.widget包下,每次只显示用户选中的元素,当用户再次点击时,会弹出选择列表供用户选择,而选择列表中的元素同样来自适配器.Spinner是View类得一个子类. 1.效果图 2.创建页面文件(main.xml) <Spinner android:id="@+id/spinner1" and

  • Android控件系列之TextView使用介绍

    学习目的: 1.了解在Android中如何使用TextView控件 2.掌握TextView控件重要属性 作用:TextView类似一般UI中的Label,TextBlock等控件,只是为了单纯的显示一行或多行文本 上图的XML布局如下: 复制代码 代码如下: <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_c

  • android控件封装 自己封装的dialog控件

    自定义dialog肯定是用的很多了但是感觉每次做都是很乱 单纯完成任务而已,现在封装了一下 以后用到直接copy 先上图: 主activity 复制代码 代码如下: package com.su.testcustomdialog; import com.su.testcustomdialog.MyDialog.Dialogcallback; import android.app.Activity; import android.os.Bundle; import android.view.Vie

  • Android下拉刷新上拉加载控件(适用于所有View)

    前面写过一篇关于下拉刷新控件的文章下拉刷新控件终结者:PullToRefreshLayout,后来看到好多人还有上拉加载更多的需求,于是就在前面下拉刷新控件的基础上进行了改进,加了上拉加载的功能.不仅如此,我已经把它改成了对所有View都通用!可以随心所欲使用这两个功能~~ 我做了一个大集合的demo,实现了ListView.GridView.ExpandableListView.ScrollView.WebView.ImageView.TextView的下拉刷新和上拉加载.后面会提供demo的

  • android ListView和ProgressBar(进度条控件)的使用方法

    ListView控件的使用:ListView控件里面装的是一行一行的数据,一行中可能有多列,选中一行,则该行的几列都被选中,同时可以触发一个事件,这种控件在平时还是用得很多的.使用ListView时主要是要设置一个适配器,适配器主要是用来放置一些数据.使用起来稍微有些复杂,这里用的是android自带的SimpleAdapter,形式如下:android.widget.SimpleAdapter.SimpleAdapter(Context context, List<? extends Map<

  • Android RecyclerView详解之实现 ListView GridView瀑布流效果

     什么是RecyclerView RecyclerView 是Google推出的最新的 替代ListView.GridView的组件,RecyclerView是用来显示大量数据的容器,并通过有限数量的子View,来提高滚动时的性能. 与ListView不同,RecyclerView 不再负责布局,而是专注于布局复用.布局主要通过 LayoutManager来管理,目前提供了3种常用的布局管理: LinearLayoutManager 线性布局管理器 (ListView效果) GridLayout

  • android 自定义控件 自定义属性详细介绍

    自定义控件在android中无处不见,自定义控件给了我们很大的方便.比如说,一个视图为imageview ,imagebutton ,textview 等诸多控件的组合,用的地方有很多,我们不可能每次都来写3个的组合,既浪费时间,效率又低.在这种情况下,我们就可以自定义一个view来替换他们,不仅提升了效率并且在xml中运用也是相当的美观. 一.控件自定义属性介绍 以下示例中代码均在values/attrs.xml 中定义,属性均可随意命名. 1. reference:参考某一资源ID. 示例:

  • Android App中实现相册瀑布流展示的实例分享

    传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳.这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面. 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于"墙"上的每张图片大小都相同的情况,如果图片的大小参差不齐,

  • Android控件之ListView用法实例详解

    本文实例讲述了Android控件之ListView用法.分享给大家供大家参考.具体如下: 示例一: 在android开发中ListView是比较常用的组件,它以列表的形式展示具体内容,并且能够根据数据的长度自适应显示. main.xml布局文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout android:id="@+id/LinearLayout01" androi

  • Android开发之瀑布流控件的实现与使用方法示例

    本文实例讲述了Android开发之瀑布流控件的实现与使用方法.分享给大家供大家参考,具体如下: public class FlowLayout extends ViewGroup { /**行里子view之间的行距离*/ public int mHorizontolSpace = Util.getDimen(R.dimen.top_padding); /**行里子view之间的垂直距离*/ public int mVerticalSpace = Util.getDimen(R.dimen.top

随机推荐