android - viewModel 和 Fragment 之间通信的良好实践

标签 android viewmodel android-architecture-components android-viewmodel android-mvvm

我正在实现 viewModel 并为了在 viewModel 和 fragment 之间进行通信,我正在这样做:

public class SplashViewModel extends AndroidViewModel {

private LiveData<Boolean> actions;

public SplashViewModel(@NonNull Application application) {
    super(application);
    actions= new MutableLiveData<>();
}


public void aViewModelMethod() {
    //doing some stuff
    if (stuff == X){
          //I need to hide a view for exemple, I'm doing this
          actions.postValue(true);
    }
} 

现在在我的 fragment 中,我有一个可观察的对象,当达到 actions.postValue(true) 时会触发它

viewModel.actions.observe(getViewLifecycleOwner(), new Observer<Boolean>() {
            @Override
            public void onChanged(Boolean action) {
                if (action){
                    databinding.myView.setVisibility(View.VISIBLE);
                }
            }
        });

这工作正常,但如果我有很多沟通,我需要每次实现一个新变量,并观察它? 当他们 4 或 5 岁的时候还可以,但是当他们有数百人的时候我应该做什么?

我尝试用带有开关和操作列表的整数来更改 bool 值,但是当 viewModel 初始化时,可能会触发多个 postValue,当我创建可观察值时,我只得到最后一个,这使得感觉。

最佳答案

通常,我的 View 模型中有两个可观察的实时数据。首先是代表整个屏幕的状态。其次,我用于“单次”事件,例如 toast、导航、显示对话框。

我的 View 模型:

class PinCreateViewModel(...) : ViewModel() {

    val event = MutableLiveData<BaseEvent<String>>()
    val state = MutableLiveData<PinCreateViewState>()
}

我有一个用于整个屏幕的状态对象:

sealed class PinCreateViewState {

    object FirstInput : PinCreateViewState()

    data class SecondInput(val firstEnteredPin: String) : PinCreateViewState()

    object Error : PinCreateViewState()

    object Loading : PinCreateViewState()
}

我认为通过这种方法可以很容易地考虑我的屏幕状态,很容易将我的屏幕设计为 finite state machine ,并且易于调试。特别是,我喜欢这种处理非常复杂的屏幕的方法。在这种情况下,我有一个 single source of truth对于我的整个屏幕状态。

但有时我想显示对话框、 toast 或打开新屏幕。这些东西不是我的屏幕状态的一部分。这就是为什么我想单独处理它们。在本例中,我使用 Events :

sealed class BaseEvent(private val content: String) {

    var hasBeenHandled = false
        private set

    fun getContentIfNotHandled(): String? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    fun peekContent(): String = content
}

class ErrorEvent(content: String) : BaseEvent(content)

class MessageEvent(content: String) : BaseEvent(content)

我的 Fragment 与 ViewModel 的交互如下所示:

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)    
        observe(viewModel.event, this::onEvent)
        observe(viewModel.state, this::render)
}

private fun render(state: PinCreateViewState) {
        when (state) {
            PinCreateViewState.FirstInput -> setFirstInputState()
            is PinCreateViewState.SecondInput -> setSecondInputState()
            PinCreateViewState.Error -> setErrorState()
            PinCreateViewState.Loading -> setLoadingState()
        }
}

fun onEvent(event: BaseEvent<String>) {
        event.getContentIfNotHandled()?.let { text ->
            when (event) {
                is MessageEvent -> showMessage(text)
                is ErrorEvent -> showError(text)
            }
        }
    }

我真的很喜欢Kotlin Sealed classes因为它迫使我处理所有可能的情况。我什至可以在编译之前找到未处理的状态。

关于android - viewModel 和 Fragment 之间通信的良好实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62357118/

相关文章:

android - GoogleAnalyticsTracker.getInstance() 时出现 NoClassDefFoundError

java - android sqlite没有这样的表错误(代码1)

ASP.NET MVC 2 - ViewModel 前缀

Android Architecture Components LiveData进度更新

android - map 的房间类型转换器

java - 日期解析给出错误的值

java - 出于测试目的将 JSON 对象响应传递给 TextView 时出错

c# - 从 MainViewModel 调用 ViewModel 的方法

MVVM SelectionChanged 组合框不调用 'Set'

android - Room LiveData 观察器 (onChange) 触发多次但仅针对 INSERT