本篇文章基于上篇的基础文章,现在网上充斥着大量的复制粘贴文,各种关于databinding的误解,我写这系列的目的很简单,教你们怎么正确使用databinding,不要被网上的复制粘贴文坑。
databinding可以很轻松的帮我们实现视图和模型之间的绑定,但是他们是如何绑定的呢。
先来看一下文件结构:
看一个简单的例子:
activity_main.xml
这个xml文件会由系统自动生成一个与之对应的ActivityMainBinding文件(假如不使用类名或者),它继承的是android.databinding.ViewDataBinding类,实现了android.databinding.generated.callback.OnClickListener.Listener接口,这个接口就是在xml布局文件中的onclick方法,并且实现了android.databinding.generated.callback.OnLongClickListener.Listener,这个接口是xml布局文件中的onLongClick方法。
这个地方有一点要澄清,现在网上大部分教程都是写的databinding不支持onLongClick方法,这种说法是错误的,databinding现在已经支持大部分控件的接口实现,只是有一些还不支持IDE提示,所以在写的时候要手动去填写,另外在上一篇文章已经讲过,onLongClick需要返回一个boolean值,所以你自定义的方法类中必须也要为onlongclick方法设定为一个boolean值,否则编译器会报错。
下面是我定义的一个内部方法类(懒癌)
public class Presenter{
public void addFragmentOne(){
Log.d(TAG,"Click AddOne");
ft=fm.beginTransaction();
ft.add(R.id.fragment_container,new OneFragment()).commit();
}
public void addFragmentTwo(){
Log.d(TAG,"Click AddTwo");
ft=fm.beginTransaction();
ft.replace(R.id.fragment_container,new TwoFragment()).commit();
}
public boolean onLongClick(){
Log.d(TAG,"Click Long");
return true;
}
}
02-21 01:37:02.519 3523-3523/com.saka.fragmentdemo D/MainActivity: Click Long
可以看到,正常安装并且运行实现了onLongClick方法。
BR文件概览
BR文件位于根目录下\app\build\generated\source\apt\debug\com\saka\fragmentdemo\BR.java位置。这是一个非常简单的常量类,每个ID对应于xml布局文件中的variable。在本例中的主要代码如下:
public class BR {
public static final int _all = 0;
public static final int presenter = 1;
public static final int ssd = 2;
public static final int text = 3;
}
所有的name都会自动生成一个int值(吐槽一下,为什么不是大写),有多少varibale就会对应生成多少ID。
Note:这里假如使用import导入的类或者为导入类设置别名均不会再BR文件中生成ID。
ActivityMainBinding概览
要了解Binding类,首先看它继承的父类
java.lang.Object
↳ android.databinding.BaseObservable
↳ android.databinding.ViewDataBinding
它继承自BaseObservable,而BaseObsetvable直接继承自Object。
ViewDatabing这个父类是为生成databindingclass(也就是本例中的ActivityMainBinding)而出现的类。它必须实现 bind(View) 或者 inflate(LayoutInflater, int, ViewGroup, boolean)这两个静态方法中的一个。
看一下其中的几个方法:
void addOnRebindCallback (OnRebindCallback listener)
重新绑定字段调用的监听器,它允许暂停自动更新,但不会停止对executePendingBindings()的显示调用。
void executePendingBindings ()
更新已被挂起的任何具有绑定表达式的控件视图,必须运行在主线程中,通常recyclerview或者listview经常使用。
View getRoot ()
返回与binding关联的布局文件中的最外面的view,如果次帮帝国是用于merge布局文件,将返回第一个根标签。在fragment的绑定或者自定义view的绑定中使用的较多。
boolean hasPendingBindings ()
返回一个是否在主线程中等待被刷新的数据。
void invalidateAll ()
使所有绑定表达式无效,并请求新的重新绑定以刷新UI。
void removeOnRebindCallback (OnRebindCallback listener)
移除在 addOnRebindCallback(OnRebindCallback)中所有的监听器。
boolean setVariable (int variableId, Object value)
绑定值。variableId是BR文件中的id,value是你设定的类的实例。
返回的值假如为true表示你已经成功绑定。
void unbind ()
为表达式移除绑定监听器。
void finalize ()
这是一个protected方法,很少使用,但是讲一下原理:
看过thinkinjava的同学应该知道这一章,这个方法是在系统gc的时候调用的,但是我们很少在其中写方法,因为这个方法不确定会不会被调用。
当垃圾回收器确定当前的对象没有引用并且需要回收时,在ViewDataBinding的子类中就会调用该方法执行清理操作。
上面的方法中主要看一下其中几个在ActivityMainBinding中的继承使用:
setVariable
系统生成的setVariable方法已经自动启动了责任链模式,与之对应的系统会自动生成相应的getter和setter方法。本例中使用了Presenter和Ssd两个名字(但是是同一个类),方法如下:
public boolean setVariable(int variableId, Object variable) {
switch(variableId) {
case BR.text :
setText((com.saka.fragmentdemo.models.Text) variable);
return true;
case BR.presenter :
setPresenter((com.saka.fragmentdemo.MainActivity.Presenter) variable);
return true;
case BR.ssd :
return true;
}
return false;
}
此处需要注意的是假如你在xml文件没有使用某个variable,但是你在java文件中定义了它,
binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.setPresenter(new Presenter());
binding.setSsd(new Presenter());
系统会生成ssd的getter和setter方法,但是在setVariable方法中不会调用,因为你没有在xml中是使用这个variable,自动被系统忽略。也是精简的一种办法。
当然你可以直接调用setPresenter方法来绑定数据,和setVarible同样的作用,而且比它还少了很多步骤,推荐书hi用setPresenter方法。
_internalCallback
这个方法是用来绑定监听回调的。首先在xml文件中定义的事件方法都会在binding文件中生成一个对应的调用方法(例如onclik会生成_internalCallbackOnClick方法,onLongClick会生成_internalCallbackOnLongClick方法),并且会为每个回调方法指定一个sourceId(指定方法稍后讲),通过判断sourceId,来确定调用哪个方法。
public final void _internalCallbackOnClick(int sourceId , android.view.View callbackArg_0) {
switch(sourceId) {
case 3: {
com.saka.fragmentdemo.MainActivity.Presenter presenter = mPresenter;
boolean presenterObjectnull = false;
presenterObjectnull = (presenter) != (null);
if (presenterObjectnull) {
presenter.addFragmentTwo();
}
break;
}
case 2:
break;
}
}
这个方法是onClick生成的对应binging文件中的方法。注意你自定义的方法中假如传参数的话会自动添加到你的方法中去。presenter.addFragmentTwo(10);
executeBindings
这个是用来绑定和监听数据的方法
// batch finished
if ((dirtyFlags & 0x9L) != 0) {
// api target 1
android.databinding.adapters.TextViewBindingAdapter.setText(this.addOne, btn1Text);
android.databinding.adapters.TextViewBindingAdapter.setText(this.addTwo, btn2Text);
}
这例的this.addOne就是button1,btn1Text获取的是variable中text的数据,可以看到源码中已经自动判断是否为空了,省了我们自己的步骤。
if ((dirtyFlags & 0x9L) != 0) {
if (text != null) {
// read text.btn2
btn2Text = text.getBtn2();
// read text.btn1
btn1Text = text.getBtn1();
}
}
所有的监听回到方法类
此文件位于generated.callback文件夹下,这个文件夹包含了所有的回调方法类,类似于(OnClickListener,OnLongClickListener)。
这个文件实现了view.OnClickListener接口,所以可以被bingding类实现,它的功能就是添加sourceId。在binding文件中绑定:
public OnClickListener(Listener listener, int sourceId) {
mListener = listener;
mSourceId = sourceId;
}
mCallback1 = new android.databinding.generated.callback.OnLongClickListener(this, 1);