LiveData - 如何观察对象内部列表的变化?

LiveData - How to observe changes to List inside object?

我有一个 Composable、一个 ViewModel 和一个 User class 的对象,其中有一个 List 变量。在 ViewModel 中,我定义了一个 LiveData 对象来保存 User 对象,在 Composable 中,我想观察 User 对象中 List 的变化,但它似乎工作得不是很好。

我知道当您更改 List 的内容时,它的引用是相同的,因此 List 对象本身不会更改,但我已经尝试复制列表,但它不起作用;复制整个 User 对象也不起作用;它似乎唯一有效的方法是创建两者的副本。对于更大的列表和对象来说,这似乎太牵强而且成本太高。有没有更简单的方法来做到这一点?

我的代码是这样的:

可组合

@Composable
fun Greeting(viewModel: ViewModel) {
    val user = viewModel.user.observeAsState()

    Column {
        // TextField and Button that calls viewModel.addPet(petName)

        LazyColumn {
            items(user.value!!.pets) { pet ->
                Text(text = pet)
            }
        }
    }
}

视图模型

class ViewModel {
    val user: MutableLiveData<User> = MutableLiveData(User())

    fun addPet(petName: String){
        val sameList = user.value!!.pets
        val newList = user.value!!.pets.toMutableList()
        newList.add(petName)
        sameList.add(petName)                            // This doesn't work
        user.value = user.value!!.copy()                 // This doesn't work
        user.value!!.pets = newList                      // This doesn't work
        user.value = user.value!!.copy(pets = newList)   // This works BUT...
    }
}

用户

data class User(
    // Other variables
    val pets: MutableList<String> = mutableListOf()
)

Jetpack Compose 最适合不可变对象,使用现代 Android 制作副本,而 ART 不再是过去的问题。

但是,如果您不想制作对象的完整副本,您可以向其添加一个虚拟 int,然后在您还改变列表时改变 int,但是我强烈建议您考虑不变性并实例化一个新的用户对象。

MutableLiveData 只会在值发生变化时通知视图,例如当您放置与旧值不同的其他值时。这就是 user.value = user.value!!.copy(pets = newList) 起作用的原因。

MutableLiveData 无法知道其中一个字段何时更改,当它们很简单时 types/classes。

但您可以使 pets 成为可变状态,在这种情况下,实时数据将能够通知更改。定义为 val pets = mutableStateListOf<String>().


我个人不太喜欢实时数据,使用 value!! 编写的代码看起来也不是我希望在我的项目中看到的。所以我会告诉你有关 compose 的方法,以防你的项目允许你使用它。您需要将 pets 定义为字符串的可变状态列表,并将 user 定义为用户的可变状态。

我建议您仔细阅读 documentation 中有关撰写状态的内容。

另请注意,在我的代码中,我定义了具有委托的用户和没有委托的宠物。您只能在视图模型中使用委托,而不能在状态持有者内部使用委托,否则它最终会变成普通对象。

@Composable
fun TestView() {
    val viewModel = viewModel<TestViewModel>()
    Column {
        // TextField and Button that calls viewModel.addPet(petName)
        var i by remember { mutableStateOf(0) }

        Button(onClick = { viewModel.addPet("pet ${i++}") }) {
            Text("add new pet")
        }

        LazyColumn {
            items(viewModel.user.pets) { pet ->
                Text(text = pet)
            }
        }
    }
}

class User {
    val pets = mutableStateListOf<String>()
}

class TestViewModel: ViewModel() {
    val user by mutableStateOf(User())

    fun addPet(petName: String) {
        user.pets.add(petName)
    }
}