Jetpack Compose:如何重置 LazyListState?

Zia*_*abi 7 android kotlin android-jetpack-compose lazycolumn compose-recomposition

我有一个LazyColumn嵌套的LazyRows(类似于 Netflix 或 Spotify 主页提要)。简而言之,我遇到的问题是,当页面内容更改时,嵌套 LazyRows 的滚动位置不会重置。

例如,用户向下滚动主页,滚动第三个水平部分看到第五项,刷新主页,加载新的页面内容。但第三个水平部分仍然显示第五个而不是重置到初始位置。

在代码方面,这里有一些片段:

在主屏幕可组合项中:

    Scaffold {
                when (val viewState = viewModel.stateFlow.collectAsState().value) {
                    is ViewState.Success -> {
                        Box {
                            with(viewState.data) {
                                LazyColumn {
                                    itemsIndexed(sections) { index, section ->
                                    LazyRow{}
                                    //etc.
                                }
                            }
                      }
               }
    
    }
                    
Run Code Online (Sandbox Code Playgroud)

在 HomeViewModel 中,当用户刷新主屏幕时,调用此函数:

private val _stateFlow = MutableStateFlow<ViewState<HomePage>>(ViewState.Loading)
val stateFlow: StateFlow<ViewState<HomePage>>
    get() = _stateFlow

 fun loadHomeScreen() {
        viewModelScope.launch {
            repository.getHomePage()
                .collect {
                    _stateFlow.tryEmit(it)
                }
        }
    }
Run Code Online (Sandbox Code Playgroud)

现在,我已成功添加额外的代码,以便在用户刷新主屏幕时将主页的 LazyColumn 滚动到屏幕顶部。但是,我仍然不知道对于嵌套 LazyRows 来说最好的方法是什么。我不想保留对他们的引用,LazyListState因为该解决方案无法很好地扩展。但是,我很困惑为什么在发出新状态并显示新的 LazyRows 时滚动位置没有被重置。这是 Compose 中的错误吗?或者我错过了什么?

z.g*_*g.y 2

\n

如何重置LazyListState

\n
\n

恐怕没有 API 可以将可组合项“重置”回其创世状态(如果这就是您所说的“重置”的意思),在这种情况下,您希望这些Lazy组件滚动到特定的位置当一个元素添加到其中之一时,您必须执行惰性状态滚动操作。

\n
\n

例如,用户向下滚动主页,滚动第三个水平部分以查看第五项,刷新主页,加载新页面内容。但第三个水平部分仍然显示\n第五个而不是重置到初始位置。

\n
\n

我在这里假设您总是在 a 的第一个索引中插入新项目,并且每次添加新元素时LazyRow您都希望滚动到开头(索引)。0

\n
\n

我不想保留对其 LazyListState 的引用,因为该解决方案无法很好地扩展

\n

\xe2\x80\xa6

\n

但我希望有一个更简单的解决方案,因为这个解决方案的扩展性不好。

\n
\n

我不知道你所说的“扩展性不好”是什么意思,但正如@vitidev提到的,你可以利用,如果你想滚动到,LaunchedEffect你也不需要保存每个状态实例的引用添加新元素时的索引。LazyList0

\n

ViewModel所以我假设您的或与流程或协程相关的任何内容都没有问题。

\n

根据我的理解,您可以简单地调整您的设计,以避免创建和保留太多LazyListState参考。考虑下面的示例,虽然它的代码相当长,但它们都是可复制和粘贴的,因此您可以毫无问题地编译它们。

\n
data class NestedItem(\n    val display: String\n)\n\nval row1 = mutableStateListOf(\n    NestedItem("Row 1 Item 1"),\n    NestedItem("Row 1 Item 2"),\n    NestedItem("Row 1 Item 3"),\n    NestedItem("Row 1 Item 4"),\n    NestedItem("Row 1 Item 5"),\n)\n\nval row2 = mutableStateListOf(\n    NestedItem("Row 2 Item 1"),\n    NestedItem("Row 2 Item 2"),\n    NestedItem("Row 2 Item 3"),\n    NestedItem("Row 2 Item 4"),\n    NestedItem("Row 2 Item 5"),\n)\n\nval row3 = mutableStateListOf(\n    NestedItem("Row 3 Item 1"),\n    NestedItem("Row 3 Item 2"),\n    NestedItem("Row 3 Item 3"),\n    NestedItem("Row 3 Item 4"),\n    NestedItem("Row 3 Item 5"),\n)\n\nval row4 = mutableStateListOf(\n    NestedItem("Row 4 Item 1"),\n    NestedItem("Row 4 Item 2"),\n    NestedItem("Row 4 Item 3"),\n    NestedItem("Row 4 Item 4"),\n    NestedItem("Row 4 Item 5"),\n)\n\nvar nest = listOf(\n    row1, row2, row3, row4\n)\n\n@Composable\nfun MyScreen(nestedList: List<List<NestedItem>>) {\n    Column {\n        Row(\n            modifier = Modifier\n                .fillMaxWidth()\n                .wrapContentHeight(),\n            horizontalArrangement = Arrangement.SpaceEvenly\n        ) {\n            repeat(4) { idx ->\n                Button(onClick = {\n                    when (idx) {\n                        0 -> {\n                            row1.add(0, NestedItem("New Item At Row ${idx + 1}"))\n                        }\n                        1 -> {\n                            row2.add(0, NestedItem("New Item At Row ${idx + 1}"))\n                        }\n                        2 -> {\n                            row3.add(0, NestedItem("New Item At Row ${idx + 1}"))\n                        }\n                        3 -> {\n                            row4.add(0, NestedItem("New Item At Row ${idx + 1}"))\n                        }\n                    }\n                }) {\n                    Text(text = "R${idx + 1} Add")\n                }\n            }\n        }\n\n        MyScreen(nestedList = nest)\n    }\n}\n\n\n@Composable\nfun MyContent(nestedList: List<List<NestedItem>>) {\n    LazyColumn(\n        modifier = Modifier.fillMaxWidth()\n    ) {\n        items(nestedList) {\n            MyEveryRow(rowItems = it)\n        }\n    }\n}\n\n// we leave all the scrolling to LaunchedEffect here everytime the size changes\n@Composable\nfun MyEveryRow(rowItems: List<NestedItem> ) {\n\n    val lazyRowState = rememberLazyListState()\n    \n    LaunchedEffect(rowItems.size) { \n        lazyRowState.animateScrollToItem(0)\n    }\n\n    LazyRow(\n        state = lazyRowState\n    ) {\n        items(rowItems) { item ->\n            MyCell(item = item)\n        }\n    }\n}\n\n@Composable\nfun MyCell(item: NestedItem) {\n\n    Box(\n        modifier = Modifier\n            .size(width = 150.dp, height = 150.dp)\n            .border(BorderStroke(Dp.Hairline, Color.DarkGray)),\n        contentAlignment = Alignment.Center\n    ) {\n        Text(text = item.display)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

上面的代码将产生此输出,将新项目添加到第一个索引并滚动到它,而不需要太多LazyListState引用。

\n

在此输入图像描述

\n

在这里,我们使用项目的大小作为关键,LaunchedEffect因此当添加新元素时,将执行此副作用。

\n
LaunchedEffect(rowItems.size) { \n     lazyRowState.animateScrollToItem(0)\n}\n
Run Code Online (Sandbox Code Playgroud)\n