考虑以下最小可重现代码:
@Composable
fun Sandbox() {
val state = rememberLazyListState()
LazyColumn(
state = state,
modifier = Modifier.height(200.dp)
) {
// This line of code leads to sporadic recompositions.
// No recompositions once that line is commented out.
val test = DoSomething(state)
items(count = 50, key = {index -> index}) { index ->
Text(text = "Item: $index")
}
}
}
// note that this is not a composable function, that's intended from my side
// as I want to return values (not possible with composable functions)
fun DoSomething(state: LazyListState) : Int? {
val items = state.layoutInfo.visibleItemsInfo
return null
}
一旦删除对
DoSomething(state)
的呼叫,我就没有这个问题了。为什么读取状态会导致重组?
当你调用
DoSomething(state)
时,你正在访问 LazyListState
对象,它是一个状态持有者。此访问会导致 Compose 安排重组以确保状态是最新的。
出现此行为的原因是
LazyListState
是一个复杂的状态对象,取决于列表的布局和滚动状态。当您访问其属性(例如 layoutInfo
或 visibleItemsInfo
)时,Compose 需要确保状态一致且是最新的。
在您的情况下,调用
DoSomething(state)
会触发重组,因为 Compose 需要更新列表的状态。这样做是为了确保任何依赖的可组合项(例如 LazyColumn
及其项目)都能正确更新。
当您删除对
DoSomething(state)
的调用时,不再触发重组,因为不需要更新状态。
考虑实施以下选项:
LazyListState
对象,而是使用remember
或derivedStateOf
创建快照状态来存储必要的信息。这将使状态访问与重组分离。DoSomething
转换为可组合函数,这将允许您使用 Compose 的内置状态管理并避免显式状态访问。LazyListState
对象,请尝试最小化访问频率或使用更轻量级的替代方案,例如用于简单滚动状态的rememberLazyListScrollState
。通过应用这些策略,您应该可以立即解决问题!如果这不能解决问题,请告诉我。