将 RememberSaveable 与 mutableStateListOf 结合使用

wal*_*eko 24 android android-jetpack-compose

我正在尝试将可分割对象的可变列表添加到我的可组合项中。我还希望能够向其中添加对象和从中删除对象。

目前我正在使用这样的东西:

val names = remember { mutableStateListOf<String>() }

names.add("Bill")
names.remove("Bill")
Run Code Online (Sandbox Code Playgroud)

现在我希望这个列表在配置更改后继续存在,因此使用rememberSaveable. 也许是这样的:

val names = rememberSaveable { mutableStateListOf<String>() }

names.add("Bill")
names.remove("Bill")
Run Code Online (Sandbox Code Playgroud)

但这不起作用,它会抛出以下异常:

androidx.compose.runtime.snapshots.SnapshotStateList 无法使用当前的 SaveableStateRegistry 进行保存。默认实现仅支持可以存储在 Bundle 内的类型。请考虑为此类实现自定义 Saver 并将其传递给 RememberSaveable()。

这意味着SnapshotStateList( 的结果mutableStateListOf)不可保存。

到目前为止,我能想到几种解决这个问题的方法:

  1. 实际上实现了一个保护程序SnapshotStateList
  2. 使用类似的东西val namesState = rememberSaveable { mutableStateOf(listOf<String>()) }。这确实可以完美地工作,但是更新列表需要设置值,这既慢又不方便(例如,namesState.value = namesState.value + "Joe"仅添加单个元素)。

对于看似很小的任务来说,这两种方法似乎都太复杂了。我想知道做我想做的事的最好方法是什么。谢谢。

Phi*_*hov 32

里面不应该有任何数据remember或者rememberSaveable你害怕丢失的数据:一旦你消失Composable(准确地说,失去了组成),它就会被销毁。在这种情况下请考虑使用视图模型


如果您仍然对存储mutableStateListOf在内部感兴趣rememberSaveable,我建议您按照错误建议创建一个Saverfor SnapshotStateList

@Composable
fun <T: Any> rememberMutableStateListOf(vararg elements: T): SnapshotStateList<T> {
    return rememberSaveable(
        saver = listSaver(
            save = { stateList ->
                if (stateList.isNotEmpty()) {
                    val first = stateList.first()
                    if (!canBeSaved(first)) {
                        throw IllegalStateException("${first::class} cannot be saved. By default only types which can be stored in the Bundle class can be saved.")
                    }
                }
                stateList.toList()
            },
            restore = { it.toMutableStateList() }
        )
    ) {
        elements.toList().toMutableStateList()
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样使用它:

val names = rememberMutableStateListOf<String>()
LaunchedEffect(Unit) {
    names.add("Bill")
}
Text(names.joinToString { it })
Run Code Online (Sandbox Code Playgroud)

此示例的预期行为:每次旋转设备时,都会添加一个元素。

不要像添加和删除项目时那样在可组合项中使用任何状态修改。您应该只在side-effects内部执行此操作,就像我在这里使用LaunchedEffect或回调一样onClick

请注意,saveableMutableStateListOf仍然仅限于 Bundle-saveable 类型,例如StringInt等。如果您需要在内部存储自定义类型,您也需要修改Saver以保存/重新创建它。