[译] 使用 MODEL-VIEW-INTENT 第四部分 — 独立 UI 组件 (1)
<LinearLayout><com.hannesdorfmann.SelectedCountToolbarandroid:id="@+id/selectedCountToolbar"android:layout_width="match_parent"android:layout_height="wrap_content"/>
<com.hannesdorfmann.ShoppingBasketRecyclerViewandroid:id="@+id/shoppingBasketRecyclerView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"/></LinearLayout>
但是如何使这些组件进行相互间通信呢?显然每个组件有它自己的 Presenter:selectedCountPresenter 和 shoppingBasketPresenter。这是父子关系吗?不,两者都仅仅观察同一个 Model(从相同的业务逻辑里获
取更新):
public class SelectedCountPresenterextends MviBasePresenter<SelectedCountView, Integer> {
private ShoppingCart shoppingCart;
public SelectedCountPresenter(ShoppingCart shoppingCart) {this.shoppingCart = shoppingCart;}
@Override protected void bindIntents() {subscribeViewState(shoppingCart.getSelectedItemsObservable(), SelectedCountView::render);}}
class SelectedCountToolbar extends Toolbar implements SelectedCountView {
...
@Override public void render(int selectedCount) {if (selectedCount == 0) {setVisibility(View.VISIBLE);} else {setVisibility(View.INVISIBLE);}}}
ShoppingBasketRecyclerView 的代码看起来不错,有很多相同的地方,因此我忽略掉这些相同的地方了。然而,如果我们仔细观察 selectedCountPresenter 我们会注意到这个 Presenter 与 shoppingcart 耦合。我们想要使用这个 UI 组件可以在我们 App 的其他的页面使用,让这个组件变的可复用,我们需要移除这个依赖,这事实上是一个简单的重构:这个 Presenter 得到一个 Observable 作为 Model 的构造函数取代原来的 ShoppingCart:
public class SelectedCountPresenterextends MviBasePresenter<SelectedCountView, Integer> {
private Observable<Integer> selectedCountObservable;
public SelectedCountPresenter(Observable<Integer> selectedCountObservable) {this.selectedCountObservable = selectedCountObservable;}
@Override protected void bindIntents() {subscribeViewState(selectedCountObservable, SelectedCountToolbarView::render);}}
就是这样,任何时候,当我们想要显示当前 item 选择数量的时候,我们可以用这个 SelectedCountToolbar 组件。这个组件在购物车,可以记物品项的数量。但是,这个 UI 控件也可以用在你 App 里完全不同的情景下。此外,这个 UI 控件可以放在一个独立库中,并且在其他的 app 中使用,比如一个能显示选择多少张照片的 app。
Observable<Integer> selectedCount = photoManager.getPhotos().map(photos -> {int selected = 0;for (Photo item : photos) {if (item.isSelected()) selected++;}return selected;});
return new SelectedCountToolbarPresnter(selectedCount);
总结
这篇博客的目的是为了演示,父子关系通常来说是不需要的,并且可以避免,通过简单的观察你业务逻辑的相同部分。 不用 EventBus, 不需要从你的父 Activity/Fragment 中 findViewById(),不需要 Presenter.getParentPresenter() 或者其他需要其他的解决办法。仅仅需要观察者模式。伴有 RxJava 的帮助,RxJava 是实现观察者模式的基础,我们可以很轻松的构建这样的响应式 UI 组件。
另外的思考
通过与 MVP 或者 MVVM 的对比,在 MVI 我们强制(用一种激进的方法)让业务逻辑驱动一定的组件状态。故在使用 MVI 上有经验的开发者总结出下面结论:
如果一个 view 状态是另一个组件的 model?如果 view 的状态在一个组件中发生了变化,这个变化是另一个组件的意图,那么如何处理?
例子:
Observable<Integer> selectedItemCountObservable =shoppingBasketPresenter.getViewStateObservable().map(items -> {int selected = 0;
评论