Jetpack Compose:滚动到底部监听器(列表末尾)

17

我想知道在达到列表底部时(类似于recyclerView.canScrollVertically(1)),是否可以在@Compose函数内获取观察者。

提前致谢。

6个回答

19

你可以使用rememberLazyListState()并比较

scrollState.layoutInfo.visibleItemsInfo.lastOrNull()?.index == scrollState.layoutInfo.totalItemsCount - 1

使用示例:

首先将上述命令作为扩展添加(例如,extensions.kt 文件):

fun LazyListState.isScrolledToEnd() = layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1

然后将其用于以下代码中:

@Compose
fun PostsList() {
  val scrollState = rememberLazyListState()

  LazyColumn(
    state = scrollState,),
  ) {
     ...
  }

  // observer when reached end of list
  val endOfListReached by remember {
    derivedStateOf {
      scrollState.isScrolledToEnd()
    }
  }

  // act when end of list reached
  LaunchedEffect(endOfListReached) {
    // do your stuff
  }
}


当项目在顶部时也会触发。 - Sayok Majumder

17

对我来说,最好和最简单的解决方案是在我的LazyColumn中添加LaunchedEffect作为最后一项:

LazyColumn(modifier = Modifier.fillMaxSize()) {
    items(someItemList) { item ->
        MyItem(item = item)
    }
    item {
        LaunchedEffect(true) {
            //Do something when List end has been reached
        }
    }
}

1
这是最简单和最优雅的解决方案。 有什么缺点吗? 从我在LaunchedEffect文档中看到的内容来看,当LaunchedEffect离开组合时,启动的协程将被取消。 - gswierczynski
哦,我不知道那个。我用它从API加载下一页的项目,为此我只需从LaunchedEffect(当然是通过viewModel)启动一个新的协程,它运行良好,所以如果你有这样的问题,这就是解决方案。尽管如此,这是一个很好的观点。 - Arkadiusz Mądry
这种方法很优雅,但在使用无限加载时有一个缺点,因为LaunchedEffect将在您多次滚动列表中的同一项时继续触发。除非您手动使用某些逻辑来使其失效。 - Amal
2
另一个缺点是当最后一项立即可见时,例如当您的项目列表在加载网络之前就已经被填充时,最后一项已经触发。 - RufusInZen
这看起来不对。这个效果也会在第一次启动时就出现,即使没有滚动,因为只是向列表中添加项目并不意味着它一定滚动到底部,甚至项目是否在屏幕上可见。 - undefined

8
1.4.0-alpha03 开始,你可以使用 LazyListState#canScrollForward 来检查是否已经滑动到列表末尾。
例如:
val state = rememberLazyListState()
val isAtBottom = !state.canScrollForward

LaunchedEffect(isAtBottom){
    if (isAtBottom) doSomething()
}

在此版本之前,您可以使用LazyListState#layoutInfo来获取有关可见项的信息。请注意,您应该使用derivedStateOf以避免冗余的重新组合。

使用某物:

@Composable
private fun LazyListState.isAtBottom(): Boolean {

    return remember(this) {
        derivedStateOf {
            val visibleItemsInfo = layoutInfo.visibleItemsInfo
            if (layoutInfo.totalItemsCount == 0) {
                false
            } else {
                val lastVisibleItem = visibleItemsInfo.last()
                val viewportHeight = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset

                (lastVisibleItem.index + 1 == layoutInfo.totalItemsCount &&
                        lastVisibleItem.offset + lastVisibleItem.size <= viewportHeight)
            }
        }
    }.value
}

上面的代码不仅检查最后一个可见项是否等于列表中的最后一个索引,还检查它是否完全可见(lastVisibleItem.offset + lastVisibleItem.size <= viewportHeight)。
然后:
val state = rememberLazyListState()
var isAtBottom = state.isAtBottom()
LaunchedEffect(isAtBottom){
    if (isAtBottom) doSomething()
}

LazyColumn(
    state = state,
){
  //...
}

你是不是想说 val isAtBottom = !state.canScrollForward?如果列表无法向前滚动,则它已经到底了。 - dannyroa
1
@dannyroa 抱歉我的错。回答已更新。 - Gabriele Mariotti

5

根据其他答案,我认为recyclerView.canScrollVertically(1)最佳解释是指底部滚动。

fun LazyListState.isScrolledToTheEnd() : Boolean {
    val lastItem = layoutInfo.visibleItemsInfo.lastOrNull()
    return lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset
}

1
使用此解决方案,当查看到最后一个项目的底部时,isScrolledToTheEnd() 为 true,而另一个答案只需要最后一个项目的一部分可见。当最后一个项目足够大以至于无法全部适合视图时,这一点非常重要。 - Sjolfr
我认为条件应该更新为lastItem == null || (lastItem.index == layoutInfo.totalItemsCount - 1 && lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset) - Nasser Ghodsian

0
发现了一个比其他答案更简单的解决方案。获取列表的最后一项索引。在lazyColumn的itemsIndexed中将其与lastIndex进行比较。当到达列表末尾时,它会触发if语句。代码示例:
LazyColumn(
        modifier = Modifier
            .fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        itemsIndexed(events) { i, event  ->
            if (lastIndex == i) {
                Log.e("console log", "end of list reached $lastIndex")
            }
        }
     }

如果您有多种类型的项目,则无法正常工作。 - Skizo-ozᴉʞS ツ

0

只需使用 firstVisibleItemIndex 并将其与您的最后一个索引进行比较。如果匹配,则表示到达了末尾,否则不是。将其用作 lazyListState.firstVisibleItemIndex


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接