likes
comments
collection
share

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

作者站长头像
站长
· 阅读数 51

theme: condensed-night-purple

前言

前面一篇对SLiverList的结构做了解析,去了解它的大概运行步骤这篇来分析下具体是如何运行起来的;

SliverList 如何运行起来的

上篇简单分析了一下SliverList的组成、各个组成的大体功能,可以确定下来:

具体驱动SliverList整体部分的,就放在RenderSliverList的performLayout方法中,现在逐行分析一下代码:

开始layout

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

首先是眼熟的 SliverConstraints ,ViewPort 所创建的SliverConstraints 在这里正式开始生效;

接下来是childManager.didStartLayout ;但是这个方法仅仅做了个assert,并无实际逻辑;或许仅仅当作一个生命周期的形式?

childManager.setDidUnderflow 方法更改了一个名为_didUnderflow的布尔值;这个布尔值仅在Element,也就是childManager 中起作用,按照注释的说明:这里是为了确保能更新最大偏移量

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

回到RenderSliverList , 接下来是一堆数据的初始化,跳过~

一段让人在意的注释

接下来是个比较长的注释,好像很重要的样子:

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

用我的工地英语大概瞄了一眼,应该是个踩坑心得,大体意思是:

本来想着直接根据ScrollOffset去找到对应的child,然后往后或者往前铺满ViewPort就完事了;但是用这种方式会导致布局的时候,没布局的child被删掉了的时候,列表会不一致,进而导致对不上的节点被重新创建;所以解决方式是直接从当前第一个child开始,逐个判断铺满ViewPort;

这个是什么样的情况?好像没太理解~

layout启动前确保有内容初始节点

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

这段是保险起见,在刚开始的时候去判断一下有没有初始节点,没有的话加一个,加不上就结束;

设置追踪边界

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制 之后定义了两个边界child,在这俩范围内的会被追踪;

这两个变量,一个表示开头已经layout过的child;一个表示结尾已经Layout过的变量

去除不需要绘制的节点

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制 这段是判断一下当前的child是否有scrollOffset,如果没有的话,就遍历一下,直到找到一个ScroollOffset不为空的,并将循环次数传给CollectGarbage方法;最后将那个scrollOffset不为空的尝试添加并设置为初始节点;

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

CollectGarbage会根据传入的数据,不断调用 _destroyOrCacheChild 方法;

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

_destroyOrCacheChild 方法所做的事就是调用childManager.removeChild 方法,更新Element树,保证Element树对应正确;

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

removeChild方法会标记dirty,然后更新_childElements;

回到RenderObject层:

找到规定要求的ScrollOffset对应的child节点

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

接下来这个有点长的方法,但所做的事也就一件:不断往firstChild前插View,直到达到符合constraint中规定的ScrollOffset 的地步

盲猜是为了 scrollTo 之类的方法;

先分析前面部分;

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

首先是一个for循环,将firstChild的scrollOffset作为初始值,每次判断是否大于 constant.ScrollOffset 是的话,会调用 insertAndLayoutLeadingChild 方法,更新最新child,如果最新child为空,那么判断是不是到顶了,是的话layout firstChild,中断循环;否则返回,并要求修正ScrollOffset;

inserAndLayoutLeadingChild方法中,也是调用了childManager来更新Element:

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

后半部分:

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

所做的事,一个是因为可能存在双精度的错误,进行一个修正;另一个就是最后不断更新ParentData中的数据,并更新leading和trailing 这两个范围标识;

确保如果ScrollOffset在0的位置,那么firstChild即是内容首节点,偏移量也是0

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

这部分所做的事,正如注释所述:

让0位置的一定是整个ListView的开头,如果不是,就找到第一个child,更新其geometry,要求修正;

准备开始填充满ViewPort

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

经过重重检测、判断,能到这里的部分,说明 earliestUsefulChild 对应的是firstChild,当前内容的第一个child,同时也不会超过scrollOffset;可以以此为开头,去填满Viewport了;

当然加了两个assert去检测一下;

确保第一个节点经过了测量

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

如果的检测都没有经过,比如说,scrollOffset本身就为0的那种,那么不符合前面的任何一个检测,自然首节点没有经过layout,leadingChildWithLayout 也自然为空,在这里做下初始化;

定义变量、方法

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

在这里根据firstChild,初始化了一些变量;不过重点还是这个 advance 检测方法:

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

  • 首先判断一下当前待检测的child是否是结尾已经布局好了的child,如果是的话,标记一个不在范围内;
  • 获取链表中下一个child
  • 下一个child为空,标记为不在范围内;
  • 如果发现不在范围内
    • 如果下一个child为空,尝试添加一个,添加失败的话,直接返回,没必要再检测了;
    • 如果链表中已经有了,那么直接layout
    • 更新 trailingChildWithLayout ;
  • 更新 parentData;
  • 更新 EndScrollOffset ;

填充

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

按照注释说明,这里是寻找到第一个偏移量小于要求的ScrollOffset的位置;

同时如果endScrollOffset 小的话,那说明已经过了缓存区,该回收了;

如果无法再增加下一个child,那么要求修正;

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

这里就是真正填满的地方;

如果就是无法添加,那是到头了;

回收

上面填充过程一开始,记录和计算了了一个leadingGarbage,另一个结尾的trailingGarbage还没计算,所以下面计算这块:

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

因为经过advance 方法,child已经设置为规定缓存区中的最后一个,如果最后一个还有nextChild,说明nextChild已经移出缓存区,该被回收了。

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

最后,将上面两个计算后的数字放到collectGarbage方法中,进行回收;

构建geometry

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

将包括可滑动范围、绘制偏移量之类的信息填到geometry中,返回给父类;

结束layout

【Flutter】熊孩子拆组件系列之拆ListView(八)—— SliverList的运作机制

总结:

SliverList 运行的原理其实也不复杂,一句话说就是:

根据ViewPort构造的SliverConstraint中的ScrollOffset和cache范围;先计算定位到第一个view在哪,然后不断插入,直到填满;

转载自:https://juejin.cn/post/7027032862939414559
评论
请登录