Java 如何在Android MVVM中建模父子关系?

Java 如何在Android MVVM中建模父子关系?,java,android,mvvm,android-viewmodel,dagger-hilt,Java,Android,Mvvm,Android Viewmodel,Dagger Hilt,我正在开发一个Android钢琴“测验”应用程序——用户点击钢琴键,然后点击黄色的“检查”按钮提交答案供评估,并查看钢琴上绘制的正确答案。主quizaActivity具有以下布局: 屏幕的上部有两个控件(文本、提交按钮等)。 屏幕的下部由一个定制的PianoView组件占据,该组件处理钢琴键盘的绘图 根据MVVM原则,PianoView应该有自己的PianoViewModel,该模型将其状态(即当前按下的键、突出显示的键等)存储在KeyStateRepository中。 随附的QuizActi

我正在开发一个Android钢琴“测验”应用程序——用户点击钢琴键,然后点击黄色的“检查”按钮提交答案供评估,并查看钢琴上绘制的正确答案。主
quizaActivity
具有以下布局:

屏幕的上部有两个控件(文本、提交按钮等)。 屏幕的下部由一个定制的
PianoView
组件占据,该组件处理钢琴键盘的绘图

根据MVVM原则,
PianoView
应该有自己的
PianoViewModel
,该模型将其状态(即当前按下的键、突出显示的键等)存储在
KeyStateRepository
中。 随附的
QuizActivity
还应具有一个
QuizActivityViewModel
,用于处理各种控件(提交答案、跳过问题…)。
QuizActivityViewModel
需要能够从
PianoView
(或者更确切地说是从其
keystateRepository
)查询所选密钥,将它们提交到域层进行评估,然后将结果发送回
PianoView
,以便可视化

换句话说,
quizaActivity
ViewModel
应该拥有/是
PianoView
ViewModel
的父级,以促进通信和数据共享

如何对这种父子关系建模,以便在ViewModels之间进行通信?

一个
ViewModel
不能依赖另一个
ViewModel
(在另一个
ViewModel
中,作为
ViewModelStoreOwner
获得
ViewModel
,我应该传递什么?)。我认为至少用匕首柄是不可能实现的

我想到了解决这个问题的三种解决方案,它们都无法使用:

1-视图之间共享数据的官方方式 Android开发人员使用共享的视图模型来促进两个片段/视图之间的数据共享。但是,这不适合我的用例。
PianoView
(或其ViewModel)应该是其状态的唯一所有者,其
存储库的作用域为其
ViewModel
。否则,
PianoView
组件将无法重用。例如,考虑另一个<代码>活动< /代码>,在这里我想有两个独立的<代码> PiaOnVIEW/COD>实例可见:

重用测验活动中的共享视图模型显然是错误的,因为它包含不相关的方法和逻辑(即提交测验答案),不适合双键盘场景

2-应用程序范围的存储库 通过使用存储库的共享实例的建议解决方案,解决了类似的问题。但是,使用
@Singleton
KeyStateRepository
将再次阻止两个独立的键盘显示不同的数据

3(编辑)-2个由事件总线复制的重复存储库 理论上,我可以创建2个独立的
ViewModel
s和2个
KeyStateRepository
实例。
ViewModels
将订阅事件总线。每次
ViewModel
调用其存储库上的可变操作时,它也会触发一个事件,该操作将通过订阅到同一事件总线的另一个
ViewModel
进行复制


然而,这感觉像一个脆弱而复杂的黑客。我想要一个简单的MVVM兼容解决方案。我不敢相信两个UI组件的简单父子关系在MVVM中是无法实现的。

如果您不想将
PianoViewModel
绑定到您的
ActivityViewModel
,我会创建一个
接口,由
ActivityViewModel
实现,而
PianoVM
可以有一个对该接口的可空引用。这样,
PianoViewModel
就不需要实现,也不需要存在组件

如何获得活动视图模型是另一个问题。查看
by activityViewModels()
实现中的片段,您可能可以通过
by viewModels()
传递活动的
viewModelStore
来执行相同的操作

在多体系结构活动中,如果您不想让
PianoViews
拥有
ViewModels
和您的
ActivityViewModel
了解它们,请不要对它们使用
Dagger
注入,而是在
ActivityViewModel
中创建
PianoViewModels
,并在调用阶段为它们分配一些回调创建-这样,您就可以从
ActivityViewModel
中访问他们,并能够倾听他们的事件、影响他们的行为以及保存他们的状态。这并非罕见,在某些情况下甚至是正确的方法<代码>匕首
-仅仅是一种工具,不打算在任何地方使用,但只有在那里才需要它。不需要创建
PianoViewModels
——您可以将所有需要的内容注入
ActivityViewModel
,并将所有需要的元素传递给
PianoViewModels
构造函数

如果不想,也不需要将视图包装成片段

编辑结束

基于有缺陷的体系结构方法,您做出了错误的假设

我很好奇为什么您需要
ActivityViewModel
。视图模型应仅存在于具有某些视图的图元中。当前的android开发表明,活动不应该有视图表示,而应该仅仅作为其他视图的容器()。根据您的架构,活动可能会处理显示l
// One QuizActivityViewModel, Multiple Fragments:

Activity -> PianoFragment (PianoView)| 
                                     | <-> PianoViewModel <-> KeyRepo
            PianoFragment (PianoView)|                       /
            -> QuizActivityViewModel <----------------------/

// 1 Act. 1 Frag. N Views.
Activity -> PianoFragment (PianoView)| 
                          (PianoView)| <-> PianoViewModel <-> KeyRepo
         -> QuizActivityViewModel  <---------------------------/