我正在尝试创建一个 Bug 报告屏幕。用户必须输入 Bug 描述并可以选择各种复选框。然后我检索该信息并使用它编写电子邮件。
我的问题是,当在 TextField 中输入文本或者旋转屏幕时,复选框会重新创建。
为什么会发生这种情况?我该如何避免?
BugItems 是我在资源数组中采用的固定元素。我创建了两个数据类来表示 Bug 报告。
data class BugReport(
val message: String,
val bugItems: List<BugItem>,
)
data class BugItem(
val title: String,
val completed: Boolean = false,
)
这是屏幕的组合:
@ExperimentalFoundationApi
@Composable
fun BugReportScreen(
modifier: Modifier = Modifier
) {
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = { }
)
val message = remember { mutableStateOf("") }
val bugItems = remember {
mutableStateListOf<BugItem>()
}
stringArrayResource(id = R.array.bug_report).forEach {
bugItems += BugItem(title = it, completed = false)
}
val bugReport = remember {
mutableStateOf(BugReport(message.value, bugItems))
}
Box(
modifier = modifier,
) {
Column(
modifier = modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
)
GenericToolbar(title = "Bug Report")
Spacer(modifier = Modifier.weight(1f))
Text(text = stringResource(id = R.string.bug_intro))
Text(text = stringResource(id = R.string.bug_description))
TextFieldBugDescription(message)
Text(text = stringResource(id = R.string.bug_check))
LazyVerticalGrid(
modifier = Modifier.heightIn(max = 1000.dp),
columns = GridCells.Fixed(2),
content = {
items(bugReport.value.bugItems.size) { index ->
Row(
modifier = Modifier.padding(1.dp),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = bugItems[index].completed,
onCheckedChange = { checked ->
bugItems[index] = bugItems[index].copy(completed = checked)
}
)
Text(
text = bugItems[index].title,
style = MaterialTheme.typography.bodyLarge,
)
}
}
}
)
Text(text = stringResource(id = R.string.bug_send))
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth(),
) {
LPlusButton(
onClick = {
sendEmail(launcher, bugReport)
},
text = { Text(text = stringResource(R.string.send_email)) },
leadingIcon = {
Icon(
imageVector = LPlusIcons.Email,
contentDescription = stringResource(R.string.send_email)
)
},
modifier = Modifier
.padding(horizontal = 24.dp)
.widthIn(364.dp)
.fillMaxWidth(),
)
}
}
}
}
@Composable
fun TextFieldBugDescription(message: MutableState<String>) {
TextField(
value = message.value,
onValueChange = { message.value = it },
label = { Text(stringResource(id = R.string.bug_label)) },
maxLines = 2,
modifier = Modifier
.padding(20.dp)
.fillMaxWidth()
.height(200.dp)
)
}
正如您在第一个屏幕截图中看到的,复选框是正确的:
但是当我激活 TextField 并输入一些文本时,元素会被重新创建。如果我旋转屏幕,也会发生同样的事情:
您的代码中存在几个问题:
stringArrayResource
每次父 Composable 重组时都会执行,这会一直向列表中添加越来越BugItem
多bugItems
的bugReport
用进行初始化message.value
,但是在发生变化时不会更新它message
。MutableState<String>
给您的TextFieldBugDescription
Composable,这是不推荐的。旋转设备时,所有状态都可能会丢失。为了防止这种情况发生,请使用或
rememberSaveable
等类型,或者将所有应在配置更改后继续存在的变量委托给 ,并使用 公开它们,并使用 在 Composable 中观察它们。String
Int
ViewModel
Flow
collectAsStateWithLifecycle
就你的情况而言,由于你想存储和修改 本身不支持的 bugItems 列表
rememberSaveable
,我建议引入一个ViewModel
类。就你的情况而言,你甚至可以使用AndroidViewModel
直接BugItem
用String
数组初始化列表:然后,获取
ViewModel
使用该viewModel()
函数并在 Composable 中观察它,如下所示:bugReport
然后,只需在提交时组装:最后,将您
TextFieldBugDescription
的并将其称为
在 LazyVerticalGrid 内容项()中使用键参数并分配键 id,以便组合将显示唯一键项。