我试图通过包含下载 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
)
...
}
查了很多文字,解决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
}
很奇怪,浪费了很多时间,希望对需要更新的人有帮助。
将 LiveData/Flow 与 Jetpack Compose 一起使用是完全没问题的。事实上,它们在文档中被明确命名为。
这些相同的文档还在下面的红色框中描述了您的错误:
警告:在 Compose 中使用可变对象(例如 ArrayList 或 mutableListOf())作为状态将导致用户在应用程序中看到不正确或陈旧的数据。
不可观察的可变对象,例如ArrayList或可变数据类,当它们发生变化时,Compose无法观察到它们以触发重组。
我们建议您使用可观察的数据持有者,例如 State所以解决办法很简单:和不可变的 listOf(),而不是使用不可观察的可变对象。
progressList
不可变
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(...)
。这样可以轻松修改项目并将其添加到列表中。这意味着您可以像常规列表一样使用它,并且它会正常工作,在修改时触发重组,供可组合项读取它。
片段:
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!!)
}
}