更新 mutableStateList 中的项不会触发 UI 中的重组 (LazyColumn)

Updating item in a mutableStateList is not triggering recomposition in UI (LazyColumn)

提问人:Ayush Patel 提问时间:7/21/2023 更新时间:7/22/2023 访问量:120

问:

我一直在为这个问题挠头。 我有一个数据类,表示可以选择(或取消选择)的项目。

data class FilterItem(     
    val name: String,     
    val isSelected: Boolean 
)

我在 ViewModel 中创建了一个 mutableStateList 项,它创建了一个列表,并且可以通过可组合项访问。

val filterList = mutableStateListOf<FilterItem>()

 filterList.addAll(listOf(
            FilterItem("ABC", isSelected = false),
            FilterItem("DEF", isSelected = false)
))
       

我的可组合项访问列表 -

FilterLayout(
                filterList = viewModel.filterList
                )
@Composable
fun FilterLayout(
    filterList: SnapshotStateList<FilterItem>
){
.....
 itemsIndexed(filterList)) { index, it ->
                Row(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(top = 15.dp)
                        .clickable {
                            setSelected(it.name)
                        },
                    horizontalArrangement = Arrangement.SpaceBetween,
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Text(
                        text = it.name
                    )
                    Log.d("debug", "selected " + it.isSelected + "   " + it.name)
                    Checkbox(
                        isSelected = it.isSelected,
                        onCheckedChange = {},
                        modifier = Modifier.size(25.dp)
                    )
                }
            }
}

这里的问题是,当单击一个项目时,会调用 setSelected,这会更新列表(直到这里才好)。当我打印消息时,更改会反映在 logcat 中。这意味着列表正在更新和重组。我可以看到该项目在日志中被选中,但 Ui 没有反映这一点。有时它确实会立即反映,有时我必须单击其他行来触发重组,然后它就会反射。

下面是我的更新功能 -

fun setSelected(name: String) {
        viewModelScope.launch {
            val index = filterList.indexOfFirst { name == it.name }
            filterList[index] = filterList[index].copy(isSelected = !filterList[index].isSelected)
        }
    }

我尝试用 if-else 块替换复选框,仍然面临同样的问题 -if(it.isSelected) this icon else other icon

人造人 android-jetpack-compose lazycolumn android-jetpack-compose-lazy-列 可变状态

评论

0赞 Ahsan Ullah Rasel 7/24/2023
这是由于您的 setSelected() 函数调用的位置。您在单击“行”时调用了它。但是,当您单击复选框时,单击操作将被复选框使用,因此不会反射。尝试将 setSelected() 函数调用也放在 onCheckedChange{} lambda 中。

答:

0赞 Byte Code 7/21/2023 #1

mutableStateListOf 仅在添加、删除或替换新项时通知更改。试试这篇文章中的答案。

评论

0赞 Ayush Patel 7/21/2023
但正如您在我的 setSelected 函数中看到的那样 - 它正在替换当前项目。我尝试了日志记录,我可以看到该对象的 setSelected 之前和之后的不同哈希代码。
0赞 Byte Code 7/22/2023
fun setSelected(name: String) { filterList = filterList.map { if (it.name == name) it.copy(isSelected = !it.isSelected) else it }.toMutableStateList() }如果你的清单不是很长,试试这个。
0赞 ObscureCookie 7/22/2023 #2

单击该复选框时,不会发生任何反应,因为它的 onCheckedChange 不执行任何操作。但是当单击该行时,它会成功更新。

@Composable
fun FilterLayout(
    filterList: SnapshotStateList<FilterItem>,
    setSelected: (String) -> Unit,
) {
    LazyColumn(modifier = Modifier.fillMaxSize()) {
        itemsIndexed(filterList) { index, filterItem ->
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 15.dp)
                    .clickable {
                        setSelected(filterItem.name)
                    },
                horizontalArrangement = Arrangement.SpaceBetween,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    text = filterItem.name
                )
                Log.d("debug", "selected " + filterItem.isSelected + "   " + filterItem.name)
                Checkbox(
                    checked = filterItem.isSelected,
                    // set on checked change to update selected state
                    onCheckedChange = { setSelected(filterItem.name) },
                    modifier = Modifier.size(25.dp)
                )
            }
        }
    }
}