在Last Google IO上,Google发布了一些新的Arch组件预览,其中一个是ViewModel .
在docs中,google显示了此组件的一种可能用途:
活动中的两个或多个片段需要相互通信是很常见的 . 这绝不是微不足道的,因为两个片段都需要定义一些接口描述,并且所有者活动必须将两者绑定在一起 . 而且,两个片段必须处理尚未创建或不可见的其他片段的情况 . 可以使用ViewModel对象解决这个常见的痛点 . 想象一下master-detail片段的常见情况,其中我们有一个片段,用户从列表中选择一个项目,另一个片段显示所选项目的内容 . 这些片段可以使用其活动范围共享ViewModel来处理此通信 .
并显示了一个实现示例:
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
}
我很高兴不需要那些用于片段的接口通过活动进行通信 .
但Google的示例并没有准确显示我将如何从master调用详细信息片段 .
我仍然必须使用将由活动实现的an interface,它将调用fragmentManager.replace(...),或者使用新架构还有另一种方法吗?
6 回答
Updated on 6/12/2017,
Android Official provide a simple, precise example to example how the ViewModel works on Master-Detail template, you should take a look on it first.Share data between fragments
正如@CommonWare,@ Quang Nguyen所做的那样,Yigit的目的不是从主人到细节进行呼叫,而是更好地使用中间人模式 . 但是如果你想做一些片段事务,它应该在活动中完成 . 此时,ViewModel类应该作为Activity中的静态类,并且可能包含一些Ugly Callback来回调活动以进行片段事务 .
我试图实现这个并做一个关于这个的简单项目 . 你可以看一下 . 大多数代码都是从Google IO 2017引用的,也就是结构 . https://github.com/charlesng/SampleAppArch
我不使用Master Detail Fragment来实现组件,而是使用旧的组件(ViewPager中的片段之间的通信 . )逻辑应该是相同的 .
但我发现使用这些组件有些重要
您想在中间人发送和接收的内容,只能在“查看模型”中发送和接收
片段类中的修改似乎不太多 . 因为它只将实现从"Interface callback"更改为"Listening and responding ViewModel"
视图模型初始化似乎很重要,可能会在活动中调用 .
使用MutableLiveData使源仅在活动中同步 .
1.Pager Activity
2.PagerAgentViewModel (它应该有一个更好的名字,而不是这个)
3.BlankFragmentA
4.BlankFragmentB
我实现了类似于你想要的东西,我的viewmodel包含包含Enum状态的LiveData对象,当你想要将片段从master更改为细节(或反过来)时,你调用ViewModel函数来改变livingata值,并且活动知道更改片段,因为它正在观察livingata对象 .
TestViewModel:
枚举:
测试活动:
MasterFragment:
DetailFragment:
在使用附加到Activity的回调之前,该回调被视为容器 .
那个回调是两个碎片之间的中间人 . 以前的解决方案的坏处是:
Activity必须进行回调,这对于Activity来说意味着很多工作 .
两个片段紧密耦合,以后很难更新或更改逻辑 .
使用新的ViewModel(支持LiveData),您可以获得优雅的解决方案 . 它现在扮演中间人的角色,你可以将它的生命周期附加到Activity .
两个片段之间的逻辑和数据现在布局在ViewModel中 .
Two Fragment从ViewModel获取数据/状态,因此他们不需要彼此了解 .
此外,借助LiveData的强大功能,您可以根据主动片段在被动方式中的变化而不是之前的回调方式来更改细节片段 .
你现在完全摆脱了与Activity和相关碎片紧密耦合的回调 .
我强烈建议你通过Google's code lab . 在第5步中,您可以找到一个很好的例子 .
根据google codelabs example,我找到了与其他人类似的解决方案 . 我有两个片段他们在另一个中等待对象更改并继续使用更新的对象进行处理 .
对于这种方法,您将需要一个ViewModel类,如下所示:
并且侦听器片段应如下所示:
最后,updater片段可以是这样的:
值得一提的是updater片段可以是任何形式的片段(不仅仅是DialogFragment),对于使用这些体系结构组件,您应该在app build.gradle文件中包含这些代码行 . source
我最终使用自己的ViewModel来阻止将触发Activity方法的侦听器 . 与old way类似,但正如我所说,将侦听器传递给ViewModel而不是片段 . 所以我的ViewModel看起来像这样:
在StepMasterActivity中,我获取ViewModel并将其设置为侦听器:
StepMasterActivity.class:
...
...
在片段中,我只是检索ViewModel
并致电:
我表面上测试它,它的工作原理 . 当我开始实现与此相关的其他功能时,我会发现可能出现的任何问题 .
您可以像这样设置Detail Fragment到Master Fragment的值