前言
之前设想了一下仅仅计算展示所需页面的方案,虽说验证了下效果,正如所设想的那样,计算耗时完全没啥太大影响,
比如说如下图所示:平均耗时基本是0-2ms间,毫无压力;
但是往下继续开发的时候发现了一个问题:
由于ListView仅仅需要提供三个页面:当前页、上一页、下一页;所以其childCount = 3;由此带来一个问题:
ListView还要支持无限滚动,才能让阅读页一直往下翻下去;
那么又回到了自定义ListView的开发环节,现在就来看看这块应该怎么搞;
方案
以Android为例,通用的无限滚动分两种实现方式(可以看这篇,写的比较详细# ViewPager两种方式实现无限轮播):
-
方式一:将ListView的item数量设置为无限,或者是像int的最大值这种特别大的数字,然后将初始位置设置为前面设置的item数量的一半;这样通过一种特别特别大的列表来伪造出一种无限滚动的效果;
-
方式二:通过控制item的位置,举个例子,如果一个listView仅仅有3个Item,如果想营造出一种无限滚动的效果,可以将ListView的 item数量设置为 3+2(即原本item数量+2);然后当listView滚动到原本列表那三个Item的头尾位置的时候,重新设置位置信息:用图表示的话,差不多是这样:
分析
方案一
其实现方案,现在好像已经不少了,比如说flutter_swiper,它是基于PageView,如果是循环模式,就将kMiddleValue放到pageController的 initialPage 中,并将childCount加上kMaxValue;
不过这种方式,如果放在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加一个监听器即可实现,比如说这样:
不过这种方案有个问题,如果是滑动翻页这种普通ListView的模式,由于没有PagePhsysic的参与,ListView并不能自动归位,position中的pixels自然不能保证会像上图中所示的那样,停在0或者viewPortDimension的倍数位置上;自然就不会触发jump方法;
现在再来回顾下ListView的结构,不难得出,处理这部分的widget是SLiverList,而决定SliverList 展示位置的,自然就是position,那么该做的是就很简单了,让position的pixels一直保持在最大范围和最小范围内,比如说这样:
PS :3 是ListView本身的childCount,在这里为了快速验证,先写死;
同时需要修改下itemBuilder,让构造item的index也在3以内(当然这部分也可以由自定义的ListView本身来修改提供),childCount 大于3即可(同上,也可以又ListView本身修改):
结语
最终呢,我选择的方案是方案二,因为之前已经实现过自定义ScrollController,所以对于修改ScrollPosition来说是挺方便的~~现在来看下效果:
这样,好像从分页到展示的部分都完成了?下面就是章节内容管理提供了;搞完这个,小说阅读器部分基本就完结了;再搞搞一些骚UI,2.0的flutter_novel就可以安排上了