Skip to content

复用的设计模式

Pan解耦了Activity/Fragment和ViewModel,再加上Xml与View之间本身就通过Id解耦,从而让Pan框架有了无与伦比的高复用性,可以充分利用OO带来的好处。

ViewModel继承和Xml重用

列表页和详情页是非常常见的一个场景,例如微博:

列表页和详情页 Pan

列表页中的item,在详情页中,放置在头部,两者界面相似,显示的内容基本一致,交互稍有区别。直接使用ViewModel的继承,在子类中绘制不同场景下的不同界面,可以非常轻松的重用代码,如下UML:

Pan的ViewModel继承

WeiboItemViewModel使用在列表页的ListView中,通过Adapter/ViewHolder模式与外层的WeiboListViewModel搭配。而WeiboDetailViewModel则有可能作为详情页里ListView中的headerView与其外层的ViewModel搭配。

而界面的Xml,也可以类似图中,通过include的方式进行重用。如果两边样式差距过大,也可以使用两个独立的Xml,此时只需要保证需要渲染的view的id一致即可,保证通用的渲染逻辑可以在父类重用,具体样式的渲染可以交给子类。

除了列表页/详情页的场景外,任何具有可重用的“界面部件”均可以使用继承来重用、分离、梳理代码。

ViewModel和自定义View

为什么不直接使用自定义View?

在大家意识到Activity/Fragment非常不利于界面重用的时候,大家开始使用View作为界面部件进行重用。直接订制View大大提升了界面的可重用性,但缺陷也仍然明显:渲染逻辑和控制逻辑无法分离。当然,好的开发者仍然会在CustomView中自行分离控制逻辑,而Pan的ViewModel则是强制要求了这一点,在团队合作中,强制所有开发者使用同一种分离方式无疑是最好的。

自定义CustomView与ViewModel的结合

事实上,CustomView与ViewModel甚至可以结合使用。CustomView作为一个更关注渲染逻辑的整体,可以成为ViewModel的一个field,并暴露控制接口给Controller,如下图的UML:

Pan的ViewModel和View结合

其中,NineImageView可以专注于渲染9宫格图片,而WeiboViewModel把这部分的渲染逻辑委托给NineImageView,同时NineImageView提供控制的回调接口,让ViewModel绑定的Controller可以进行控制。

ViewModel委托

上面看到,ViewModel把绘制9宫格图片委托给了NineImageView,而控制则仍然留给了自己的Controller。如果控制部分代码更加复杂,进一步分离控制是有必要的,因此,我们可以把NineImageView升级为NineImageViewModel。如下图UML:

Pan的ViewModel委托

其中,完成委托这一步,可以在ViewModel的onInit()方法中进行,ViewModel的完整初始化步骤可以参考ViewModel生命周期。示例代码如下:

@Xml(R.layout.view_weibo)
public class WeiboViewModel extends GeneralViewModel{

    protected NineImageViewModel mNineImageViewModel;

    protected Weibo mWeibo;

    @Override protected void onInit(){
        //完成委托
        mNineImageViewModel = Pan.with(getLifecylceObserved(), NineImageViewModel.class)
                    .controlledBy(NineImageController.class)
                    .setData(mWeibo != null : mWeibo.getImages(): null)
                    .getViewModel(getRootView()); //如果要自行创建View而非外层View的Xml,这里可以使用其他重载,再通过rootView加入界面。
    }

    public WeiboViewModel setData(Weibo weibo){
        mWeibo = weibo;
        if(mNineImageViewModel != null){
            mNineImageViewModel.setData(weibo.getImages());
        }
    }

    @Override public void render(){
        mNineImageViewModel.render();
        //...
    }

}

Controller的继承

随着ViewModel的继承,Controller可以进行对应的继承,以复用控制逻辑。Controller的继承时,稍稍处理一下绑定ViewModel类型的泛型参数就可以了,参考下面的UML:

Pan的Controller继承

继承后的Controller,子类可以增加额外的控制逻辑,也可以有选择的覆盖父类的控制逻辑。