android - viewModel 和 Fragment 之间通信的良好实践
问题描述
我正在实现 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 中,我有一个可观察的对象,当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 时,我只得到最后一个,这是有道理的。
解决方案
通常,我的视图模型中有两个可观察的实时数据。首先是代表整个屏幕的状态。其次,我用于“单次”事件,如祝酒词、导航、显示对话框。
我的视图模型:
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()
}
我认为使用这种方法很容易考虑我的屏幕状态,很容易将我的屏幕设计为有限状态机,并且易于调试。特别是,我喜欢这种处理非常复杂的屏幕的方法。在这种情况下,我的整个屏幕状态只有一个事实来源。
但有时我想显示对话框、吐司或打开新屏幕。这些东西不是我屏幕状态的一部分。这就是为什么我想分别处理它们。在这种情况下,我使用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 类,因为它迫使我处理所有可能的情况。我甚至可以在编译之前找到未处理的状态。
推荐阅读
- python - OperationalError 无法连接到服务器——Django/Heroku/AWS/Postgres
- c# - Visual Studio 2013:DataGridView 查询以开头的单词
- html - 在表格单元格的角落添加球体形状
- java - AES_DECRYPT for login JFrame code not working
- google-play - 上传图片时 Playstore 应用程序升级问题
- python - 如何访问元组中的列表?
- reactjs - 奇怪的“TypeError:对象不是函数”错误
- reactjs - ReactJS课程文件中的额外结束脚本标记?
- scala - 如何摆脱 Akka Stream Flow 阶段中的“额外”未来?
- openssh - 如何将 ssh-agent 套接字文件放在 /tmp 以外的目录中