Jetpack Compose LazyColumn滑动监听

3,432 阅读1分钟

LazyColumn的滑动没有类似Recyclerview的回调监听,可以通过LazyListState来做判断 LazyListState中主要的成员有:

  • isScrollInProgress 控件是否处于滑动状态
  • firstVisibleItemIndex 列表第一个可见项的下标
  • firstVisibleItemScrollOffset 列表第一个可见项的滑动偏移,向上滑动(即正方向)时变大,向下时变小
  • layoutInfo 包含列表当前显示的布局状态的相关信息。例如当前显示的item信息,item总数,列表宽高等

思路:

  • 滑动开始与结束:在isScrollInProgress为true(即滑动状态下)的代码块中启动一个key为Unit的DisposableEffect(Unit单例不变,不会导致Effect重组),DisposableEffect进入组合(即滑动状态判断条件成立)后会启动一次,并且在退出组合后会调用onDispose,可以以此做为开始滑动与结束滑动的监听
  • 滑动方向与距离:通过firstVisibleItemIndex和firstVisibleItemScrollOffset做判断 代码如下:
  @Composable
    fun AWidget() {
        val lazyListState = rememberLazyListState()
        if (lazyListState.isScrollInProgress) {
            //进入组合后只会启动一次,
            DisposableEffect(Unit) {
                Log.d(TAG, "start scroll")
                onDispose {
                    Log.d(TAG, "stop scroll")
                }
            }

            //记录上一次第一个可见item的滑动偏移
            var lastFirstVisibleItemScrollOffset by remember {
                mutableStateOf(lazyListState.firstVisibleItemScrollOffset)
            }

            //记录上一次第一个可见item下标
            var lastFirstVisibleItemIndex by remember {
                mutableStateOf(lazyListState.firstVisibleItemIndex)
            }
            run {
                val currentFirstVisibleItemIndex = lazyListState.firstVisibleItemIndex
                //手指向上滑动(即正方向)时offset会变大,向下时变小
                val currentFirstVisibleItemScrollOffset = lazyListState.firstVisibleItemScrollOffset

                //第一个可见的item改变了
                if (currentFirstVisibleItemIndex != lastFirstVisibleItemIndex) {
                    if (currentFirstVisibleItemIndex < lastFirstVisibleItemIndex) {
                        Log.d(TAG, "向下滑动↓ ")
                    } else if (currentFirstVisibleItemIndex > lastFirstVisibleItemIndex) {
                        Log.d(TAG, "向上滑动↑")
                    }
                  	//更新记录的值,退出run代码块
                    lastFirstVisibleItemIndex = currentFirstVisibleItemIndex
                    lastFirstVisibleItemScrollOffset = currentFirstVisibleItemScrollOffset
                    return@run
                }

              	//第一个可见item当前的offset - 上一次记录的offset
                val offset =
                    currentFirstVisibleItemScrollOffset - lastFirstVisibleItemScrollOffset
                if (offset < 0) {
                    Log.d(TAG, "向下滑动↓ $offset")
                } else if (offset > 0) {
                    Log.d(TAG, "向上滑动↑ $offset")
                }
                //记录第一个可见item当前的offset
                lastFirstVisibleItemScrollOffset = currentFirstVisibleItemScrollOffset
            }
        }
        
      	//将lazyListState赋值给LazyColunm
        LazyColumn(Modifier.fillMaxSize(), state = lazyListState) {
            items(100) {
                Text(
                    text = "$it",
                    Modifier
                        .fillMaxWidth()
                        .height(140.dp)
                )
            }
        }
    }