因此,在我的数据集中,我存储了一个特殊的字符串.此字符串必须解析为树以供用户操作(查看、编辑、删除、合并、添加 node 等).对于 node 的添加和删除,我需要从活动文本字段中获取光标位置.在每个操作步骤中,我将树转换回其字符串表示形式,并将其存储到DataSet中,这将触发UI更新等.

然而,我现在有三个不同的真相来源:

  • ViewModel,包含未分析字符串的数据库中的Stateflow
private val _uiState = MutableStateFlow(MyUnparsedStringFromDB())
val uiState: StateFlow<MyUnparsedStringFromDB> = _uiState.asStateFlow()
  • mutableStateOf包含TreeComponent中的树对象
@Composable
fun TreeComponent(
    modifier: Modifier = Modifier,
    unparsedString: String,
    onValueChange: (String) -> Unit,
) {
    var root: MyTreeNode by remember { mutableStateOf(unparsedString.toTree()) }
    val onNodeValueChange: (MyTreeNode, String) -> Unit = { node, newValue ->
        node.text = newValue
        onValueChange(root.toString())
    }
    ...
    // recursive logic to call NodeComponent on each node
    NodeComponent(node, onNodeValueChange)
    ...
}
  • mutableStateOf,包含NodeComponent中当前 node 的文本字段
@Composable
fun NodeComponent(
    modifier: Modifier = Modifier,
    node: MyTreeNode,
    onNodeValueChange: (MyTreeNode, String) -> Unit,
) {
    var textState by remember { mutableStateOf(TextFieldValue(node.text)) }
    BasicTextField(
        value = textState,
        onValueChange = {
            onNodeValueChange(node, it.text)
            textState = it
        }
        // ...
    )
}

最初,当我的目标是渲染这棵树或编辑node.text字段时,这很有效.但是,当我try 执行更复杂的操作,如 node 拆分、添加、删除和合并时,它会变得非常令人头疼.

例如,现在,当我try 在当前光标位置将 node 一分为二时,树会按预期完全更新,并且正确的字符串表示形式会保存在数据集中.但textfield状态更新不会被触发,除非我离开用户界面,然后返回它.我试图通过覆盖MyTreeNode.equals函数以始终将FALSE输出为suggested by this answer来强制更新,但这并不起作用

那么,当所讨论的对象是复杂的时,我应该如何正确地进行状态提升?

推荐答案

NodeComponent中的textState会被记住,改变它的唯一方法是编辑相应的BasicTextField.为了在视图模型中保留真理的来源,我建议只传递NodeComponent所需的MyTreeNode部分,如我的示例中的text属性.

此外,您应该try 将树字符串作为关键字添加到根 node remember.这种方式root将在每次unparsedString改变时被更新.

@Composable
fun TreeComponent(
    modifier: Modifier = Modifier,
    unparsedString: String,
    onValueChange: (String) -> Unit,
) {
    var root: MyTreeNode by remember(unparsedString) { mutableStateOf(unparsedString.toTree()) }
    ...
    // recursive logic to call NodeComponent on each node
    NodeComponent(
        nodeText = node.text,
        onNodeValueChange = {
            node.text = it
            onValueChange(root.toString())
        }
    )
    ...
}

private fun NodeComponent(
    modifier: Modifier = Modifier,
    nodeText: String,
    onNodeValueChange: (String) -> Unit,
) {
    TextField(
        value = nodeText,
        onValueChange = onNodeValueChange
        // ...
    )
}

Android相关问答推荐

如何使用视图模型触发可变状态?

AdMob:MobileAds. initialize()—java. lang. xml对于某些设备不能强制转换为java. lang. String""

如何解决Android Studio中的in fragment问题

如何使用Jetpack Compose使水平pager 显示离屏页面?

如何检测HitTest是否命中给定的网格对象?

替换- prop -中的值(adb shell getprop)

无法在Android中创建通知频道

尽管我们不再使用GCM SDK,但应用程序已被标记为使用GCM SDK

Android手柄注射周期错误,多个模块引用一个核心模块

Android kotlin 中闪屏 API 执行完成后如何根据身份验证将用户导航到特定屏幕

为什么第二个代码可以安全地在 map 中进行网络调用,因为它已被缓存?

围绕动态大小的内容包装 Jetpack Compose Row?

安卓模拟器打不开

无法找到方法 ''java.io.File > org.jetbrains.kotlin.gradle.tasks.KotlinCompile.getDestinationDir()

在 Jetpack Compose 中重用具有重复代码的列

Jetpack Compose UI - 在 AlertDialog 中单击时按钮宽度会发生变化

java.lang.String 类型的值 Forbidden 无法转换为 JSONObject

调用时 listFiles() nullpointerexception

如何在 Android 应用中录制短视频?

Jetpack Compose:对角拆分卡片并将内容放入其中