考虑到现在很多人项目中并没有大量使用这个东西,所以dataBinding功能默认是关闭的,开启dataBinding只需要在presenter或view的实现子类复写enableDataBinding方法,并返回true即可(不要忘记配置build.gradle)。
基本使用参考demo中PersonDataBindingActivity界面,首先需要自己在view的实现类中声明一个当前界面对应类型的dataBinding,然后调用getDataBinding方法为这个变量赋值,后面就可以直接使用dataBinding了。
private ActivityPersonBinding mBinding;
@Override
public void initView (IBasePresenter presenter) {
super.initView(presenter);
mBinding = getDataBinding();
mBinding.activityPersonCommitBtn.setOnClickListener(presenter);
}实现原理:
MVPH对于dataBinding集成非常简单,就是采用配置参数判断使用传统方法加载布局还是使用dataBinding进行加载
使用建议:
1.没用过dataBinding的同学,建议先去看下dataBinding的基本使用方法,然后再使用这个功能。
2.使用dataBinding时,建议仅仅使用它的数据双向绑定以及直接通过id调用控件的功能。不要在xml布局中写逻辑,虽然它支持在xml布局中设置监听器,但是建议不要这么做,因为这样可能会引起后面逻辑的混乱与维护的复杂性。简单来说就是,不要过度依赖dataBinding。
3.虽然dataBinding现在来说,相比之前已经成熟很多了,不过依然有个问题就是报错定位不准确的问题,这个问题在开发时绝对是令人抓狂的,所以,在使用dataBinding的时候,尽量一个界面一个界面的进行开发,以保证出错时方便查找。
4.布局的要求(重点),dataBinding布局除了官方的要求外,在MVPH中使用还有几点强制的要求。这里只说明布局要求,具体为什么有这些限制,请看下一章节的布局结构说明。布局示例参考demo中的activity_person.xml
- 首先,根布局必须为线性垂直布局
- 其次,根布局id必须为dataBindingRootLayout
- 最后,根布局中必须只能包含一个直接子布局。
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
>
<data>
<variable name="person" type="com.xujl.mvpllirary.json.PersonPayload"/>
</data>
<LinearLayout android:id="@+id/dataBindingRootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/layoutBgWhite"
android:orientation="vertical">
<ScrollView android:layout_width="match_parent"
android:layout_height="match_parent">
<!--省略中间布局细节 -->
</ScrollView>
</LinearLayout>
</layout> 本库的自动添加toolBar功能和全局空布局功能都是基于为布局文件动态添加一个父布局(垂直线性布局),然后动态结合toolBar和真实布局实现的。从示意图可以明显看出,contentLayout才是项目中的布局文件,rootView是通过代码生成的布局,toolBar则是用rootView去添加到自己内部第一个位置的,需要特别指出的是,布局配置类中有个属性是,是否自动为布局文件创建父布局,这个属性在使用自动创建toolBar时是无效的,因为自动创建toolBar必须依赖于自动创建的父布局,这个方法主要是用于某些界面需要沉浸式状态栏。
通过上面的说明,有的同学应该已经知道为什么在dataBinding布局中需要有那些强制要求了。这些强制要求就是为了dataBinding布局和普通布局保持结构的统一性。
Presenter中持有view和model的强引用,view和model的声明类型采用的是类泛型传递到基类的,实际类型则是通过子类传递,然后进行反射创建,目前支持两种方式进行反射创建。(具体逻辑参考Presenter类中的createView或createModel方法)
- 子类Presenter复写对应方法,直接返回对应view和model的类类型,通过类类型反射创建 demo中只有MainActivity采用了方法1进行反射创建,其他所有界面均采用方法2实现
@Override
protected Class<? extends IMainActivityView> getViewClassType () {
return MainActivity.class;
}
@Override
protected Class<? extends IMainActivityModel> getModelClassType () {
return MainActivityModel.class;
}- 由项目基类统一返回view和model的包路径,然后拼接出view和model的全限定名进行反射创建
@Override
protected String getViewClassPackageName () {
return AppApplication.getInstance().getViewPackageName();
}
@Override
protected String getModelClassPackageName () {
return AppApplication.getInstance().getModelPackageName();
}- 基类反射逻辑为先判断子类是否返回了类类型,如果没有才会去尝试创建全限定名,也就是说方法1的优先级高于方法2
- 两种方法优缺点对比:1方法优点在于非常自由,可以随意命名view和model类,然后进行传递,缺点在于比较繁琐,每个Presenter实现子类都必须要传递(mvp模式下)。2方法的优点在于非常简单,只需要自己在项目Presenter基类中复写一次对应方法,返回view和model的包路径,然后所有的子类无需再次复写,缺点在于:首先,对view和model的命名要求必须遵循一定规范(可以和demo中的命名规范不同,因为可以通过Presenter复写全限定名拼接方法来改变规则),其次,要求view和model的分包必须放在一起(至少activity对应的view和model,fragment对应的view和model,的分包必须各自在一起),因为如果包路径太多就会造成经常需要复写返回包路径的方法,这样的话,还不如直接使用方法1,最后,因为方法2采用的是拼接全限定名的方式,所以Presenter没有直接引用view和model的类名,这样有一个风险就是造成,以为某个view和model类未使用,而被误删。
总的来说,推荐使用方法2进行反射,虽然方法2缺点很多,但是只要习惯了这种创建方法,使用起来就会非常方便,需要特别指出的是使用方法2进行反射时就必须保证不混淆model和view的所有类,否则会找不到类,具体参见demo混淆文档示例。
整体加载流程需要自行阅读源码,这里只介绍主要的几点。
首先,不管是activity的onCreate方法还是fragment的onCreatView方法,目前都只加载了视图也就是mView的初始化代码,然后在界面可见后在子线程初始化Model,并调用model的初始化方法,延迟固定时间后最后才调用presenter的初始化方法,这么做主要是为了缓解界面初始化时的卡顿情况,当然,如果你在mView的初始化方法中做耗时操作依然后引起卡顿。
初始化方法说明:
| 方法名 | 所属类 | 说明 |
|---|---|---|
| initView | BaseView | view的初始化方法,会被UI线程最优先调用,此方法请勿做任何耗时操作 |
| initModel | BaseModel | model的初始化方法,0.1.5版本后,此方法中不再允许做耗时操作 |
| initPresenter | BasePresenter | presenter的初始化方法,会被UI线程最后调用,不要做耗时操作 |
