Flutter ListView优雅的获取第一个可见Item的Position

      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 吸顶布局模块的代码

    原文作者:baoolong
    原文地址: https://blog.csdn.net/baoolong/article/details/82744871
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞