flutter 系列之:flutter 中常用的 ListView layout 详解
简介
ListView 是包含多个 child 组件的 widget,在 ListView 中所有的 child widget 都是以 list 的形式来呈现的,你可以自定义 List 的方向,但是和 GridView 不同的是 ListView 中的每一个 List 里面都只包含一个 widget。
今天我们来详细了解一下 ListView 的底层实现和具体的应用。
ListView 详解
和 GridView 一样,ListView 也是继承自 ScrollView,表示它是一个可以滚动的 View。
具体而言,ListView 首先继承自 BoxScrollView:
然后 BoxScrollView 又继承自 ScrollView:
ListView 中的特有属性
首先我们来看下 ListView 中的特有属性,ListView 和它的父类相比,多了三个属性,分别是 itemExtent,prototypeItem 和 childrenDelegate。
其中 itemExtent 是一个 double 类型的数据,如果给定的是一个非空值,那么表示的是 child 在 scroll 方向的 extent 大小。这个属性主要用来控制 children 的 extend 信息,这样每个 child 就不需要自行来判断自己的 extend。
使用 itemExtent 的好处在于,ListView 可以统一的在滚动机制上进行优化,从而提升性能表现。
prototypeItem 是一个 widget,从名字就可以看出,这个一个 prototype 的 widget,也就是说是一个原型,其他的 child 可以参照这个原型 widget 的大小进行 extent 的设置。
ListView 中的最后一个自定义属性是 childrenDelegate,这个 childrenDelegate 和 GridView 中的含义是一样的,用来生成 ListView 中 child。
之前我们在讲解 GirdView 的时候有提到过,GirdView 中还有一个自定义的属性叫做 gridDelegate,这个 gridDelegate 是一个 SliverGridDelegate 的实例,用来控制子组件在 GridView 中的布局。
因为 ListView 的子组件的布局是已经确定的,所以就不再需要 gridDelegate 了,这是 ListView 和 GridView 的一大区别。
ListView 作为一个继承的类,需要实现一个 buildChildLayout 的方法:
这个方法的实现逻辑和我们之前讲到的三个属性是相关联的,在 buildChildLayout 中,如果 itemExtent 有值的话,因为 itemExtent 本身就是一个固定值,所以返回的是 SliverFixedExtentList。
如果 itemExtent 没有设置,并且 prototypeItem 有值的话,返回的是一个 SliverPrototypeExtentList。
最后,如果 itemExtent 和 prototypeItem 都没有设置的话,返回的是一个 SliverList 对象。
ListView 的构造函数
和 GridView 一样,为了满足我们的多样性的设计需求,ListView 也提供了多个构造函数。
首先我们来看下 ListView 的最基本的构造函数:
这里 itemExtent 和 prototypeItem 这两个属性是外部传入的,childrenDelegate 是通过其他的参数构造而来的:
ListView 中所有的 child 组件都在 List Widget 的 children 中。
这个默认的构造函数,适用于 child 比较少的情况,因为需要一次传入所有的 child 组件到 list 中,所以对性能的影响还是挺大的,并且传入的 child 是不可变的。
如果 child 比较多的情况下,就需要使用到其他的构造函数了,比如 ListView.builder。
ListView.builder 使用的是 builder 模式来构建 child 组件,具体而言他的 childrenDelegate 实现如下:
这里的 childrenDelegate 是一个 SliverChildBuilderDelegate,通过传入 itemBuilder 和总的 itemCount 就可以实现动态创建 child 的功能。
在 ListView 的实际使用过程中,为了页面好看或者更有区分度,我们一般会在 list 的 item 中添加一些分隔符 separator,为了自动化实现这个功能,ListView 提供了一个 ListView.separated 的构造函数,用来提供 list item 中间的分隔符。
ListView.separated 需要传入两个 IndexedWidgetBuilder,分别是 itemBuilder 和 separatorBuilder。
下面是 childrenDelegate 的具体实现:
可以看到,如果 index 是 even 的话就会使用 itemBuilder 生成一个 widget,如果 index 是 odd 的话,就会使用 separatorBuilder 来生成一个 separator 的 widget。
最后,ListView 还有一个更加开放的构造函数 ListView.custom,custom 和其他构造函数不同的地方在于他可以自定义 childrenDelegate,从而提供了更多的扩展空间。
ListView 的使用
有了上面的构造函数,我们可以很方便的根据自己的需要来使用 ListView,下面是一个简单的使用图片做 child 的例子:
上面的例子中,我们使用的是 ListView.builder 构造函数,返回的 Widget 中,中的 widget 个数是 5,每个 item 是由 itemBuilder 来生成的。
这里我们把 Image 封装在一个 Container 中,并且为 Container 设置了一个 constraints 来控制图片的大小。
最终生成的界面如下:
上面的例子中,item 之间是没有分隔符的,我们可以讲上面的例子进行稍微的修改一下,使用 ListView.separated 来构造 ListView,如下所示:
这里我们需要传入 separatorBuilder 来作为分隔符,为了简单起见,我们直接使用了 Divider 这个 widget。
最后生成的界面如下:
总结
以上就是 ListView 的介绍和基本的使用。
版权声明: 本文为 InfoQ 作者【程序那些事】的原创文章。
原文链接:【http://xie.infoq.cn/article/4bd0363128bee2242f8d545ff】。文章转载请联系作者。
评论