android 如何在Jetpack Compose中检查列表项的可见性

oknrviil  于 2023-05-12  发布在  Android
关注(0)|答案(2)|浏览(305)

React NativeFlatList有一个属性viewabilityConfigCallbackPairs,您可以在其中设置:

viewabilityConfig: {
    itemVisiblePercentThreshold: 50,
    waitForInteraction: true,
  }

以检测具有50%的阈值并且在交互或滚动之后的列表的可见项目。
Jetpack Compose是否也有类似的功能?
LazyListState有一些布局信息。但我想知道是否有任何内置的组件/属性用于此用例。

编辑

我有一个cardviews列表,我想检测哪些卡项目(至少50%的卡是可见的)在显示器上可见。但它只需要在用户点击卡片或滚动列表时才能被检测到。

drnojrws

drnojrws1#

要获取当前可见项目的更新列表,可以使用特定阈值LazyListState
LazyListState暴露当前可见项目的列表List<LazyListItemInfo>。使用offsetsize属性可以很容易地计算visibility percent,从而对visibility >= threshold的可见列表应用过滤器。
LazyListItemInfo具有index属性,可用于将LazyListItemInfoMap到传递给LazyColumn的列表中的实际数据项。

fun LazyListState.visibleItems(itemVisiblePercentThreshold: Float) =
    layoutInfo
        .visibleItemsInfo
        .filter {
            visibilityPercent(it) >= itemVisiblePercentThreshold
        }

fun LazyListState.visibilityPercent(info: LazyListItemInfo): Float {
    val cutTop = max(0, layoutInfo.viewportStartOffset - info.offset)
    val cutBottom = max(0, info.offset + info.size - layoutInfo.viewportEndOffset)

    return max(0f, 100f - (cutTop + cutBottom) * 100f / info.size)
}

用法

val list = state.visibleItems(50f) // list of LazyListItemInfo

必须首先将此列表Map到LazyColumn中的相应项。

val visibleItems = state.visibleItems(50f)
            .map { listItems[it.index] }
@Composable
fun App() {
    val listItems = remember { generateFakeListItems().toMutableStateList() }

    val state = rememberLazyListState()

    LazyColumn(Modifier.fillMaxSize(), state = state) {
        items(listItems.size) {
            Item(listItems[it])
        }
    }

    val visibleItems by remember(state) {
      derivedStateOf {
        state.visibleItems(50f)
          .map { listItems[it.index] }
      }
    }
    LaunchedEffect(visibleItems) {
      Log.d(TAG, "App: $visibleItems")
    }
}

fun generateFakeListItems() = (0..100).map { "Item $it" }
r1wp621o

r1wp621o2#

  • *LazyListState#layoutInfo包含可见项**的信息。

因为你想应用一个阈值,你需要根据视口大小检查第一个和最后一个项目的位置和大小。所有其他项目是肯定可见的。
需要注意的是,由于您正在阅读state,因此应该使用**derivedStateOf**来避免冗余的重新组合。
类似于:

@Composable
private fun LazyListState.visibleItemsWithThreshold(percentThreshold: Float): List<Int> {

    return remember(this) {
        derivedStateOf {
            val visibleItemsInfo = layoutInfo.visibleItemsInfo
            if (layoutInfo.totalItemsCount == 0) {
                emptyList()
            } else {
                val fullyVisibleItemsInfo = visibleItemsInfo.toMutableList()
                val lastItem = fullyVisibleItemsInfo.last()

                val viewportHeight = layoutInfo.viewportEndOffset + layoutInfo.viewportStartOffset

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

                val firstItemIfLeft = fullyVisibleItemsInfo.firstOrNull()
                if (firstItemIfLeft != null &&
                    firstItemIfLeft.offset + (lastItem.size*percentThreshold) < layoutInfo.viewportStartOffset) {
                    fullyVisibleItemsInfo.removeFirst()
                }

                fullyVisibleItemsInfo.map { it.index }
            }
        }
    }.value
}

然后用途:

val state = rememberLazyListState()

    LazyColumn( state = state ){
       //items
    }
    val visibleItems = state.visibleItemsWithThreshold(percentThreshold = 0.5f)

通过这种方式,您可以获得阈值为50%的所有可见项的列表。您可以使用以下命令观察列表:

LaunchedEffect(visibleItems){
        Log.d(TAG, "App: $visibleItems")
    }

相关问题