简体   繁体   English

Jetpack Compose,设置新数据时如何重置 LazyColumn position?

[英]Jetpack Compose, how to reset LazyColumn position when new data are set?

I'm working on a search page made in Compose with LazyColumn , everything works fine except for the wanted behavior of LazyColumn returing to first item when data changes.我正在使用用LazyColumn在 Compose 中制作的搜索页面上工作,除了数据更改时LazyColumn返回到第一项的所需行为外,一切正常。

This is my actual implementation of lazy column:这是我对惰性列的实际实现:

@Composable
fun <DataType : Any> GenericListView(
    itemsList: SnapshotStateList<DataType>, // this list comes from the search page viewmodel
    modifier: Modifier = Modifier.fillMaxSize(),
    spacing: Dp = 24.dp,
    padding: PaddingValues = PaddingValues(0.dp),
    item: @Composable (DataType) -> Unit
) {

    val listState: LazyListState = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()

    LazyColumn(
        verticalArrangement = Arrangement.spacedBy(spacing),
        state = listState,
        modifier = modifier.padding(padding)
    ) {
        items(itemsList) {
            item(it)
        }
    }

    SideEffect {
        Log.i("->->->->->->->", "side effect launched")
        coroutineScope.launch {
            listState.scrollToItem(0)
        }
    }
}

As docs says, SideEffect should be called everytime the function is recomposed, but it appear to be working only in debug mode with breakpoints in SideEffect , otherwise, it works only when the whole page is first created.正如文档所说,每次重组SideEffect都应调用 SideEffect ,但它似乎仅在带有断点的调试模式下工作SideEffect ,否则,它仅在首次创建整个页面时才有效。

I've already tried with LaunchedEffect instead of SideEffect , using itemsList as key , but nothing happened.我已经尝试使用LaunchedEffect而不是SideEffect ,使用itemsList作为key ,但什么也没发生。

Why my code works only in debug mode?为什么我的代码只能在调试模式下工作? Or better, an already made working solution to reset position when new data are set?或者更好的是,当设置新数据时,已经制定了重置 position 的可行解决方案?

SideEffect doesn't work because Compose is not actually recomposing the whole view when the SnapshotStateList is changed: it sees that only LazyColumn is using this state value so only this function needs to be recomposed. SideEffect不起作用,因为当SnapshotStateList更改时 Compose 实际上并没有重新组合整个视图:它看到只有LazyColumn正在使用这个 state 值,所以只有这个 function 需要重新组合。

To make it work you can change itemsList to List<DataType> and pass plain list, like itemsList = mutableStateList.toList() - it'll force whole view recomposition.为了使其工作,您可以将itemsList更改为List<DataType>并传递普通列表,例如itemsList = mutableStateList.toList() - 它会强制整个视图重新组合。


LaunchedEffect with passed SnapshotStateList doesn't work for kind of the same reason: it compares the address of the state container, which is not changed.带有传递的SnapshotStateListLaunchedEffect出于同样的原因不起作用:它比较未更改的 state 容器的地址。 To compare the items itself, you again can convert it to a plain list: in this case it'll be compared by items hash.要比较项目本身,您可以再次将其转换为普通列表:在这种情况下,它将通过项目 hash 进行比较。

LaunchedEffect(itemsList.toList()) {
}

You can achieve the mentioned functionality with SideEffect , remember and with some kind of identificator (listId) of the list items.您可以使用SideEffect实现上述功能, remember并使用列表项的某种identificator (listId)。 If this identificator changes, the list will scroll to the top, otherwise not.如果此标识符更改,列表将滚动到顶部,否则不会。

I have extended your code.我已经扩展了你的代码。 (You can choose any type for listId.) (您可以为 listId 选择任何类型。)

@Composable
fun <DataType : Any> GenericListView(
    itemsList: SnapshotStateList<DataType>, // this list comes from the search page viewmodel
    modifier: Modifier = Modifier.fillMaxSize(),
    spacing: Dp = 24.dp,
    padding: PaddingValues = PaddingValues(0.dp),
    listId: String? = null,
    item: @Composable (DataType) -> Unit
) {
    var lastListId: String? by remember {
        mutableStateOf(null)
    }

    val listState: LazyListState = rememberLazyListState()
    val coroutineScope = rememberCoroutineScope()

    LazyColumn(
        verticalArrangement = Arrangement.spacedBy(spacing),
        state = listState,
        modifier = modifier.padding(padding)
    ) {
        items(itemsList) {
            item(it)
        }
    }

    SideEffect {
        Log.i("->->->->->->->", "side effect launched")
        coroutineScope.launch {
            if (lastListId != listId) {
                lastListId = listId
                listState.scrollToItem(0)
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM