admin管理员组文章数量:1300010
I have investigated infinate scrolling in compose and have it working as required in a spike i have been working on recently...
heres the code:-
data class AutoScrollingModel(val key: UUID = UUID.randomUUID(), @DrawableRes val iconResource: Int, val contentDescription: String, var isFabricated: Boolean = false)
val autoScrollingList = listOf<AutoScrollingModel>(
AutoScrollingModel(iconResource = R.drawable.apple, contentDescription = "Apple"),
AutoScrollingModel(iconResource = R.drawable.banana, contentDescription = "Banana"),
AutoScrollingModel(iconResource = R.drawable.cherries, contentDescription = "cherries"),
AutoScrollingModel(iconResource = R.drawable.dates, contentDescription = "dates"),
)
the compose code resembles this:-
@Composable
fun MainUi(modifier: Modifier) {
val lazyListState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
var source by remember { mutableStateOf(autoScrollingList) }
LaunchedEffect(lazyListState) {
val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isEmpty()) return@LaunchedEffect
val lastVisibleItem = visibleItemsInfo.last()
val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
source = ArrayList(transitional)
}
}
Box {
LazyRow(
state = lazyListState,
modifier = Modifier
) {
itemsIndexed(source, key = { _, item -> item.key }) { index, item ->
AutoScrollingName(autoScrollingModel = source[index])
Spacer(modifier = Modifier.width(5.dp))
}
}
}
LaunchedEffect(Unit) {
snapshotFlow { lazyListState.layoutInfo }
.filter { it.visibleItemsInfo.isNotEmpty() }
.distinctUntilChanged()
// .onStart {
// val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
// if (visibleItemsInfo.isEmpty()) return@onStart
// val lastVisibleItem = visibleItemsInfo.last()
// val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
//
// if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
// val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
// source = ArrayList(transitional)
// }
// }
.collectLatest {
if (lazyListState.firstVisibleItemIndex == 0) source = ArrayList(source)
else {
val historicList = ArrayList(source.subList(1, source.size))
source =
if (source.last().isFabricated)
ArrayList(historicList.onEach { it.isFabricated = false } + historicList.first().copy(key = UUID.randomUUID(), isFabricated = true))
else
ArrayList(historicList + source.first())
}
}
}
LaunchedEffect(key1 = Unit) {
coroutineScope.launch {
while (true) {
lazyListState.autoScroll()
}
}
}
}
this code works fine whether I use this
LaunchedEffect(lazyListState) {
val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isEmpty()) return@LaunchedEffect // ALWAYS TRUE
val lastVisibleItem = visibleItemsInfo.last()
val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
source = ArrayList(transitional)
}
}
or this:-
// .onStart {
// val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
// if (visibleItemsInfo.isEmpty()) return@onStart // ALWAYS TRUE
// val lastVisibleItem = visibleItemsInfo.last()
// val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
//
// if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
// val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
// source = ArrayList(transitional)
// }
// }
However when i have incorporated this code into my production application niether the LaunchedEffect
or onStart
work.
this results in the side show animation only running once and not looping indefinately. one difference is that my production application has to employ androidxpose.ui.platform.ComposeView as the majority of my production application is still using xml layouts.
The issue appears to be that visibleItemsInfo.isEmpty()
is always true in both the onStart
and LaunchedEffect
blocks in my production app where in my spike app the list is populated.
I have investigated infinate scrolling in compose and have it working as required in a spike i have been working on recently...
heres the code:-
data class AutoScrollingModel(val key: UUID = UUID.randomUUID(), @DrawableRes val iconResource: Int, val contentDescription: String, var isFabricated: Boolean = false)
val autoScrollingList = listOf<AutoScrollingModel>(
AutoScrollingModel(iconResource = R.drawable.apple, contentDescription = "Apple"),
AutoScrollingModel(iconResource = R.drawable.banana, contentDescription = "Banana"),
AutoScrollingModel(iconResource = R.drawable.cherries, contentDescription = "cherries"),
AutoScrollingModel(iconResource = R.drawable.dates, contentDescription = "dates"),
)
the compose code resembles this:-
@Composable
fun MainUi(modifier: Modifier) {
val lazyListState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
var source by remember { mutableStateOf(autoScrollingList) }
LaunchedEffect(lazyListState) {
val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isEmpty()) return@LaunchedEffect
val lastVisibleItem = visibleItemsInfo.last()
val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
source = ArrayList(transitional)
}
}
Box {
LazyRow(
state = lazyListState,
modifier = Modifier
) {
itemsIndexed(source, key = { _, item -> item.key }) { index, item ->
AutoScrollingName(autoScrollingModel = source[index])
Spacer(modifier = Modifier.width(5.dp))
}
}
}
LaunchedEffect(Unit) {
snapshotFlow { lazyListState.layoutInfo }
.filter { it.visibleItemsInfo.isNotEmpty() }
.distinctUntilChanged()
// .onStart {
// val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
// if (visibleItemsInfo.isEmpty()) return@onStart
// val lastVisibleItem = visibleItemsInfo.last()
// val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
//
// if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
// val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
// source = ArrayList(transitional)
// }
// }
.collectLatest {
if (lazyListState.firstVisibleItemIndex == 0) source = ArrayList(source)
else {
val historicList = ArrayList(source.subList(1, source.size))
source =
if (source.last().isFabricated)
ArrayList(historicList.onEach { it.isFabricated = false } + historicList.first().copy(key = UUID.randomUUID(), isFabricated = true))
else
ArrayList(historicList + source.first())
}
}
}
LaunchedEffect(key1 = Unit) {
coroutineScope.launch {
while (true) {
lazyListState.autoScroll()
}
}
}
}
this code works fine whether I use this
LaunchedEffect(lazyListState) {
val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isEmpty()) return@LaunchedEffect // ALWAYS TRUE
val lastVisibleItem = visibleItemsInfo.last()
val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
source = ArrayList(transitional)
}
}
or this:-
// .onStart {
// val visibleItemsInfo = lazyListState.layoutInfo.visibleItemsInfo
// if (visibleItemsInfo.isEmpty()) return@onStart // ALWAYS TRUE
// val lastVisibleItem = visibleItemsInfo.last()
// val viewportDimension = lazyListState.layoutInfo.viewportEndOffset + lazyListState.layoutInfo.viewportStartOffset
//
// if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
// val transitional = (source + source[0].copy(key = UUID.randomUUID(), isFabricated = true))
// source = ArrayList(transitional)
// }
// }
However when i have incorporated this code into my production application niether the LaunchedEffect
or onStart
work.
this results in the side show animation only running once and not looping indefinately. one difference is that my production application has to employ androidxpose.ui.platform.ComposeView as the majority of my production application is still using xml layouts.
The issue appears to be that visibleItemsInfo.isEmpty()
is always true in both the onStart
and LaunchedEffect
blocks in my production app where in my spike app the list is populated.
1 Answer
Reset to default 1Delay Execution Until the Layout is Ready
Try using snapshotFlow
with an additional .debounce()
to wait for the layout to settle before processing
LaunchedEffect(lazyListState) {
snapshotFlow { lazyListState.layoutInfo }
.debounce(100) // Delay slightly to ensure measurement happens
.filter { it.visibleItemsInfo.isNotEmpty() }
.distinctUntilChanged()
.collectLatest {
val visibleItemsInfo = it.visibleItemsInfo
if (visibleItemsInfo.isNotEmpty()) {
val lastVisibleItem = visibleItemsInfo.last()
val viewportDimension = it.viewportEndOffset + it.viewportStartOffset
if (source.size == (viewportDimension / lastVisibleItem.size) + 1) {
val transitional = source + source[0].copy(key = UUID.randomUUID(), isFabricated = true)
source = ArrayList(transitional)
}
}
}
}
Or
Try Adding a Timeout in LaunchedEffect
If ComposeView initializes late, you can try adding a timeout to wait before checking visibleItemsInfo
LaunchedEffect(Unit) {
delay(500) // Allow ComposeView to initialize
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo }
.filter { it.isNotEmpty() }
.collectLatest { visibleItems ->
println("Visible Items: $visibleItems")
}
}
本文标签:
版权声明:本文标题:Android compose snapshotflow for infinite paging slide show: visibleItemsInfo.isEmpty() is always true - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741652791a2390581.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论