我有以下项目列表,每个项目旁边都有一个按钮形式的复选框。当未选中按钮时,应显示一个黑色空心圆圈。当选中按钮时,黑色圆圈内应有一个复选标记。
通过运行代码并查看调试日志,视图模型正在更新,但我的视图却没有更新。缺少什么?
data class ListItem(
var title: String,
var checked: Boolean
)
class ItemsViewModel: ViewModel() {
private var _reportDetails: MutableStateFlow<MutableList<String>> = MutableStateFlow(mutableListOf())
val reportDetails = _reportDetails.asStateFlow()
private var _issues: MutableStateFlow<MutableList<ListItem>> =
MutableStateFlow(mutableListOf(
ListItem(title = "Item 1", checked = false),
ListItem(title = "Item 2", checked = false),
ListItem(title = "Item 3", checked = false),
ListItem(title = "Item 4", checked = false),
ListItem(title = "Item 5", checked = false),
ListItem(title = "Item 6", checked = false),
ListItem(title = "Item 7", checked = false),
ListItem(title = "Item 8", checked = false),
ListItem(title = "Item 9", checked = false)
))
val issues = _issues.asStateFlow()
fun addToReportDetails(reportDetail: String) {
_reportDetails.update {
it.add(reportDetail)
return
}
}
fun removeFromReportDetails(reportDetail: String) {
_reportDetails.update {
it.remove(reportDetail)
return
}
}
fun updateOnlineIssuesAtIndex(index: Int, checked: Boolean) {
_issues.update {
it[index].checked = checked
return
}
}
}
@Composable
fun ReportView(
itemsViewModel: ItemsViewModel,
) {
val reportDetails by itemsViewModel.reportDetails.collectAsStateWithLifecycle()
val issues by itemsViewModel.issues.collectAsStateWithLifecycle()
LazyColumn(
modifier = Modifier
.padding(50.dp)
) {
item {
for ((index, issue) in issues.withIndex()) {
Column(
verticalArrangement = Arrangement.spacedBy(5.dp),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(text = issue.title)
Button(
onClick = {
if (!issue.checked) {
itemsViewModel.updateOnlineIssuesAtIndex(index = index, checked = true)
itemsViewModel.addToReportDetails(issue.title)
Log.d("ReportUser", "reportDetails = $reportDetails")
Log.d("ReportUser", "issue.checked = ${issue.checked}")
} else {
itemsViewModel.updateOnlineIssuesAtIndex(index = index, checked = false)
itemsViewModel.removeFromReportDetails(issue.title)
Log.d("ReportUser", "reportDetails = $reportDetails")
Log.d("ReportUser", "issue.checked = ${issue.checked}")
}
}
) {
Box(
modifier = Modifier
.size(25.dp)
.border(
width = 1.dp,
color = Color.Black,
shape = CircleShape
)
) {
if (issue.checked) {
Icon(
imageVector = Icons.Rounded.CheckCircle,
contentDescription = "null",
modifier = Modifier.size(25.dp),
tint = Color.Black,
)
}
}
}
}
}
}
}
}
}
您的 MutableLists 必须是不可变的,否则 Stateflow 无法检测到变化:
出于同样的原因
ListItem
也必须是不可变的:它不起作用的唯一原因是您需要更新值以
MutableState
触发重组。您正在更改一个项目的属性,该项目仍存在于同一个MutableList 中。仅此更改即可解决问题。这与 无关
StateFlow
。但是,每次选中复选框时都创建一个新列表有点过分,您最终可能会创建一个包含 100 个项目的列表,更糟糕的是,这样做会重新组合 LazyColumn 中的每个项目。正确的方法是使用 SnapshotStateList,它可以检测添加或删除或用新项目替换现有项目。这意味着您需要使用副本为该索引设置新项目。当发生这种情况时,如果您遵守稳定性,您可以仅通过组合该项目而不是 LazyColumn 中的每个项目来选中或取消选中任何项目。
更新当前索引处的单个项目
但在 ui 中,您还需要创建具有稳定参数的新函数,以确保单个项目被重组。此外,您还需要不可变数据类来确保其稳定。如果此数据类参数中的任何一个不稳定,则需要 @Immutable 注释。但是,如果您启用强跳过,这在将来不会成为问题。
您可以参考下面的答案进行更改,以确保只有触摸的项目在单击时重新组合。
Jetpack Compose 惰性列在单个项目更新时重新组合所有项目