重用 ViewModels - 将使用它的每个屏幕的状态
Reusing ViewModels - State for each screens that will use it
据此article:
The ViewModel should expose states for the View, rather than just
events.
我正在重用视图模型,因为我需要多个视图的相同数据,但我怀疑它们使用相同的数据但显示它以不同的方式。例如,如果我有一个用户列表,第一个视图显示他们,第二个视图使用数据排序目的,如果用户列表达到特定大小,第三个视图显示一些标签。
如果我只公开数据(用户列表)并且视图将决定对它做什么,除了我违反架构之外,它也很难测试,因为我需要模拟 android, 但我只想测试是否调用了某个方法,视图如何显示并不重要。
所以我正在考虑为每个使用视图模型的视图创建一个状态 class 这样的解决方案,这样视图模型将更新状态,我现在可以轻松地测试状态是否发生变化。
我的问题是重用视图模型的视图越多,每个视图的状态就越多,这对我来说也不对,想象一下即使只有一个视图,该方法也会更改所有状态一次可以显示。
为每个视图创建单独的视图模型对我来说就像复制代码,例如:ResetPasswordView
和 CreatePasswordView
,它们确实具有相同的过程,也几乎相同的行为,所以为什么不呢重用视图模型? ...或者我应该?我在这里错过了什么?
编辑(我目前的解决方案):
根据我所做的,我为每个 fragment/activity 创建了单独的视图模型,因为 虽然它们使用相同的数据,但它们以不同的方式表示 。通过这样做,我可以对表示逻辑进行单元测试,因为所有数据操作(特别是视图)都发生在视图模型上。
我确实共享视图模型,但出于导航的目的,示例是解释这一点的最佳方式(我在这里使用 Koin 作为我的依赖框架):
OnBoardingActivity:
class OnBoardingActivity {
var fullNameViewModel by viewModel<FullNameViewModel>()
private fun initViewModels() {
fullNameViewModel.stateShowEmail.observe(this, Observer {
// do navigate to email because that's the next screen/fragment after FullNameFragment
})
}
}
FullNameFragment:
class FullNameFragment {
var viewModel by sharedViewModel<FullNameViewModel>() // notice that I share the view model from the activity here to have only 1 instance
private fun initViews() {
RxTextView.textChanges(etFirstName)
.doOnNext { viewModel.setFirstName(it.toString()) }
.subscribe()
// ... set the other fields
}
private fun initViewModels() {
viewModel.stateValidationFirstName.observe(this, Observer {
when(it) { // validation
is RequiredValidation -> // show some error/validation message
else -> it is valid! remove any error/validation messages
}
})
}
}
FullNameViewModel:
class FullNameViewModel {
val stateValidationFirstName = MutableLiveData<Validation>() // some validation object
val stateShowEmail = SingleLiveEvent<Any>() // I'm using the hacky single live event here hehe
fun setFullName() {
// do the validations, some process and this can be easily test
// like: stateValidationFirstName = RequiredValidation()
stateShowEmail.call()
}
}
通常,当一篇文章提到视图时,它们并不是指文字 View
class。他们的意思是建筑意义上的。
采用 MVVM。 Model-View-ViewModel.
当我们谈论 Android 时,视图是 Activity
、Fragment
和 View
布局。
所以理想情况下,您应该有一个 ViewModel
用于最合乎逻辑的集合。
如果你有一个 Activity
和一个布局,但没有 Fragment
,那么有一个 ViewModel
来覆盖 Activity
状态是有意义的,其中包括它的布局。
如果您有一个具有自己布局的 Activity
和一个 Fragment
,那么您可能有两个 ViewModel
。一种用于 Activity
状态,另一种用于 Fragment
.
我写了一个可能有帮助的示例应用程序:
https://github.com/DavidEdwards/mvvm-example
以基本 Activity
为例,除了 Fragment
之外没有任何布局。您将在 Fragment
中有一个 ViewModel
(通常,这绝不是规则)。在这个 ViewModel
中,您将有 LiveData
代表您的 View
的状态。当您想要更改 View
的状态时,您更改 ViewModel
中的状态,然后 ViewModel
将更改传播到 View
。我通常会通过使用出色的 Android Databindings Library 将 ViewModel
传递到我的布局中来实现这一点。
你可以看到一个例子here, here and here。在这个例子中,我通过观察游戏中玩家数量的状态来控制 View
的可见性。
据此article:
The ViewModel should expose states for the View, rather than just events.
我正在重用视图模型,因为我需要多个视图的相同数据,但我怀疑它们使用相同的数据但显示它以不同的方式。例如,如果我有一个用户列表,第一个视图显示他们,第二个视图使用数据排序目的,如果用户列表达到特定大小,第三个视图显示一些标签。
如果我只公开数据(用户列表)并且视图将决定对它做什么,除了我违反架构之外,它也很难测试,因为我需要模拟 android, 但我只想测试是否调用了某个方法,视图如何显示并不重要。
所以我正在考虑为每个使用视图模型的视图创建一个状态 class 这样的解决方案,这样视图模型将更新状态,我现在可以轻松地测试状态是否发生变化。
我的问题是重用视图模型的视图越多,每个视图的状态就越多,这对我来说也不对,想象一下即使只有一个视图,该方法也会更改所有状态一次可以显示。
为每个视图创建单独的视图模型对我来说就像复制代码,例如:ResetPasswordView
和 CreatePasswordView
,它们确实具有相同的过程,也几乎相同的行为,所以为什么不呢重用视图模型? ...或者我应该?我在这里错过了什么?
编辑(我目前的解决方案):
根据我所做的,我为每个 fragment/activity 创建了单独的视图模型,因为 虽然它们使用相同的数据,但它们以不同的方式表示 。通过这样做,我可以对表示逻辑进行单元测试,因为所有数据操作(特别是视图)都发生在视图模型上。
我确实共享视图模型,但出于导航的目的,示例是解释这一点的最佳方式(我在这里使用 Koin 作为我的依赖框架):
OnBoardingActivity:
class OnBoardingActivity {
var fullNameViewModel by viewModel<FullNameViewModel>()
private fun initViewModels() {
fullNameViewModel.stateShowEmail.observe(this, Observer {
// do navigate to email because that's the next screen/fragment after FullNameFragment
})
}
}
FullNameFragment:
class FullNameFragment {
var viewModel by sharedViewModel<FullNameViewModel>() // notice that I share the view model from the activity here to have only 1 instance
private fun initViews() {
RxTextView.textChanges(etFirstName)
.doOnNext { viewModel.setFirstName(it.toString()) }
.subscribe()
// ... set the other fields
}
private fun initViewModels() {
viewModel.stateValidationFirstName.observe(this, Observer {
when(it) { // validation
is RequiredValidation -> // show some error/validation message
else -> it is valid! remove any error/validation messages
}
})
}
}
FullNameViewModel:
class FullNameViewModel {
val stateValidationFirstName = MutableLiveData<Validation>() // some validation object
val stateShowEmail = SingleLiveEvent<Any>() // I'm using the hacky single live event here hehe
fun setFullName() {
// do the validations, some process and this can be easily test
// like: stateValidationFirstName = RequiredValidation()
stateShowEmail.call()
}
}
通常,当一篇文章提到视图时,它们并不是指文字 View
class。他们的意思是建筑意义上的。
采用 MVVM。 Model-View-ViewModel.
当我们谈论 Android 时,视图是 Activity
、Fragment
和 View
布局。
所以理想情况下,您应该有一个 ViewModel
用于最合乎逻辑的集合。
如果你有一个 Activity
和一个布局,但没有 Fragment
,那么有一个 ViewModel
来覆盖 Activity
状态是有意义的,其中包括它的布局。
如果您有一个具有自己布局的 Activity
和一个 Fragment
,那么您可能有两个 ViewModel
。一种用于 Activity
状态,另一种用于 Fragment
.
我写了一个可能有帮助的示例应用程序: https://github.com/DavidEdwards/mvvm-example
以基本 Activity
为例,除了 Fragment
之外没有任何布局。您将在 Fragment
中有一个 ViewModel
(通常,这绝不是规则)。在这个 ViewModel
中,您将有 LiveData
代表您的 View
的状态。当您想要更改 View
的状态时,您更改 ViewModel
中的状态,然后 ViewModel
将更改传播到 View
。我通常会通过使用出色的 Android Databindings Library 将 ViewModel
传递到我的布局中来实现这一点。
你可以看到一个例子here, here and here。在这个例子中,我通过观察游戏中玩家数量的状态来控制 View
的可见性。