上一节简单的使用Data Binding 库与界面绑定数据与事件处理,当数据更新后,使用mBinding.setCompany(mCompany);方法可以把界面上的数据全部更新一次。
如果只想在类的某个成员变量更新时相应也只更新相对应的界面,而不是layout里绑定的全部数据时,Data Binding 库也提供了相应的类,还提供了一个监听属性字段变化的回调。
java的基本数据类型都有一个对的包装类。如int对应Integer,long对应于Long,boolean对应Boolean。
类似的Data Binding 库对于成员变量也有相对应的包装类。如下图:
1.当类的成员变量值变化时,通知相应的回调接口
1.1新建对应的java类继承BaseObservable,并用Observablexxx包装相应的成员变量,在相应的get方法上使用@Bindable,@BindingAdapter注解,如下所示
public class BRCompany extends BRBaseObservable{
public ObservableField
**public** BRCompany(String name, String icon, String info, **long** createTime) {
**this**.**nameField**.set(name);
**this**.**iconField**.set(icon);
**this**.**infoField**.set(info);
**this**.**createTimeField**.set(createTime);
}
@BindingAdapter({**"bind:imageUrl"**, **"bind:error"**})
**public static void** setIcon(ImageView view, String url, Drawable error) {
Glide._with_(view.getContext()).load(url).error(error).into(view);
}
@Bindable
public String getBRName() { return nameField.get(); }
**public void** setName(String name) {
**this**.**nameField**.set(name);
}
...省略n行代码,完整请查看完整工程。 }
做安卓开发的都知道有一个叫EditText的输入控件,在输入内容变化时,有相应的接口回调相应的输入内容(这一个特性其实是EditText的父类TextView的方法,但经常用在EditText输入变化时使用,TextView相对较少使用这一个特性。)
public void addTextChangedListener(TextWatcher watcher) public void removeTextChangedListener(TextWatcher watcher)
public interface TextWatcher extends NoCopySpan { public void beforeTextChanged(CharSequence s, int start,int count, int after); public void onTextChanged(CharSequence s, int start, int before, int count); public void afterTextChanged(Editable s); }
类似的Data Binding 库对成员变量的包装Observablexxx类也有一个类似的方法和回调接口,
public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback)
public abstract static class OnPropertyChangedCallback { public OnPropertyChangedCallback() { }
**public abstract void** onPropertyChanged(Observable var1, **int** var2);
}
使用方法也类似,只是一个是在输入内容发生变化时回调,一个是在调用set方法时回调。
mCompany.nameField.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() { @Override public void onPropertyChanged(Observable observable, int i) { Log.d("onPropertyChanged","name:"+mCompany.nameField); } });
2.在成员变量值变化时让layout里绑定这个成员变量的控件也同时变化。
如上面所示,新建相应的java类后,在layout里做相应的引用,注意是以成员变量名称方式引用,即定方义了public ObservableField
<**LinearLayout ** **android****:layout_width=****"match_parent" ** **android****:layout_height=****"wrap_content"**>
<**TextView
** android:id=****"@+id/tv_name" ** android:layout_width="wrap_content" ** android:layout_height="wrap_content" ** android:layout_weight="1" ** android:text='@{"name:"+brCompany.nameField}' ** android:textSize=****"16sp"** />
<**TextView
** android:id=****"@+id/tv_create_time" ** android:layout_width="wrap_content" ** android:layout_height="wrap_content" ** android:layout_weight="1" ** android:text="@{DateUtil.getTime(brCompany.createTimeField)}" ** android:textSize=****"16sp"** /> </**LinearLayout**>
然后在调用mCompany.setName(s);方法时就可以同步更新界面。
注意事项
在生成的临时文件里,会有上面的一个BR文件,和R文件类似,生成的都是一个类似id的属性。
public class Company extends BaseObservable { public String name; private String icon; private String info; private long createTime; ...省略n行代码。
@Bindable public String getName() { ** return name**; } }
上面的类生成了图中对应的11~15行数据。
public class BRCompany extends BRBaseObservable{
public ObservableField
上面的类生成了图中对应的6~10行数据。
以Company类为例定义了一个public String name;属性,也注解一个方法
@Bindable public String getName()
所以在layout里可以有两种引用方法
以方法形式引用
android:text=****'@{"name:"+company.getName()}'
以BR文件里生成的name属性形式引用。
android:text=****'@{"name:"+company.name}'
对比使用了上面两个类的成员变量名称,可知其实BR文件里的属性值是根据get方法名称生成的,不是根据属性名称生成的,所有会有一个坑就是,当使用了 Observablexxx包装成员变量n,而且有一个getN的方法里,但在layout使用了BR文件里生成的对应属性形式引用时,Observablexxx包装过的成员变量值发生变化时,界面不会发生变化,所以示例中定义时
public ObservableField
但对应的get方法不是 getNameField 而是 @Bindable public String getBRName();
所以建议在使用Observablexxxx包装类时,成员变量名字叫name时,不要直接发使作@Bindable注解getName方法,否则可能会造成误解。
还有比较坑的就是,在因为在layout里使用不当造成编译失败的时候,提示不太友好,会很难找出哪里错了。
完整代码请查看