android - 滚动时 LazyColumn 不保持项目的状态
问题描述
遵循Pathway
谷歌关于 Jetpack compose 的代码实验室,我正在尝试这段代码
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Divider
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.codelab.basics.ui.BasicsCodelabTheme
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
MyScreenContent()
}
}
}
}
@Composable
fun MyApp(content: @Composable () -> Unit) {
BasicsCodelabTheme {
Surface(color = Color.Yellow) {
content()
}
}
}
@Composable
fun MyScreenContent(names: List<String> = List(1000) { "Hello Android #$it" }) {
val counterState = remember { mutableStateOf(0) }
Column(modifier = Modifier.fillMaxHeight()) {
NameList(names, Modifier.weight(1f))
Counter(
count = counterState.value,
updateCount = { newCount ->
counterState.value = newCount
}
)
}
}
@Composable
fun NameList(names: List<String>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(items = names) { name ->
Greeting(name = name)
Divider(color = Color.Black)
}
}
}
@Composable
fun Greeting(name: String) {
var isSelected by remember { mutableStateOf(false) }
val backgroundColor by animateColorAsState(if (isSelected) Color.Red else Color.Transparent)
Text(
text = "Hello $name!",
modifier = Modifier
.padding(24.dp)
.background(color = backgroundColor)
.clickable(onClick = { isSelected = !isSelected })
)
}
@Composable
fun Counter(count: Int, updateCount: (Int) -> Unit) {
Button(
onClick = { updateCount(count + 1) },
colors = ButtonDefaults.buttonColors(
backgroundColor = if (count > 5) Color.Green else Color.White
)
) {
Text("I've been clicked $count times")
}
}
@Preview("MyScreen preview")
@Composable
fun DefaultPreview() {
MyApp {
MyScreenContent()
}
}
我注意到只要项目在屏幕上可见LazyColumn
(预期行为!)就会重新组合项目,但是,小部件的本地状态完全丢失了!Greeting
我相信这是 Compose 中的一个错误,理想情况下,作曲家应该考虑remember
缓存状态。有没有一种优雅的方法来解决这个问题?
提前致谢!
解决方案
[更新]
使用rememberSaveable {...}
赋予了在 android 配置更改以及scrollability
文档
Remember the value produced by init.
It behaves similarly to remember, but the stored value will survive the activity or process recreation using the saved instance state mechanism (for example it happens when the screen is rotated in the Android application).
现在的代码更优雅更短,我们甚至不需要提升状态,它可以保持在内部。我现在唯一不太确定的事情是rememberSaveable
当列表越来越大时是否会有任何性能损失,比如1000 个项目。
@Composable
fun Greeting(name: String) {
val isSelected = rememberSaveable { mutableStateOf(false) }
val backgroundColor by animateColorAsState(if (isSelected.value) Color.Red else Color.Transparent)
Text(
text = "Hello $name! selected: ${isSelected.value}",
modifier = Modifier
.padding(24.dp)
.background(color = backgroundColor)
.clickable(onClick = {
isSelected.value = !isSelected.value
})
)
}
[原答案]
根据@CommonWare的回答,LazyColumn
当它们在屏幕外时,将处置可组合物及其状态,这意味着当LazyColumn
重新组合Compsoables
时,它将重新开始state
。在这种情况下,要解决此问题,只需将状态提升到消费者的范围LazyColumn
。
另外我们需要在内部使用lambdamutableStateMapOf()
代替,否则 Compose-core 引擎不会察觉到这种变化。MutableMapOf
remember { ... }
到目前为止,这里是代码:
@Composable
fun NameList(names: List<String>, modifier: Modifier = Modifier) {
val selectedStates = remember {
mutableStateMapOf<Int, Boolean>().apply {
names.mapIndexed { index, _ ->
index to false
}.toMap().also {
putAll(it)
}
}
}
LazyColumn(modifier = modifier) {
itemsIndexed(items = names) { index, name ->
Greeting(
name = name,
isSelected = selectedStates[index] == true,
onSelected = {
selectedStates[index] = !it
}
)
Divider(color = Color.Black)
}
}
}
快乐作曲!
推荐阅读
- php - 来自 MySQL 的 PHP 循环文件被最后一个循环文件覆盖
- c++ - 为什么我在 C++ 中使用链接程序的散列会产生意外的输出?
- r - 正则表达式字符串以匹配 R 中的列表
- reactjs - Dexie 说在初始化问题之前有一个电话
- html - `padding-right` 属性未添加填充
- azure - 天蓝色下载密钥/秘密不起作用-RequiredArgumentMissingError
- java - 伪代码到 Java
- python - 将 style.format 永久应用于 DataFrame
- java - 如何通过“:”对齐多行 JOptionPane 输出对话框消息?
- javascript - Cypress 在自定义命令中加载环境变量