如何解决Jetpack Compose中LazyVerticalGrid复选框重复问题?
问题:Bug Report界面复选框重复创建
问题描述
开发Bug Report界面时,用户输入Bug描述或旋转屏幕后,复选框会被重复创建。界面核心功能为:用户输入Bug描述、选择对应复选框后,将这些信息用于生成邮件发送。
相关数据类定义:
data class BugReport( val message: String, val bugItems: List<BugItem>, ) data class BugItem( val title: String, val completed: Boolean = false, )
原界面Composable代码:
@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) ) }
问题原因
- 重复添加列表元素:
stringArrayResource(id = R.array.bug_report).forEach { bugItems += BugItem(...) }这段代码写在Composable根作用域中,每次界面重组(比如输入文字触发message状态变化、屏幕旋转触发重建)时,都会执行这段代码,往bugItems这个MutableStateList中追加新元素,导致列表长度持续增加,复选框重复显示。 - BugReport状态未自动同步:原代码中
bugReport用mutableStateOf初始化,仅在第一次创建时获取message.value和bugItems的初始值,后续message或bugItems变化时,bugReport不会自动更新。
解决方法
1. 一次性初始化bugItems
将填充bugItems的逻辑放到remember的初始化块内部,确保只在第一次创建bugItems时执行一次元素添加操作,避免每次重组重复添加:
val bugItems = remember { mutableStateListOf<BugItem>().apply { stringArrayResource(id = R.array.bug_report).forEach { add(BugItem(title = it, completed = false)) } } }
2. 用derivedStateOf自动更新BugReport
使用derivedStateOf替代mutableStateOf创建bugReport,这样当message或bugItems发生变化时,bugReport会自动计算新值:
val bugReport = remember { derivedStateOf { BugReport(message.value, bugItems) } }
修复后的完整代码
@ExperimentalFoundationApi @Composable fun BugReportScreen( modifier: Modifier = Modifier ) { val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult(), onResult = { } ) val message = remember { mutableStateOf("") } // 一次性初始化bugItems,避免重复添加 val bugItems = remember { mutableStateListOf<BugItem>().apply { stringArrayResource(id = R.array.bug_report).forEach { add(BugItem(title = it, completed = false)) } } } // 自动同步message和bugItems的变化 val bugReport = remember { derivedStateOf { 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(), ) } } } }
内容的提问来源于stack exchange,提问作者A. Cedano
相关产品推荐
相关产品推荐

