使用 LiveData 和 ViewModel 删除项目会导致重新发射
Remove item using LiveData and ViewModel causes re-emitting
我有一个显示项目列表的片段,从视图模型中观察(来自 http 服务,它们不会保存在数据库中)。现在,我需要删除其中一项。我有一个删除结果实时数据,因此视图可以观察项目何时被删除。
片段
fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//...
viewModel.deleteItemLiveData.observe(viewLifecycleOwner) {
when (it.status) {
Result.Status.ERROR -> showDeletingError()
Result.Status.SUCCESS -> {
itemsAdapter.remove(it.value)
commentsAdapter.notifyItemRemoved(it.value)
}
}
}
}
fun deleteItem(itemId: String, itemIndex: Int) = lifecycleScope.launch {
viewModel.deleteItem(itemId, itemIndex)
}
ViewModel
val deleteItemLiveData = MutableLiveData<Result<Int>>()
suspend fun deleteItem(itemId: String, itemIndex: Int) = withContext(Dispatchers.IO) {
val result = service.deleteItem(itemId)
withContext(Dispatchers.Main) {
if (result.success) {
deleteItemLiveData.value = Result.success(itemIndex)
} else {
deleteItemLiveData.value = Result.error()
}
}
}
它工作正常,但是当我导航到另一个片段并再次返回时出现问题。 deleteItemLIveData
与最后一个 Result
一起再次发出,因此片段尝试再次从适配器中删除该项目,但它崩溃了。
我该如何解决这个问题?
我找到了解决办法。我更改了我的代码,因此片段从 onCreate
方法而不是 onViewCreated
观察。我也换了主人。 viewLifecycleOwner
现在是 this
。这样,片段恢复时的值不是 re-emitted,而是在创建或专门调用 viewModel.deleteItem
时。
现在可以正常使用了。如果有人认为这是一个糟糕的解决方案,请告诉我。
与其从适配器中删除单个项目,还不如更新 LiveData 的原始源,因为视图会观察该列表。
项存储库应处理删除,从 LiveData 中删除该项,后者将更新传播到视图,然后传播到适配器。
回购可能看起来像这样...
fun deleteItem(item: Item): Result {
val updated = items.value
updated.remove(item)
items.postValue(updated)
. . .
// propagate result of success/failure back to the view
}
fun observeItems() = items
在您的片段中,您将从单个 LiveData 源获得即时更新
fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.observeItems().observe(viewLifecycleOwner) {
itemsAdapter.update(it) //use DiffUtil to update list or notifyDataSetChanged
}
}
}
显示错误应该是上下文相关的,提示消息或一些视觉通知。
更新:
处理删除错误可能看起来像这样,超出了我的头脑...
suspend fun deleteItem(itemId: String, itemIndex: Int): Result = withContext(Dispatchers.IO) {
val result = service.deleteItem(itemId)
withContext(Dispatchers.Main) {
if (result.success) {
// push updated list to items
val updated = items.value
updated.remove(item)
items.postValue(updated)
Result.Success()
} else {
Result.error()
}
}
}
当您对应该只发生一次的事件使用 LiveData
时,这是一个常见问题。解释了几种解决方案 here and here。它们要么包装发出的数据,要么包装观察者。在这个包装器中,他们存储了一个标志,用于跟踪事件是否已经 handled/emitted。
我有一个显示项目列表的片段,从视图模型中观察(来自 http 服务,它们不会保存在数据库中)。现在,我需要删除其中一项。我有一个删除结果实时数据,因此视图可以观察项目何时被删除。
片段
fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//...
viewModel.deleteItemLiveData.observe(viewLifecycleOwner) {
when (it.status) {
Result.Status.ERROR -> showDeletingError()
Result.Status.SUCCESS -> {
itemsAdapter.remove(it.value)
commentsAdapter.notifyItemRemoved(it.value)
}
}
}
}
fun deleteItem(itemId: String, itemIndex: Int) = lifecycleScope.launch {
viewModel.deleteItem(itemId, itemIndex)
}
ViewModel
val deleteItemLiveData = MutableLiveData<Result<Int>>()
suspend fun deleteItem(itemId: String, itemIndex: Int) = withContext(Dispatchers.IO) {
val result = service.deleteItem(itemId)
withContext(Dispatchers.Main) {
if (result.success) {
deleteItemLiveData.value = Result.success(itemIndex)
} else {
deleteItemLiveData.value = Result.error()
}
}
}
它工作正常,但是当我导航到另一个片段并再次返回时出现问题。 deleteItemLIveData
与最后一个 Result
一起再次发出,因此片段尝试再次从适配器中删除该项目,但它崩溃了。
我该如何解决这个问题?
我找到了解决办法。我更改了我的代码,因此片段从 onCreate
方法而不是 onViewCreated
观察。我也换了主人。 viewLifecycleOwner
现在是 this
。这样,片段恢复时的值不是 re-emitted,而是在创建或专门调用 viewModel.deleteItem
时。
现在可以正常使用了。如果有人认为这是一个糟糕的解决方案,请告诉我。
与其从适配器中删除单个项目,还不如更新 LiveData 的原始源,因为视图会观察该列表。
项存储库应处理删除,从 LiveData 中删除该项,后者将更新传播到视图,然后传播到适配器。
回购可能看起来像这样...
fun deleteItem(item: Item): Result {
val updated = items.value
updated.remove(item)
items.postValue(updated)
. . .
// propagate result of success/failure back to the view
}
fun observeItems() = items
在您的片段中,您将从单个 LiveData 源获得即时更新
fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.observeItems().observe(viewLifecycleOwner) {
itemsAdapter.update(it) //use DiffUtil to update list or notifyDataSetChanged
}
}
}
显示错误应该是上下文相关的,提示消息或一些视觉通知。
更新: 处理删除错误可能看起来像这样,超出了我的头脑...
suspend fun deleteItem(itemId: String, itemIndex: Int): Result = withContext(Dispatchers.IO) {
val result = service.deleteItem(itemId)
withContext(Dispatchers.Main) {
if (result.success) {
// push updated list to items
val updated = items.value
updated.remove(item)
items.postValue(updated)
Result.Success()
} else {
Result.error()
}
}
}
当您对应该只发生一次的事件使用 LiveData
时,这是一个常见问题。解释了几种解决方案 here and here。它们要么包装发出的数据,要么包装观察者。在这个包装器中,他们存储了一个标志,用于跟踪事件是否已经 handled/emitted。