复用的设计模式
Pan解耦了Activity/Fragment和ViewModel,再加上Xml与View之间本身就通过Id解耦,从而让Pan框架有了无与伦比的高复用性,可以充分利用OO带来的好处。
ViewModel继承和Xml重用
列表页和详情页是非常常见的一个场景,例如微博:
列表页中的item,在详情页中,放置在头部,两者界面相似,显示的内容基本一致,交互稍有区别。直接使用ViewModel的继承,在子类中绘制不同场景下的不同界面,可以非常轻松的重用代码,如下UML:
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:
其中,NineImageView可以专注于渲染9宫格图片,而WeiboViewModel把这部分的渲染逻辑委托给NineImageView,同时NineImageView提供控制的回调接口,让ViewModel绑定的Controller可以进行控制。
ViewModel委托
上面看到,ViewModel把绘制9宫格图片委托给了NineImageView,而控制则仍然留给了自己的Controller。如果控制部分代码更加复杂,进一步分离控制是有必要的,因此,我们可以把NineImageView升级为NineImageViewModel。如下图UML:
其中,完成委托这一步,可以在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:
继承后的Controller,子类可以增加额外的控制逻辑,也可以有选择的覆盖父类的控制逻辑。