如何知道jetpack compose中惰性行的视图端口中完全可见的项目?

问题描述 投票:0回答:3

我有一个包含项目的惰性行。现在,我想对每当用户滚动惰性行时在视口中完全可见的项目进行 API 调用。

我尝试过以下代码:

    listState = rememberLazyListState()
    LaunchedEffect(listState){
    snapshotFlow { listState.firstVisibleItemIndex }
    .collectLatest{
    Log.d("printed Item",  listState.firstVisibleItemIndex.toString())
    }}

这段代码的问题是:

  1. 即使第二项占据了视口,除非第一项完全不可见,否则它不会被打印。
  2. 对于平板电脑,由于屏幕尺寸较大,即使屏幕上有 2 个可见项目,也仅对第一个可见项目进行 API 调用。请参考截图。

当第一个项目部分可见且第二个项目完全可见时 [1]:https://i.stack.imgur.com/l5QcB.jpg

当第二个图块完全可见并且第一个图块完全不可见时 [2]:https://i.stack.imgur.com/6rmiQ.jpg

对于 2 个项目完全可见的平板电脑 [3]:https://i.stack.imgur.com/QYRTI.jpg

谁能告诉我如何解决我的问题?

android android-layout android-jetpack-compose
3个回答
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)
        )
    }
}

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值。


0
投票

也许您可以尝试在视频刚出屏幕时暂停视频? 这些允许视图组件不用担心列表(容器)。

要实现此功能,您应该添加

androidx.lifecycle:lifecycle-runtime-compose:2.7.+

LifecycleResumeEffect(Unit) { onPauseOrDispose { viewModel.pause() } }

但是,如果您在一个屏幕上同时显示多个视频,如何正确暂停一个视频并保留另一个视频 - 这仍然是一个开放的话题

© www.soinside.com 2019 - 2024. All rights reserved.