FlatList
of React Native
has a property viewabilityConfigCallbackPairs
where you can set:
viewabilityConfig: {
itemVisiblePercentThreshold: 50,
waitForInteraction: true,
}
to detect visible items of the list with threshold of 50% and after interaction or scroll.
Does Jetpack Compose also have something similar to this?
There is LazyListState
with some layout info. But I wonder if there is anything built-in component/property for this use case.
Edit
I have a list of cardviews and I want to detect which card items (at least 50% of card is visible) are visible on display. But it needs to be detected only when the card is clicked or list is scrolled by user.
To get an updating list of currently visible items with a certain threshold LazyListState
can be used.
LazyListState
exposes the list of currently visible items List<LazyListItemInfo>
. It's easy to calculate visibility percent
using
offset
and size
properties, and thus apply a filter to the visible list for visibility >= threshold
.
LazyListItemInfo
has index
property, which can be used for mapping LazyListItemInfo
to the actual data item in the list passed to 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)
}
Usage
val list = state.visibleItems(50f) // list of LazyListItemInfo
This list has to be mapped first to corresponding items in 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" }