Вопрос или проблема
Я пытаюсь создать экран отчета о неисправностях. Пользователь должен ввести описание неисправности и может выбрать различные чекбоксы. Затем я извлекаю эту информацию, чтобы написать с ней электронное письмо.
Моя проблема в том, что чекбоксы восстанавливаются, когда текст вводится в поле ввода или когда экран поворачивается.
Почему это происходит и как я могу этого избежать?
Элементы BugItems являются фиксированными элементами, которые я беру из массива ресурсов. Я создал два класса данных, чтобы представить отчет о неисправностях.
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 = "Отчет о неисправностях")
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)
)
}
Как вы видите на этом первом снимке экрана, чекбоксы правильные:
Но когда я активирую поле ввода и вводжу текст, элементы восстанавливаются. И то же самое происходит, если я поворачиваю экран:
Ответ или решение
Ваша проблема с повторной инициализацией чекбоксов в вашем LazyVerticalGrid
в Jetpack Compose, вероятнее всего, связана с тем, как вы сохраняете состояние ваших объектов BugItem
. При изменении состояния текста в TextField
или при повороте экрана, ваша композиция пересоздается, и вы теряете информацию о выборе чекбоксов.
Чтобы избежать повторных инициализаций и сохранить состояние чекбоксов, вам следует использовать более устойчивые структуры данных и обеспечить, чтобы состояние ваших чекбоксов сохранялось даже при переработке композиции. Вот несколько шагов, которые помогут вам с этим.
Шаги для решения проблемы
-
Используйте
remember
дляbugItems
:Вместо того чтобы использовать
mutableStateListOf
для храненияbugItems
, можно сохранить их вremember
, чтобы данные не терялись, когда пользователь взаимодействует сTextField
или поворачивает экран.val bugItems = remember { mutableStateListOf<BugItem>().apply { stringArrayResource(id = R.array.bug_report).forEach { add(BugItem(title = it, completed = false)) } } }
-
Используйте
LazyColumn
илиLazyVerticalGrid
сitems
и ключами:Когда вы создаете элементы в
LazyVerticalGrid
илиLazyColumn
, вы можете использоватьitems
с указанием ключа, чтобы Compose мог отслеживать элементы, даже когда происходит переработка композиции.LazyVerticalGrid( modifier = Modifier.heightIn(max = 1000.dp), columns = GridCells.Fixed(2), content = { items(bugItems, key = { it.title }) { bugItem -> Row( modifier = Modifier.padding(1.dp), verticalAlignment = Alignment.CenterVertically ) { Checkbox( checked = bugItem.completed, onCheckedChange = { checked -> bugItems[bugItems.indexOf(bugItem)] = bugItem.copy(completed = checked) } ) Text( text = bugItem.title, style = MaterialTheme.typography.bodyLarge, ) } } } )
-
Структура состояния:
Если у вас есть возможность, подумайте о том, чтобы использовать более сложные структуры данных или дополнительные библиотеки для управления состоянием, такие как
ViewModel
, и хранить данные о состоянииBugReport
в нем. Это обеспечит, что данные остаются в общественном состоянии даже при изменениях конфигурации.Пример использования
ViewModel
:class BugReportViewModel : ViewModel() { var message by mutableStateOf("") var bugItems = mutableStateListOf<BugItem>().apply { stringArrayResource(id = R.array.bug_report).forEach { add(BugItem(title = it, completed = false)) } } } @Composable fun BugReportScreen(viewModel: BugReportViewModel = viewModel()) { // Используйте viewModel.message и viewModel.bugItems вместо локальных переменных // Ваш код здесь... }
Заключение
Следуя этим шагам, вы сможете избежать проблемы с повторной инициализацией чекбоксов и сохранить их состояние во время взаимодействия пользователя с интерфейсом. Использование remember
и устойчивых структур данных, таких как ViewModel
, поможет вам создать более надежный и предсказуемый интерфейс для вашего экрана отчета о ошибках.