【问题标题】:LiveData - How to observe changes to List inside object?LiveData - 如何观察对象内部列表的变化?
【发布时间】:2021-08-25 15:09:50
【问题描述】:

我有一个 Composable、一个 ViewModel 和一个 User 类的对象,其中包含一个 List 变量。在 ViewModel 中,我定义了一个 LiveData 对象来保存 User 对象,在 Composable 中,我想观察 User 对象中 List 的变化,但它似乎效果不佳。

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

我的代码是这样的:

可组合

@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()
)

【问题讨论】:

    标签: android android-livedata android-jetpack-compose


    【解决方案1】:

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

    MutableLiveData 无法知道其中一个字段何时更改,何时更改为简单的基本类型/类。

    但是您可以将pets 设为可变状态,在这种情况下,实时数据将能够通知更改。将其定义为val pets = mutableStateListOf&lt;String&gt;()


    我个人不是实时数据的忠实拥护者,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)
        }
    }
    

    【讨论】:

    • 我们有一个复杂的多模块项目,模型是从网络中获取的,所以我们试图尽量减少对后端的依赖,并为前端保留 compose 库。 LiveData 在这方面也没有太大帮助,但不确定将状态一直转移到模型是否是最好的方法。但非常感谢您的洞察力!我认为我们可以解决这个问题!
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2019-09-21
      • 1970-01-01
      • 1970-01-01
      • 2018-10-05
      • 1970-01-01
      • 2019-01-30
      • 1970-01-01
      • 2018-03-01
      • 1970-01-01
      相关资源
      最近更新 更多