Flutter给我们提供了丰富的控件和控制方法,但是也有不少问题,就这两天,我就遇到去获取ListView第一个可见Item的Position的问题,Flutter并没有提供方法,只能我们想办法,我记得有大佬说过ListView和Android的RecyleView一样,那ListView的ItemBuilder肯定是用多少创建多少,用哪个创建哪个,抱着试试的态度,我在ItemBuilder打印了一下,果然,他只创建当前要用的Item,下来我们看看我的思路
我们首先考虑初始化了多少个Item,这个信息对我们又什么用呢?? 这个数据我们缓存成Items的位置数组“positions”,(例如初始化Item数量为 5 ,表示初始化了0-5 Position 的 Item),初始化完成后,ListView滑动时,builder会根据需要创建新的Item,回收不用的Item。 builder每添加一个Item(前面说过,用一个添加一个,已经初始化好的Item,builder不在创建,即不再执行创建方法),我们就在初始化时缓存的位置变量“positions”的基础上改动,那Position 数组就可以自行调整头和尾了。
这样,我们就知道大概第一个可见Item就在数组变量“positions”的前几个,为什么说是前几个,而不是第一个,因为我们缓存的Position 包括 屏幕可见+缓存的,所以第一个并不一定时可见的。
好了,讲了那么多,那怎么去知道初始化了多少个Item??? 在初始化时 ,我们不确定到底会 初始创建 多少Item,数组变量也不好创建, 所以我们用一个整形变量“memoryPosition”去记录 初始化创建 多少Item,好了,我们现在知道Item数量了(包括 屏幕可见+缓存的)。 初始化Item的数量我们知道了。接下来 我们找个合适的时机将Item数量 Position 缓存成List数组—>“positions”,我是在NotificationListener的ScroStartNotification里面缓存Position的。 positions数组可以自行调整头和尾 那不就更简单了 ,看代码 ↓↓↓↓
注意:大家尽量把cacheEctent的值调小,缓存越小,后面获取position越快
List<int> positions;
int memoryPosition;
child: new ListView.builder(
cacheExtent: 30.0,
itemBuilder: (context, index) {
//根据positions==null来判断是否已经初始化
if(positions!=null) {
//已经初始化过的话 滑动自行调整头和尾
if (index > positions.last) {
positions.removeAt(0);
positions.add(index);
} else if (index < positions.first) {
positions.removeLast();
positions.insert(0, index);
}
}else{
//记录初始化了多少个Item
memoryPosition=index;
}
},),
NotificationListener的ScroStartNotification里面将变量memoryPosition 缓存成数组 positions,看代码
new NotificationListener(
onNotification: (notification){
if(notification is ScrollStartNotification){
if(positions==null) {
positions=new List();
for(int i=0 ;i<=memoryPosition;i++){
positions.add(i);
}
}
}
},
child: new ListView.builder(),
这样我们就知道头和尾了,并实时更新,可是头不一定是第一个可见Item,有可能是缓存的item,那我们怎么办呢,
我当时在想 我在调用时 如果我让 ListView在屏幕的位置 和 Item在屏幕中的位置+Item的高度 做比较 如果Item可见 ,那么Item在屏幕中的位置+Item的高度肯定大于ListView在屏幕的位置 (大家别忘了给ListView和它的Item们设置Key哟),好了,我们看代码
int firstChildPosition=positions.first;
int lastChildPosition=positions.last;
double chileGlobalPositionY;
double chileHeight;
//获取ListView在屏幕中的位置
double listViewGlobalPositionY=listViewKey.currentContext.findRenderObject().getTransformTo(null).getTranslation().y;
for(int i=firstChildPosition;i<=lastChildPosition;i++){
if(adsorptionDatas[i].adsorptionKey.currentContext==null){
continue;
}
//子控件在屏幕中的位置 用于计算第一个可见Item的位置
chileGlobalPositionY=adsorptionDatas[i].adsorptionKey.currentContext.findRenderObject().getTransformTo(null).getTranslation().y;
//控件高度 用于计算第一个可见Item的位置
chileHeight=adsorptionDatas[i].adsorptionKey.currentContext.findRenderObject().paintBounds.size.height;
//如果在屏幕中可见
if(chileGlobalPositionY+chileHeight>listViewGlobalPositionY){
//TODO i就是第一个可见Item的位置
break;
}
}
这样,我们的第一个可见Item的位置就获取到了
详细代码可参考https://github.com/baoolong/PullToRefresh 吸顶布局模块的代码