重用 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 这样的解决方案,这样视图模型将更新状态,我现在可以轻松地测试状态是否发生变化。

我的问题是重用视图模型的视图越多,每个视图的状态就越多,这对我来说也不对,想象一下即使只有一个视图,该方法也会更改所有状态一次可以显示。

为每个视图创建单独的视图模型对我来说就像复制代码,例如:ResetPasswordViewCreatePasswordView,它们确实具有相同的过程,也几乎相同的行为,所以为什么不呢重用视图模型? ...或者我应该?我在这里错过了什么?

编辑(我目前的解决方案):

根据我所做的,我为每个 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 时,视图是 ActivityFragmentView 布局。

所以理想情况下,您应该有一个 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 LibraryViewModel 传递到我的布局中来实现这一点。

你可以看到一个例子here, here and here。在这个例子中,我通过观察游戏中玩家数量的状态来控制 View 的可见性。