Android lambda 更新 mutablestatelistof 不重组

问题描述 投票:0回答:1

我有一个可变状态列表,我想通过 lambda 从单独的可组合函数进行更新。该代码使用新数据更新列表,但不会重构 UI。

这应该做什么:

单击按钮时,打开两个文本字段的叠加层以编辑可变状态列表中的信息。 完成后,关闭叠加层并根据叠加层中输入的信息更新 UI。

概念相当简单,但重组是问题所在。

    val groupComponents = remember {
        mutableStateListOf<GroupedComponents>()
    }

    val boxVisible = remember {
        mutableStateOf(false)
    }

    val boxElementIndex = remember {
        mutableIntStateOf(0)
    }

    val boxGroupIndex = remember {
        mutableIntStateOf(0)
    }

    val boxName = remember {
        mutableStateOf("")
    }

    val boxCommand = remember {
        mutableStateOf("")
    }

    EditCustomUIBoxOverlay(
        boxVisible,
        boxName,
        boxCommand,
        close = {
            boxVisible.value = !boxVisible.value
        },
        processBoxData = { name, command ->
            val list = groupComponents[boxGroupIndex.value].elements
            list[boxElementIndex.value].name = name.value
            list[boxElementIndex.value].data = command.value
            groupComponents[boxGroupIndex.value].copy(
                elements = list
            )
        })

@Composable
fun EditCustomUIBoxOverlay(
    boxVisible: MutableState<Boolean>,
    boxName: MutableState<String>,
    boxCommand: MutableState<String>,
    processBoxData: (MutableState<String>, MutableState<String>) -> Unit,
    close: () -> Unit
) {
    AnimatedVisibility(
        visible = boxVisible.value,
        enter = fadeIn(),
        exit = fadeOut()
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(color = Color(0.0f, 0f, 0f, 0.5f)),
            contentAlignment = Alignment.Center
        ) {
            ElevatedCard(
                modifier = Modifier
                    .padding(15.dp)
                    .defaultMinSize()) {
                Row(
                    modifier = Modifier.defaultMinSize(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    TextField(value = boxName.value, onValueChange = { it ->
                        boxName.value = it
                    }, label = { Text(text = "Button Name") })
                }
                Row(
                    modifier = Modifier.defaultMinSize(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    TextField(value = boxCommand.value, onValueChange = { it ->
                        boxCommand.value = it
                    }, label = { Text(text = "Enter Command") })
                }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    Button(onClick = {
                        close()
                        processBoxData(boxName, boxCommand)
                    }) {
                        Text(text = "Done")
                    }
                    Button(onClick = { close() }) {
                        Text(text = "Cancel")
                    }
                }
            }
        }
    }
}

如果我跳过单独创建新的可组合函数并直接添加代码,它就会按照我想要的方式工作。它更新可变状态列表并根据更改重新组合 UI。

以下作品

AnimatedVisibility(
        visible = boxVisible.value,
        enter = fadeIn(),
        exit = fadeOut()
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(color = Color(0.0f, 0f, 0f, 0.5f)),
            contentAlignment = Alignment.Center
        ) {
            ElevatedCard(
                modifier = Modifier
                    .padding(15.dp)
                    .defaultMinSize()) {
                Row(
                    modifier = Modifier.defaultMinSize(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    TextField(value = boxName.value, onValueChange = { it ->
                        boxName.value = it
                    }, label = { Text(text = "Button Name") })
                }
                Row(
                    modifier = Modifier.defaultMinSize(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    TextField(value = boxCommand.value, onValueChange = { it ->
                        boxCommand.value = it
                    }, label = { Text(text = "Enter Command") })
                }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    Button(onClick = {
                        boxVisible.value = !boxVisible.value
                        val list = groupComponents[boxGroupIndex.value].elements
                        list[boxElementIndex.value].name = boxName.value
                        list[boxElementIndex.value].data = boxCommand.value
                        groupComponents[boxGroupIndex.value].copy(
                            elements = list
                        )
                    }) {
                        Text(text = "Done")
                    }
                    Button(onClick = { close() }) {
                        Text(text = "Cancel")
                    }
                }
            }
        }
    }

使用 lambda 回调创建单独的函数是否会失去按预期访问可变状态列表的能力?

我尝试过在可组合函数内部和外部移动变量,移动调用函数的位置,更改可组合函数中的参数。所有这些都给出了相同的问题,数据根据需要进行更改,但 UI 不会重新组合。

android lambda android-jetpack-compose recompose mutablestateof
1个回答
0
投票

在 Jetpack Compose 中,您永远不应该将

MutableState<T>
作为参数传递给其他可组合项,因为这违反了 单向数据流 模式。在您的代码中,数据在可组合层次结构中向上流动。

相反,您应该传递实际值

T
,当子 Composable 想要更新
T
时,它会使用回调函数通知父 Composable 应设置的新值。

在你的情况下,它看起来像这样:

@Composable
fun EditCustomUIBoxOverlay(
    boxVisible: Boolean,
    boxName: String,
    boxCommand: String,
    processBoxData: (newName: String, newCommand: String) -> Unit,
    close: () -> Unit
) {

    var updatedBoxName by remember { mutableStateOf(boxName) }
    var updatedBoxCommand by remember { mutableStateOf(boxCommand) }

    AnimatedVisibility(
        visible = boxVisible,
        enter = fadeIn(),
        exit = fadeOut()
    ) {
        Box(
            modifier = Modifier
                .fillMaxSize()
                .background(color = Color(0.0f, 0f, 0f, 0.5f)),
            contentAlignment = Alignment.Center
        ) {
            ElevatedCard(
                modifier = Modifier
                    .padding(15.dp)
                    .defaultMinSize()) {
                Row(
                    modifier = Modifier.defaultMinSize(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    TextField(
                        value = updatedBoxName, 
                        onValueChange = { it ->
                            updatedBoxName = it
                        }, 
                        label = { Text(text = "Button Name") })
                }
                Row(
                    modifier = Modifier.defaultMinSize(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    TextField(
                        value = updatedBoxCommand, 
                        onValueChange = { it ->
                            updatedBoxCommand = it
                        }, 
                        label = { Text(text = "Enter Command") })
                }
                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.Center
                ) {
                    Button(
                        onClick = {
                            processBoxData(updatedBoxName, updatedBoxCommand)
                            close()
                        }
                    ) {
                        Text(text = "Done")
                    }
                    Button(onClick = { close() }) {
                        Text(text = "Cancel")
                    }
                }
            }
        }
    }
}

然后,从父 Composable 中,您可以这样调用它:

@Composable
fun MainComposable() {

    var boxVisible by remember { mutableStateOf(true) }
    var boxName by remember { mutableStateOf("") }
    var boxCommand by remember { mutableStateOf("") }

    EditCustomUIBoxOverlay(
        boxVisible = boxVisible,
        boxName = boxName,
        boxCommand = boxCommand,
        processBoxData = { newBoxName, newBoxCommand ->
            boxName = newBoxName,
            boxCommand = newBoxCommand
        },
        close = {
            boxVisible = false
        }
    )

}
© www.soinside.com 2019 - 2024. All rights reserved.