如何解决一对 Fragment-ViewModel 的多个 LiveData/StateFlow
我有一个 Fragment,它代表单个 Activity 应用中的一个屏幕,以及这个 Fragment 的 viewmodel。
viewmodel 使用多个存储库从多个 API 调用加载一组数据,并通过多个 StateFlow 将此数据发送到片段。
假设片段有 2 个视图,每个视图都从与其相关的 StateFlow 收集数据。直到所有 2 个视图都没有绘制它们的数据我想显示一些进度条,然后当这些视图接收到数据时,将整个片段可见性从不可见变为可见。
我的问题是:当所有 2 个视图都收到他们的数据时,如何正确处理?
存储库:
class Repository(private val name: String) {
private val _data = MutableStateFlow<String?>(null)
val data = _data.asstateFlow()
suspend fun load() {
// load from the DB/API if no data
// load fresh from the API if have data
delay(1000)
_data.emit("$name data")
}
}
视图模型:
class Screenviewmodel : viewmodel() {
// will be injected by Dagger2/Hilt
private val repository1 = Repository("Repository1")
private val repository2 = Repository("Repository2")
private val _flow1 = MutableStateFlow<String?>(null)
private val _flow2 = MutableStateFlow<String?>(null)
val flow1 = _flow1.asstateFlow()
val flow2 = _flow2.asstateFlow()
init {
viewmodelScope.launch {
repository1.data.collect {
_flow1.emit(it)
}
}
viewmodelScope.launch {
repository2.data.collect {
_flow2.emit(it)
}
}
viewmodelScope.launch {
repository1.load()
}
viewmodelScope.launch {
repository2.load()
}
}
}
片段:
class Screen : Fragment(/*layoutd*/) {
private val viewmodel: Screenviewmodel by viewmodels()
override fun onViewCreated(view: View,savedInstanceState: Bundle?) {
super.onViewCreated(view,savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewmodel.flow1.filterNotNull().collect {
draw1(it)
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewmodel.flow2.filterNotNull().collect {
draw2(it)
}
}
}
private fun draw1(data: String) {
// some stuff
}
private fun draw2(data: String) {
// some stuff
}
}
解决方法
对于这种情况,我会选择 BindingAdapters。
首先在我的 ViewModel 文件中(在 ViewModel 类之外),我将创建一个 Enum Class
class ScreenViewModel : ViewModel() { .... }
enum class LoadingStatus {
LOADING,ERROR,FINISHED
}
然后我会在 ViewModel 中创建一个由 LiveData 支持的 MutableLiveData 来监控加载状态。
private val _status = MutableLiveData<LoadingStatus>()
val status: LiveData<LoadingStatus>
get() = _status
在 init 块中,我将更改 MutableLiveData 的值
init {
viewModelScope.launch {
try {
//before fetching data show progressbar loading
_status.value = LoadingStatus.LOADING
repository1.data.collect {
_flow1.emit(it)
}
//after fetching the data change status to FINISED
_status.value = LoadingStatus.FINISHED
}
catch (e:Exception) {
_status.value = LoadingStatus.FINISHED
}
}
在一个单独的 Kotlin 文件中,我为 Binding Adapter 编写代码这使得 ProgressBar 根据状态消失。
@BindingAdapter("apiLoadingStatus")
fun ProgressBar.setApiLoadingStatus(status:LoadingStatus?){
when(status){
LoadingStatus.LOADING -> {
visibility = View.VISIBLE
}
LoadingStatus.FINISHED -> {
this.visibility = View.GONE
}
LoadingStatus.ERROR -> {
this.visibility = View.GONE
}
}
}
然后为 ProgressBar 的 Fragmment XML 包含此代码。注意我在这里使用DataBinding
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:motion="http://schemas.android.com/tools">
<!-- TODO: Add data binding node -->
<data>
<variable
name="viewModel"
type="com.yourPackageName.ViewModel" />
</data>
<ImageView
android:id="@+id/statusImageView"
android:layout_width="192dp"
android:layout_height="192dp"
app:apiLoadingStatus="@{viewModel.status}"
基本上,要使此代码正常工作,您需要启用 Databinding 并在 gradle 文件中为 ViewModel/LiveData 添加依赖项。
您还可以编写另一个 BindingAdapter 将片段的视图可见性从不可见更改为可见。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。