一对Fragment ViewModel的多个LiveData/StateFlow



我有一个Fragment,它表示单个活动应用程序中的屏幕,还有一个用于该片段的ViewModel。

ViewModel使用多个存储库从多个API调用加载一组数据,并通过多个StateFlow将这些数据发送到片段

假设片段有两个视图,每个视图都从与其相关的StateFlow中收集数据。在这两个视图都没有绘制数据之前,我想显示一些进度条,然后当这些视图接收到数据时,将整个片段的可见性从不可见变为可见。

我的问题是:当所有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文件中,我为绑定适配器编写代码。这会使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的Fragment 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,将片段的视图可见性从不可见更改为可见。

相关内容

  • 没有找到相关文章

最新更新