经过长达一个月的期末考和短学期课程,终于有时间写第三篇了。做人不能太监。不多说,直接进入主题。
Flutter 简单实现手写瀑布流 第一篇 (juejin.cn)
Flutter简单实现手写瀑布流 第二篇 (juejin.cn)
三步走
一.重写performLayout
这个部分也就是对各个元素布局信息进行设定,分三个步骤。
1.初始时,元素的布局信息设定。
首先要执行其他功能逻辑,得保证元素充满一整行。这部分在源码(276-361,myrender.dart),这里得实现也很简单,向Element要子元素,满了就下一步,填充不满就进入输出布局信息,就return。
2.视窗上移,元素的布局信息设定。
视窗上移,也就是手指从上往下滑的过程。
这里不能随便找到顶部最短的卡片,然后获得其index-1对应的元素进行布局,这样会导致乱序,这里自己随便找张草稿纸画画就明白了。
如何实现?参考别人的思路,总结大概有两种,分页(没思路)或者记录下标,这里我选择了后者,即记录已经布局的元素对应哪个列,到时候布局就直接取。源码中叫layoutChildren(254行)。
视窗上移动的时候,得已经布局的元素中寻找顶部最短的元素,获取他们的上方坐标,用于新元素的布局。这里我创建了一个和纵轴元素个数长度相同的List(源码中叫 leadingChildContainer)来存放顶部元素。
需要布局的时候遍历这个表,获得,找到顶部最短元素,然后获得新元素进行布局,同时更新该表,这里来个动图演示一下。假设List存的下标为{[1,4],[2,5],[3,6]},而当前只布局到4,5,6三个元素。视窗上移时触发更新,找到最短的元素6,查表得上一个元素为3,于是找Element取下标为3的元素,同时更新leadingChildContainer(RenderBox)为[4,5,3]。
布局停止的条件为该容器的 layoutOffset + paintExtentOf < scrollOffset的时候停止。源码(195行)
3.视窗下移,元素的布局信息设定。
视窗下移,也就是手指从下往上滑的过程。
这个时候得从已经布局的元素中寻找底部最短的元素,获取他的底部坐标,用于新元素的布局。同样,这里我创建了一个和纵轴元素个数长度相同的List(源码中叫 trailingChildContainer)来存放底部元素。
需要布局的时候只需从这个表遍历,找到底部最短元素,布局新元素,同时再次更新该表。这样找底部最短元素就没必要把所有元素遍历一边了,算是个小优化把。
这里上个动图演示一下。这里List的元素(RenderBox)由1,2,3变成了4,2,3。
布局到List中最短元素的layoutOffset(相对于起始位置的偏移量)大于targetEndScrollOffset的时候停止,这部分的逻辑在(387-416)。
4.回收超出缓存范围的所有元素
这里的逻辑也很简单,两个步骤:回收上方元素和回收下方元素。具体实现也很简单,使用Element提供的removeChild回收的时候记得更新上述的两个Container。(源码144,164)
二.稍微改改paint方法
这边只需更改crossAxisDelta为对应的parentData即可,这个属性是控制绘制的位置,即距离屏幕左侧的偏移量。
final double crossAxisDelta =
(child.parentData as FlowSliverParentData).crossAxisPosition;
三.悄悄改改官方的SliverMultiBoxAdaptorElement
官方提供的新建元素方法createChild有保证元素是连续递增的要求。由于视窗上移的时候新生成的元素不一定是连续(但肯定是递增),于是这里进行重写,修改文件为silver.dart中的上述类内,代码就不贴在这里了,放在源码的render里面。
总结
仅供学习,因为是对官方源码的修改,写出了💩山(至少比之前的啥都没有好),能处理部分布局图片的情况源码在此。至于视窗那部分的源码还没磕过,如果真要以一个较为清晰的思路实现,还是得自己从Element和Widget慢慢搭积木,关于滑动,这只是冰山一角,上个最终效果,到此完结撒花。