Jetpack Compose MutableLiveData 不更新 UI 组件

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

我试图通过包含下载 ID 和进度值的数据对象列表一次显示多个下载进度条。此对象列表的值正在正确更新(通过日志记录显示),但 UI 组件在其初始值从 null 更改为第一个进度值后将不会更新。请帮忙!

我看到有类似的问题,但他们的解决方案对我不起作用,包括附加观察员。

class DownLoadViewModel() : ViewModel() {
   ...
   private var _progressList = MutableLiveData<MutableList<DownloadObject>>()
   val progressList = _progressList // Exposed to the UI.
   ...
   
   //Update download progress values during download, this is called 
   // every time the progress updates.
   val temp = _progressList.value
   temp?.forEach { item ->
      if (item.id.equals(download.id)) item.progress = download.progress
   }
   _progressList.postValue(temp)
   ...
}

UI组件

@Composable
fun ExampleComposable(downloadViewModel: DownloadViewModel) {
    val progressList by courseViewModel.progressList.observeAsState()
    val currentProgress = progressList.find { item -> item.id == local.id }
    ...
    LinearProgressIndicator(
        progress = currentProgress.progress
    )
    ...
}
android viewmodel android-livedata android-jetpack android-jetpack-compose
4个回答
9
投票

查了很多文字,解决ViewModel中List不更新Composable的问题。我尝试了三种方法都没有效果,比如:LiveData、MutableLiveData、mutableStateListOf、MutableStateFlow

根据测试,发现值发生了变化,但是界面没有更新。文档中说,只有当State的值发生变化时,页面才会更新。根本问题是数据问题。如果没有更新,说明State没有监听到数据更新。

以上方法对于添加和删除都有效,但是单独更新不行,因为我更新了T中的元素,但是对象并没有改变。

解决方案是深复制。


    fun agreeGreet(greet: Greet) {
        val g = greet.copy(agree = true)  // This way is invalid 
        favourites[0] = g
    }


    fun agreeGreet(greet: Greet) {
        val g = greet.copy() // This way works
        g.agree = true
        favourites[0] = g
    }

很奇怪,浪费了很多时间,希望对需要更新的人有帮助。


4
投票

将 LiveData/Flow 与 Jetpack Compose 一起使用是完全没问题的。事实上,它们在文档中被明确命名为

这些相同的文档还在下面的红色框中描述了您的错误:

警告:在 Compose 中使用可变对象(例如 ArrayList 或 mutableListOf())作为状态将导致用户在应用程序中看到不正确或陈旧的数据。

不可观察的可变对象,例如ArrayList或可变数据类,当它们发生变化时,Compose无法观察到它们以触发重组。

我们建议您使用可观察的数据持有者,例如 State

和不可变的 listOf(),而不是使用不可观察的可变对象。

所以解决办法很简单:

    使你的
  • progressList
    不可变
  • 更新时创建一个新列表,它是旧列表的副本,但具有新的进度值

3
投票
尽可能考虑在 JC 中使用

mutableStateOf(...)

 代替 
LiveData
Flow
。所以,在你的
viewmodel
里面

class DownLoadViewModel() : ViewModel() { ... private var progressList by mutableStateOf(listOf<DownloadObject>()) //Using an immutable list is recommended ... //Update download progress values during download, this is called // every time the progress updates. val temp = progress.value temp?.forEach { item -> if (item.id.equals(download.id)) item.progress = download.progress } progress.postValue(temp) ... }
现在,如果您想向 

progressList

 添加元素,您可以执行以下操作:-

progressList = progressList + listOf(/*item*/)


在您的活动中,

@Composable fun ExampleComposable(downloadViewModel: DownloadViewModel) { val progressList by courseViewModel.progressList val currentProgress = progressList.find { item -> item.id == local.id } ... LinearProgressIndicator( progress = currentProgress.progress ) ... }
编辑,

对于特定用例,您还可以使用

mutableStateListOf(...)

 代替 
mutableStateOf(...)
。这样可以轻松修改项目并将其添加到列表中。这意味着您可以像常规列表一样使用它,并且它会正常工作,在修改时触发重组,供可组合项读取它。


0
投票
是的,我知道这不是最好的实现,但对我个人来说它有帮助:

片段:

class Fragment : Fragment() { private val viewModel : MyViewModel by viewModels() private val flow by lazy { callbackFlow<List<Item>> { viewModel.viewModelItmes.observe(viewLifecycleOwner) { trySend(it) } awaitClose() } } ... @Composable private fun Root() { val items = remember { mutableStateListOf<ChatListItem>() } LaunchedEffect(Unit) { flow.collect { items.clear() items.addAll(it) } } Column { items.forEach { /* ... */ } } } }
视图模型:

class MyViewModel : ViewModel() { val viewModelItmes = MutableLiveData<ArrayList<Item>>(emptyList()) fun someOneAddItem(item : Item) { viewModelItmes.value!!.add(item) viewModelItmes.postValue(viewModelItmes.value!!) } }
    
© www.soinside.com 2019 - 2024. All rights reserved.