【Flutter】自定义ListView开发记录(六)—— 支持无限滚动

1,120 阅读4分钟

前言

之前设想了一下仅仅计算展示所需页面的方案,虽说验证了下效果,正如所设想的那样,计算耗时完全没啥太大影响,

比如说如下图所示:平均耗时基本是0-2ms间,毫无压力;

QQ20211216-183125-HD.gif

但是往下继续开发的时候发现了一个问题:

由于ListView仅仅需要提供三个页面:当前页、上一页、下一页;所以其childCount = 3;由此带来一个问题:

ListView还要支持无限滚动,才能让阅读页一直往下翻下去;

那么又回到了自定义ListView的开发环节,现在就来看看这块应该怎么搞;

方案

以Android为例,通用的无限滚动分两种实现方式(可以看这篇,写的比较详细# ViewPager两种方式实现无限轮播):

  • 方式一:将ListView的item数量设置为无限,或者是像int的最大值这种特别大的数字,然后将初始位置设置为前面设置的item数量的一半;这样通过一种特别特别大的列表来伪造出一种无限滚动的效果;

  • 方式二:通过控制item的位置,举个例子,如果一个listView仅仅有3个Item,如果想营造出一种无限滚动的效果,可以将ListView的 item数量设置为 3+2(即原本item数量+2);然后当listView滚动到原本列表那三个Item的头尾位置的时候,重新设置位置信息:用图表示的话,差不多是这样:

image.png

分析

方案一

其实现方案,现在好像已经不少了,比如说flutter_swiper,它是基于PageView,如果是循环模式,就将kMiddleValue放到pageController的 initialPage 中,并将childCount加上kMaxValue;

image.png

不过这种方式,如果放在ListView上是行不通的;

在前面的分析中,已经得知在Flutter的ListView中,给定了scrollOffset后,是通过不断从当前位置开始,不断layout child,直到达到指定位置的方式,来判断是否到了展示位置;试想一下,如果你将PageController设置给ListView,并给其 initialPage 设置为一百万, 那么就要从头遍历并layout 一百万个 child,才能完成展示,这显然会非常耗时;

而在 PageView中,使用的是另一套机制:

pageView中对应 ListView的SliverList部分的widget,是 RenderSliverFillViewport,其比较特殊的部分是专门针对那种填满ViewPort的List而做的;

其能快速实现跳转到initialPage的诀窍,就是每次都是从规定的page位置重建List(仅重建没有和需要回收的部分),并不像ListView那样还要从当前位置不断遍历过来;

PS :这种实现方式可以结合到ListView中?说白了,改一下layout方法,新建一个jumptoIndex方法,当调用这个方法的时候加个标志,让其完全重建?(或者像Android那样,先判断当前显示区域中是否有目标,再决定是重建还是滚动)

方案二

方案二的最简单实现方式,就是给Controller加一个监听器即可实现,比如说这样:

image.png

不过这种方案有个问题,如果是滑动翻页这种普通ListView的模式,由于没有PagePhsysic的参与,ListView并不能自动归位,position中的pixels自然不能保证会像上图中所示的那样,停在0或者viewPortDimension的倍数位置上;自然就不会触发jump方法;

现在再来回顾下ListView的结构,不难得出,处理这部分的widget是SLiverList,而决定SliverList 展示位置的,自然就是position,那么该做的是就很简单了,让position的pixels一直保持在最大范围和最小范围内,比如说这样:

image.png

PS :3 是ListView本身的childCount,在这里为了快速验证,先写死;

同时需要修改下itemBuilder,让构造item的index也在3以内(当然这部分也可以由自定义的ListView本身来修改提供),childCount 大于3即可(同上,也可以又ListView本身修改):

image.png

结语

最终呢,我选择的方案是方案二,因为之前已经实现过自定义ScrollController,所以对于修改ScrollPosition来说是挺方便的~~现在来看下效果:

QQ20211217-141639-HD.gif

这样,好像从分页到展示的部分都完成了?下面就是章节内容管理提供了;搞完这个,小说阅读器部分基本就完结了;再搞搞一些骚UI,2.0的flutter_novel就可以安排上了