如何知道Jetpack Compose中懒加载行完全可见的项目?

12
我有一个包含项目的延迟加载行。现在,每当用户滚动该延迟加载行时,我想为完全可见于视口中的项目进行API调用。
我尝试了以下代码:
    listState = rememberLazyListState()
    LaunchedEffect(listState){
    snapshotFlow { listState.firstVisibleItemIndex }
    .collectLatest{
    Log.d("printed Item",  listState.firstVisibleItemIndex.toString())
    }}

这段代码存在以下问题:
  1. 即使第二个项目在视口中,只有当第一个项目完全不可见时,它才会被打印。
  2. 对于平板电脑,由于其大屏幕尺寸,即使屏幕上有2个可见项,仅为第一个可见项调用API。 请参考截图。

当第一个项目部分可见且第二个项目完全可见时 [1]: https://istack.dev59.com/l5QcB.webp

当第二个瓷砖完全可见且第一个瓷砖完全不可见时 [2]: https://istack.dev59.com/6rmiQ.webp

对于同时完全可见的2个平板电脑 [3]: https://istack.dev59.com/QYRTI.webp

请问有人可以告诉我如何解决我的问题吗?

listState.layoutInfo.visibleItemsInfo? - Phil Dukhov
@PylypDukhov,你能否提供完整的代码来回答吗?因为我不知道如何使用它来解决问题,listState.layoutInfo.visibleItemsInfo会导致无限重组,并且返回屏幕上部分可见项和完全可见项。 - Vallamkonda Neelima
2个回答

15
所有关于可见项的信息都可以在state.layoutInfo中找到。要检查项是否可见,您需要根据视口大小比较第一个和最后一个项的位置(其他所有项肯定是可见的)。
为了仅对更改的单元格进行重新组合,您可以再次使用derivedStateOf来将索引转换为特定的布尔状态,并将其放在item中。
val state = rememberLazyListState()
val fullyVisibleIndices: List<Int> by remember {
    derivedStateOf {
        val layoutInfo = state.layoutInfo
        val visibleItemsInfo = layoutInfo.visibleItemsInfo
        if (visibleItemsInfo.isEmpty()) {
            emptyList()
        } else {
            val fullyVisibleItemsInfo = visibleItemsInfo.toMutableList()

            val lastItem = fullyVisibleItemsInfo.last()

            val viewportHeight = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset

            if (lastItem.offset + lastItem.size > viewportHeight) {
                fullyVisibleItemsInfo.removeLast()
            }

            val firstItemIfLeft = fullyVisibleItemsInfo.firstOrNull()
            if (firstItemIfLeft != null && firstItemIfLeft.offset < layoutInfo.viewportStartOffset) {
                fullyVisibleItemsInfo.removeFirst()
            }

            fullyVisibleItemsInfo.map { it.index }
        }
    }
}
LazyColumn(
    state = state,
    contentPadding = PaddingValues(30.dp)
) {
    items(100) { index -> 
        val isVisible by remember(index) {
            derivedStateOf {
                fullyVisibleIndices.contains(index)
            }
        }
        Text(
            index.toString(),
            modifier = Modifier
                .background(if (isVisible) Color.Green else Color.Transparent)
                .padding(30.dp)
        )
    }
}

这个解决方案只适用于具有同质列表的情况,其中所有项目都相同,但如果您在同一个LazyColumn中具有多个items()的异质列表,则无法使用此解决方案。 - Kinjal Rathod
@KinjalRathod 这个解决方案不依赖于项目的大小/类型,因此适用于任何类型的内容。但是,如果您有多个item/items调用,则必须计算相对项目索引,这是 LazyColumn 唯一的API。 - Phil Dukhov
@PhilDukhov 这个解决方案存在你提到的性能问题。如何在项目级别上实现这个功能呢?我正在尝试在视频滚动到可见区域之外时暂停播放。 - 500865
@500865 我已更新我的答案 - Phil Dukhov
有趣。但这是否会导致一个完全懒惰的作品列表呢?我会尝试并告诉你。 - 500865
显示剩余2条评论

0

另一个解决方案是使用可见卡片的偏移量

val currentVisibleIndex = remember {
                derivedStateOf {
                    val visibleItem = listState.layoutInfo.visibleItemsInfo

                    visibleItem.firstOrNull()?.index?.let { currentIndex ->
                        if (visibleItem.firstOrNull()?.offset!! == 0) {
                           currentIndex
                        } else if (visibleItem.firstOrNull()?.offset!! > LEFT_OFFSET) { //value should be checked based on your card size ex: -234 means card is partially visible on screen
                            if (currentIndex < list.size) {
                               currentIndex + 1
                            } else {
                               currentIndex
                            }
                        } else if (visibleItem.firstOrNull()?.offset!! > RIGHT_OFFSET){
                            if (currentIndex-1 >= 0) {
                               currentIndex - 1
                            } else {
                               currentIndex
                            }
                        } else {
                            currentIndex
                        }
                    }
                }
            }

您可以根据您的卡片大小通过日志检查LEFT和RIGHT_OFFSET值。


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