【问题标题】:Good practice for communicate between viewModel and fragmentviewModel 和 Fragment 之间通信的良好实践
【发布时间】:2020-06-13 08:07:39
【问题描述】:

我正在实现 viewModel 并在 viewModel 和片段之间进行通信,我正在这样做:

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 中,我有一个 observable,当达到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 的时候没关系,但是当他们是数百人时我应该怎么做?

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

【问题讨论】:

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


    【解决方案1】:

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

    我的视图模型:

    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

    但有时我想显示对话框、吐司或打开新屏幕。这些东西不是我屏幕状态的一部分。这就是为什么我想分别处理它们。在这种情况下,我使用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)
    

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

    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,因为它迫使我处理所有可能的情况。我甚至可以在编译之前找到未处理的状态。

    【讨论】:

      【解决方案2】:

      PostValue 方法 将任务发布到主线程以设置给定值。如果您在主线程执行发布的任务之前多次调用此方法,则只会分派最后一个值。

      如果您的片段和 viewModel 之间有数百次通信,那么您只需推断片段逻辑,例如如果您必须在某些条件下显示视图,那么只需观察片段中的一个非可变实时数据并使用两个实时数据mutable 和另一个 non mutable .... 使用 non mutable 在各种东西上设置该布尔值并检查您的 viewModel 并在开始时将该实时数据分配给您的 non mutable 。

      private val _liveData = MutableLiveData<Boolean>()
      internal val liveData: LiveData<Boolean> = _liveData
      

      这是更好的方法,我希望我能更好地理解你的问题,如果不是,请详细说明,以便我提供帮助。

      【讨论】:

      • 是的,我理解逻辑,但我想知道是否可以避免有很多观察者以及是否存在其他方法
      猜你喜欢
      • 2023-03-23
      • 2017-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多