Android Jetpack Compose mutableStateListOf 不做重组

问题描述 投票:0回答:5

所以,我有一个

mutableStateListOf
viewModel

var childTravellersList = mutableStateListOf<TravellersDetails>()

TravellersDetails
是一个数据类,它有一个名为
error
的字段。

这个

childTravellersList
在用户界面中用作:

val list = remember{viewModel.childTravellersList}

LazyColumn(state = lazyColumnState) {
    itemsIndexed(list) { index, item ->
        SomeBox(show = if(item.error) true else false)
    }
  }

我在

viewModel
中写了一个函数,它更新 TravellersDetails 的
error
index
的给定
childTravellersList
作为:

fun update(index){
    childTravellersList[index].error = true
}

所以,每当我调用这个函数时,列表应该得到更新。

这会更新列表,但不会触发 UI 重组😕。 我哪里做错了?

android kotlin android-jetpack-compose android-jetpack
5个回答
35
投票

mutableStateListOf
只能通知添加/删除/替换列表中的某些元素。当您更改列表中的任何类时,可变状态无法知道它。

数据类非常适合在单向数据流中存储不可变状态,因为您始终可以使用

copy
“更改”它,同时您看到需要将新数据传递给视图或可变状态。因此,避免将
var
变量与数据类一起使用,始终将它们声明为
val
以防止此类错误。

var childTravellersList = mutableStateListOf<TravellersDetails>()

fun update(index){
    childTravellersList[index] = childTravellersList[index].copy(error = true)
}

另一个问题是您正在使用

val list = remember{viewModel.childTravellersList}
:它保存第一个列表值并防止将来更新。有了ViewModel就可以直接使用了
itemsIndexed(viewModel.childTravellersList)


5
投票

仅当您更改列表本身时才会发生重组。你可以这样做。

var childTravellersList by mutableStateOf(emptyList<TravellersDetails>())

fun update(indexToUpdate: Int) {
    childTravellersList = childTravellersList.mapIndexed { index, details ->
        if(indexToUpdate == index) details.copy(error = true)
        else details
    }
}

此外,您无需像在此处所做的那样在可组合项中记住此列表

val list = remember{viewModel.childTravellersList}
。由于 MutableState 位于视图模型内部,因此它将始终在所有重组中幸存下来。只需在 LazyColumn 中使用
viewModel.childTravellersList


2
投票

使用单一触发机制来更新您的可组合项。您的列表应该仅作为普通变量提供。您可以添加/删除/删除列表中的项目,甚至可以更新每个项目的属性。对列表进行这些更新后,您可以通过生成一个随机数来重构您的可组合项,该随机数绑定到在您的可组合项中观察到的可变状态可观察项:

科特林:

class MyViewModel: ViewModel() {
   var childTravellersList = mutableListOf<TravellersDetails>()
   var onUpdate = mutableStateOf(0)

   private fun updateUI() {
      onUpdate.value = (0..1_000_000).random()
   }

   fun update(index){
       childTravellersList[index].error = true
       updateUI()
   }
}

@Composable
fun MyComposableHandler() {

   // This will detect any changes to data and recompose your composable.
   viewmodel.onUpdate.value
  
   MyComposable(
      travelersList = viewmodel.childTravellersList
   )
}

@Composable
fun MyComposable(
    travelersList: List<TravellersDetails>
) {
   
}

您应该避免在视图模型中为需要更新的不同变量创建多个可变状态变量。完全没有必要这样做。只需创建一个可变状态变量,如上所示。每当您需要重构可组合项时,只需更新 UI 函数即可。视图模型中的逻辑应该决定需要更新什么,但将实际的更新机制留给可观察到的单个可变状态。这是一个干净的模式,将使更新您的可组合项变得更加容易。

但是,请记住,您的 UI 通常由层次结构中的许多可组合项组成。当只有一个元素发生变化时,您不希望重构整个层次结构。出于这个原因,可变状态可观察对象应该用于每个需要独立于层次结构中的其他组合的可组合项。

使用上面显示的解决方案的另一个好处是您可以轻松更新对象而无需创建新对象。如果您希望您的可组合项仅在对象的某个属性发生变化时进行重组,则不能使用可变状态可观察对象,因为它们仅检测对象本身的变化,而不检测对象属性的变化。这就是为什么您最好使用上面显示的触发方法,并在可组合项重构时简单地检索更新的对象。


0
投票

如果没有

data class
也行,可以看看这个


0
投票

ViewModel 中的状态 是一个非常好的教程,解释了如何管理列表。以下片段来自教程:

有两种方法可以解决这个问题:

  1. 更改我们的数据类 WellnessTask,使 checkedState 变为 MutableState 而不是 Boolean,这会导致 Compose 跟踪项目变化。
  1. 复制你要变异的项目,删除 项目并将突变的项目重新添加到列表中,这 使 Compose 跟踪该列表更改。
© www.soinside.com 2019 - 2024. All rights reserved.