获取jetpack中最后一个可见项目索引组成LazyColumn

Mah*_*alv 34 android android-jetpack-compose android-jetpack-compose-list android-jetpack-compose-lazy-column

我想检查列表是否滚动到列表末尾。然而lazyListState不提供此属性

为什么我需要这个?我想显示一个用于列表“滚动到末尾”的 FAB,如果最后一项已经可见则隐藏它

(注:确实如此,但它是internal

  /**
   * Non-observable way of getting the last visible item index.
   */
  internal var lastVisibleItemIndexNonObservable: DataIndex = DataIndex(0)
Run Code Online (Sandbox Code Playgroud)

不知道为什么)

val state = rememberLazyListState()
LazyColumn(
    state = state,
    modifier = modifier.fillMaxSize()
) {
    // if(state.lastVisibleItem == logs.length - 1) ...
    items(logs) { log ->
        if (log.level in viewModel.getShownLogs()) {
            LogItemScreen(log = log)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

那么,如何检查我的内容是否LazyColumn滚动到数据集的末尾?

pau*_*aap 22

以下是您实现它的一种方法:

检查是否滚动到末尾的扩展函数:

fun LazyListState.isScrolledToTheEnd() = layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1
Run Code Online (Sandbox Code Playgroud)

用法示例:

val listState = rememberLazyListState()
val listItems = (0..25).map { "Item$it" }

LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) {
    items(listItems) { item ->
        Text(text = item, modifier = Modifier.padding(16.dp))
    }
}

Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomEnd) {
    if (!listState.isScrolledToTheEnd()) {
        ExtendedFloatingActionButton(
            modifier = Modifier.padding(16.dp),
            text = { Text(text = "Go to Bottom") },
            onClick = { /* Scroll to the end */}
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 访问“LazyListState”的“layoutInfo”会导致重新组合,然后以无限组合结束。避免这种情况的一个技巧是记住“firstVisibleItemIndex”并在获取“layoutInfo”之前检查该值是否更改 (8认同)

Gab*_*tti 17

从开始,1.4.0-alpha03您可以使用它来LazyListState#canScrollForward检查是否可以向前滚动或者是否位于列表的末尾。

   val state = rememberLazyListState()
   if  (!state.canScrollForward){ /* ... */  }
Run Code Online (Sandbox Code Playgroud)

在您可以使用LazyListState#layoutInfo包含有关可见项目的信息的 之前。如果列表在底部滚动,您可以使用它来检索信息。
既然您正在阅读,state您应该使用它derivedStateOf来避免冗余的重组。

就像是:

val state = rememberLazyListState()

val isAtBottom by remember {
    derivedStateOf {
        val layoutInfo = state.layoutInfo
        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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


fra*_*ang 13

我正在分享我的解决方案,以防它对任何人有帮助。

它提供了实现问题用例所需的信息,并通过遵循https://developer.android.com/jetpack/compose/lists#control-scroll-position的建议来避免无限重组。

  1. 创建这些扩展函数来计算列表状态所需的信息:
val LazyListState.isLastItemVisible: Boolean
        get() = layoutInfo.visibleItemsInfo.lastOrNull()?.index == layoutInfo.totalItemsCount - 1
    
val LazyListState.isFirstItemVisible: Boolean
        get() = firstVisibleItemIndex == 0
Run Code Online (Sandbox Code Playgroud)
  1. 创建一个简单的数据类来保存要收集的信息:
data class ScrollContext(
    val isTop: Boolean,
    val isBottom: Boolean,
)
Run Code Online (Sandbox Code Playgroud)
  1. 创建此记住可组合项以返回先前的数据类。
@Composable
fun rememberScrollContext(listState: LazyListState): ScrollContext {
    val scrollContext by remember {
        derivedStateOf {
            ScrollContext(
                isTop = listState.isFirstItemVisible,
                isBottom = listState.isLastItemVisible
            )
        }
    }
    return scrollContext
}
Run Code Online (Sandbox Code Playgroud)

请注意,派生状态用于避免重组并提高性能。该函数需要列表状态才能在派生状态内进行计算。阅读我上面分享的链接。

  1. 将所有内容粘贴到可组合项中:
@Composable
fun CharactersList(
    state: CharactersState,
    loadNewPage: (offset: Int) -> Unit
) {
    // Important to remember the state, we need it
    val listState = rememberLazyListState()
    Box {
        LazyColumn(
            state = listState,
        ) {
            items(state.characters) { item ->
                CharacterItem(item)
            }
        }

        // We use our remember composable to get the scroll context
        val scrollContext = rememberScrollContext(listState)

        // We can do what we need, such as loading more items...
        if (scrollContext.isBottom) {
            loadNewPage(state.characters.size)
        }

        // ...or showing other elements like a text
        AnimatedVisibility(scrollContext.isBottom) {
            Text("You are in the bottom of the list")
        }

        // ...or a button to scroll up
        AnimatedVisibility(!scrollContext.isTop) {
            val coroutineScope = rememberCoroutineScope()
            Button(
                onClick = {
                    coroutineScope.launch {
                        // Animate scroll to the first item
                        listState.animateScrollToItem(index = 0)
                    }
                },
            ) {
                Icon(Icons.Rounded.ArrowUpward, contentDescription = "Go to top")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

干杯!


Mah*_*alv 3

我找到的当前解决方案是:

LazyColumn(
    state = state,
    modifier = modifier.fillMaxSize()
) {
    if ((logs.size - 1) - state.firstVisibleItemIndex == state.layoutInfo.visibleItemsInfo.size - 1) {
        println("Last visible item is actually the last item")
        // do something
    }
    items(logs) { log ->
        if (log.level in viewModel.getShownLogs()) {
            LogItemScreen(log = log)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

该语句
lastDataIndex - state.firstVisibleItemIndex == state.layoutInfo.visibleItemsInfo.size - 1
通过从第一个可见项目中减去数据集的最后一个索引并检查它是否等于可见项目计数来猜测最后一个项目