Jetpack Compose中LazyVerticalGrid选中项滚动后状态丢失问题
LazyVerticalGrid滚动后选中项状态丢失问题
我使用LazyVerticalGrid实现网格列表,点击卡片可切换背景色,未滚动时功能正常,但滚动后选中项状态丢失。已知Compose会在隐藏元素重新可见时重绘,但无法正确管理列表项状态,尝试过状态提升、rememberSaveable{},但UI状态仍无法在滚动后保持。
动图演示:
代码实现
数据类与列表组件
data class MonthFee(var isPaid :Boolean,var month :String,var monthIndex :Long) @OptIn(ExperimentalMaterialApi::class) @Composable fun MonthSelectList2( monthList: List<MonthFee>, color: Color, onMonthSelect: (list: List<MonthFee>) -> Unit) { val selectedItems = remember { mutableStateListOf<MonthFee>() } LazyVerticalGrid( userScrollEnabled = true, modifier = Modifier.background(color = MaterialTheme.colors.background.copy(0.6f)), columns = GridCells.Adaptive(150.dp) ) { items(items = monthList,key = {it.monthIndex}) { item -> val isSelected = selectedItems.contains(item) val cardBgColor = remember { mutableStateOf(item.isPaid.getActivatedColor(color)) } Card(backgroundColor = MaterialTheme.colors.background.copy(1f), onClick = { if (!item.isPaid) { if (isSelected) { selectedItems.remove(item) cardBgColor.value = Color.LightGray } else { selectedItems.add(item) cardBgColor.value = color.copy(0.65f) } onMonthSelect(selectedItems.toList()) } }) { Box{ Column( modifier = Modifier.background(color = cardBgColor.value) ) { Text(text = item.month) } if (item.isPaid) { Image( painter = painterResource(id = R.drawable.green_tick), modifier = Modifier .requiredSize(25.dp) .padding(start = 0.dp, end = 0.dp, top = 0.dp, bottom =0.dp) .clip(CircleShape), contentDescription = "" ) } }}}}}
扩展函数
fun Boolean.getActivatedColor(color: Color): Color { return if (this) { color } else { Color.LightGray }}
问题原因与解决方案
核心问题
cardBgColor状态存储逻辑错误:每个列表项的cardBgColor用remember存储,当列表项被滚出屏幕后重绘时,remember会重新初始化该值,导致背景色回到item.isPaid对应的初始状态,而非选中后的状态。- 选中状态与UI状态分离:选中状态存于
selectedItems,但UI背景色单独维护在cardBgColor,两者未联动,重绘时无法从选中状态反推正确的背景色。
修复方案
移除单独维护的cardBgColor状态,直接通过item.isPaid和isSelected计算背景色,确保每次重绘都能依据最新状态渲染:
@OptIn(ExperimentalMaterialApi::class) @Composable fun MonthSelectList2( monthList: List<MonthFee>, color: Color, onMonthSelect: (list: List<MonthFee>) -> Unit ) { val selectedItems = remember { mutableStateListOf<MonthFee>() } LazyVerticalGrid( userScrollEnabled = true, modifier = Modifier.background(color = MaterialTheme.colors.background.copy(0.6f)), columns = GridCells.Adaptive(150.dp) ) { items(items = monthList, key = { it.monthIndex }) { item -> val isSelected = selectedItems.contains(item) // 直接计算背景色,无需单独维护状态 val cardBgColor = when { item.isPaid -> item.isPaid.getActivatedColor(color) isSelected -> color.copy(0.65f) else -> Color.LightGray } Card( backgroundColor = MaterialTheme.colors.background.copy(1f), onClick = { if (!item.isPaid) { if (isSelected) { selectedItems.remove(item) } else { selectedItems.add(item) } onMonthSelect(selectedItems.toList()) } } ) { Box { Column(modifier = Modifier.background(color = cardBgColor)) { Text(text = item.month) } if (item.isPaid) { Image( painter = painterResource(id = R.drawable.green_tick), modifier = Modifier .requiredSize(25.dp) .padding(0.dp) .clip(CircleShape), contentDescription = "" ) } } } } } }
额外优化建议
- 建议将
MonthFee改为不可变数据类(把var isPaid改成val),避免意外的数据修改导致状态混乱。 - 若需要持久化选中状态,可将选中逻辑改为存储
monthIndex集合(而非MonthFee对象,避免序列化问题),并使用rememberSaveable保存:
// 父组件或ViewModel中维护选中的monthIndex集合 val selectedMonthIndices = rememberSaveable { mutableStateSetOf<Long>() } // 在列表中判断选中状态 val isSelected = selectedMonthIndices.contains(item.monthIndex) // 点击事件更新逻辑 onClick = { if (!item.isPaid) { if (isSelected) { selectedMonthIndices.remove(item.monthIndex) } else { selectedMonthIndices.add(item.monthIndex) } onMonthSelect(monthList.filter { selectedMonthIndices.contains(it.monthIndex) }) } }
内容的提问来源于stack exchange,提问作者Hritik Gupta
相关产品推荐
相关产品推荐

