在viewModel和片段之间进行通信的良好做法

问题描述 投票:1回答:2

我正在实现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);
    }
} 

现在我的片段中有一个可观察到的人,当到达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,而当我创建可观察对象时,我只会得到最后一个,这很有意义。

android viewmodel android-architecture-components android-viewmodel android-mvvm
2个回答
1
投票

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

如果您要在片段和viewModel之间进行数百次通信,那么您只需要推断出片段逻辑,就像您必须在某些情况下显示视图,然后观察片段中的一个不可更改的实时数据并使用两个可更改的实时数据和另一个不可更改的....使用不可更改的方法在每种类型的东西上设置该布尔值,并检查您的viewModel并在开始时将实时数据分配给您不可更改的数据。

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

这是更好的方法,我希望我能更好地理解您的问题,如果不请进一步阐述,以便我能提供帮助。


0
投票

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

我的视图模型:

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

© www.soinside.com 2019 - 2024. All rights reserved.