通过本文可以了解到的内容:
1.如何使用dataBinding;
2.设置点击事件(含参数的);
3.数据与UI同步更新;
4.基本的dataBinding语法;
5.自定义注解。
按照官网的文档一步一步来学习
官方文档传送门
简单开始
第一步,在module的build.gradle文件中添加如下代码,注意是android节点下添加:
android {
...
dataBinding {
enabled = true
}
}
第二步,增加一个数据实体类。
public class UserBean{
private String firstName;
public UserBean(String firstName) {
this.firstName = firstName;
}
}
第三步,修改layout文件,以layout节点开始,data节点包含数据实体类,其他就和以前布局文件没有啥区别。
给某个textview绑定数据:android:text="@{user.firstName}",user就是某个实体类在这个文件中的引用名,firstName为这个实体类的某个属性。
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
// 以下类在本xml文件中的引用名称
name="user"
// 数据实体类全名
type="com.hotelgg.android.hotelggreview.data.UserBean" />
<variable
name="person"
type="com.hotelgg.android.hotelggreview.data.PersonBean"/>
<variable
name="onclick"
type="com.hotelgg.android.hotelggreview.handler.ClickHandler"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/main_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/red_2"
android:padding="10dp"
android:text="@{user.firstName}"
android:onClick="@{onclick.onClick}"
android:textColor="@color/white_1" />
<TextView
android:id="@+id/person_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/red_2"
android:layout_marginTop="20dp"
android:padding="10dp"
android:text="@{person.name}"
android:onClick="@{onclick.onClick}"
android:textColor="@color/white_1" />
</LinearLayout>
</layout>
第四步,在activity中的使用,ActivityMainBinding的命名规则是和其绑定的xml文件的命名规则相关的,例如,activity_main为xml文件的名称,那么就在其后面增加一个binding,首字母大写即可。
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
UserBean userBean = new UserBean("shuai bu shuai");
binding.setUser(userBean);
坑Num.1
注意ActivityMainBinding这个类,编译器不会自动编译出来,需要build一下项目这个类才能够被引用到。
坑Num.2
From the perspective of data binding, these two classes are equivalent.
这是官网的一段话,我来断章取义一下,第一句话不知道怎么翻译,什么鬼从data binding的透明(还能穿透明装呢)?申明来看?(知道的朋友指点一下)第二句,这两个类是等同的。然后我就跟着官方文档一样就写了一个数据model的构造器。
public class UserBean{
private String firstName;
public UserBean(String firstName) {
this.firstName = firstName;
}
}
编译,啪!不出意外,你已经得到了ide的一个异常
Error:(31, 29) Could not find accessor com.hotelgg.android.hotelggreview
.data.UserBean.firstName
应该添加一个get方法的入口,所以不知道官方文档说的这两者是等同的是嘛意思!
public class UserBean{
private String firstName;
public UserBean(String firstName) {
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
}
添加点击事件
第一步,添加一个ClickHandler类,名字随便取。
public class ClickHandler {
private BaseActivity context;
private ActivityMainBinding mDataBinding;
public ClickHandler(BaseActivity context, ActivityMainBinding mDataBinding) {
this.context = context;
this.mDataBinding = mDataBinding;
}
public void onClick(View view) {
if (R.id.main_tv == view.getId()) {
UserBean bean = mDataBinding.getUser();
bean.setFirstName("perry");
} else if (R.id.person_name == view.getId()) {
// 这里先不用理会
PersonBean bean = mDataBinding.getPerson();
bean.name.set("mary");
}
}
}
第二步,在activity中添加绑定
binding.setOnclick(new ClickHandler(this, binding));
第三步,对应的xml中添加引用,上面已经给出完整代码。
<data>
<variable
name="onclick"
type="com.hotelgg.android.hotelggreview.handler.ClickHandler"/>
</data>
// textview添加点击事件
android:onClick="@{onclick.onClick}"
然后,就没有然后了,可以运行开干了。
点击事件中传递参数
这种场景主要应用于:一个recyclerview的item在点击时需要取出当前item的数据,比如一个当前item的id或者url要传递到下一个页面中去。这个时候从绑定的数据中取出这个参数也有相应的办法。
<data>
<import type="com.hotelgg.android.hotelggreview.adapter.UserAdapter.UserAdapterItemClick" />
<variable
name="hotelInfo"
type="com.hotelgg.android.hotelggreview.data.HotelInfoBean" />
<variable
name="itemClick"
type="UserAdapterItemClick" />
</data>
// 某个控件的点击方法,其实就是lambda表达式的形式
android:onClick="@{()->itemClick.onClick(hotelInfo)}"
然后在adapter中进行相应的注册,这里涉及到在recyclerview当中使用,具体的代码会在后头贴出。这里只是简单的演示。
((ItemSingleTextBinding) binding).setItemClick(new UserAdapterItemClick());
public static class UserAdapterItemClick {
public void onClick(HotelInfoBean bean) {
LogUtil.e(bean.title.get());
bean.title.set("change big hotel");
}
}
这里有一点小小的观点需要表达一下:我们可以把这里的UI这样理解,它有自身的数据源model,有自身的操作行为handler,而同一个页面,我更加倾向于让其使用一个handler,这样复用性高一些,至于每个控件的数据源,我们可以通过传递进来的ActivityMainBinding对象进行获取和改变,UI层面就不需要做除了初始化之外的其他任何工作。所有的逻辑操作全由handler进行。
虽然点击事件和数据绑定都已经完成,但是数据绑定后,改变数据源是不是就能够直接让UI发生相应的更新变化呢?答案是否定的。
数据源与UI同步更新
如果想要改变数据源后同步更新UI界面,就需要对数据源进行一定的修改,我们还是以UserBean为例。
第一种方法
继承BaseObservable,然后在get方法上加上@Bindable注解,在set方法中加入notifyPropertyChanged(BR.firstName);进行数据更新。这个BR有点类似于资源文件引用中的R,也是包名.BR。
public class UserBean extends BaseObservable{
private String firstName;
private int num;
public UserBean(String firstName, int num) {
this.firstName = firstName;
this.num = num;
}
@Bindable
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
notifyPropertyChanged(BR.num);
}
@Bindable
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
}
第二种方法
用Google给我们提供的Observable...类,支持所有数据类型。
public class PersonBean {
public ObservableField<String> name = new ObservableField<>();
public ObservableField<String> lastName = new ObservableField<>();
}
// 更新方式
personBean.lastName.set("White");
一些基本的语法
增加默认值
除了指定相应的绑定数据对象外,还可以增加默认值,如下:
android:text="@{user.firstName,default=test}"
数据类型转换
类型的转换,把int型转化成string类型并进行存储,其中int值还可以进行相应的计算。
android:text="@{String.valueOf(user.num + 1)}"
使用资源文件属性
还可以指定资源文件中的属性,并且直接使用占位符的形式来填充数据(这个功能强大)。
android:text="@{@string/name_format(person.name,person.lastName)}"
<resources>
<string name="name_format">第一个:%s,第二个:%s</string>
</resources>
使用三目运算符
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
不过注意,在导入集合类型时,咱们一般都会使用泛型类,以下泛型类为string类型,因为是基本数据类型,所以不用导入。使用泛型类的格式如下:
<data>
<import type="java.util.List"/>
<variable name="list" type="List<String>"/>
</data>
自定义注解
这个功能相当之强大,废话不多说,直接上代码。先做一个工具类,名字随便取,如我的AppBindingUtils,具体啥意思,注释当中已经说的很清楚。
public class AppBindingUtils {
// BindingAdapter注解,自定义注解,可以让某些控件增加自定义属性,并且,只要在xml中出现该属性,
// 就会调用该注解注释的方法。
// 其中 bind_data 为xml中自定义属性的属性名,setDataTest这个名字随便取。
// 不让recyclerView每次都设置adapter,这样会刷新多余数据
@BindingAdapter("bind_data")
public static void setDataTest(RecyclerView recyclerView, ObservableArrayList<MultipleListBean> data) {
if (recyclerView != null && recyclerView.getAdapter() == null) {
recyclerView.setLayoutManager(new LinearLayoutManager(recyclerView.getContext(), LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new UserAdapter(data));
}
}
}
xml布局文件中如下,这样,当编译器发现了xml中有一项自定义注解编注的属性时,就会带着这个属性去找有@BindingAdapter注解编注过并且属性名为bind_data的方法,并在初始化该控件的时候,调用这个被注解标注过的方法。
<variable
name="data"
type="ObservableArrayList<MultipleListBean>" />
<android.support.v7.widget.RecyclerView
android:id="@+id/main_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
bind_data="@{data}" />
在activity当中设置这个数据源。
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
beans = new ObservableArrayList<>();
beans.add(new MultipleListBean(1, bean));
beans.add(new MultipleListBean(1, bean1));
binding.setData(beans);
以上介绍差不多就是dataBinding的一些基础内容的介绍了。欢迎留言讨论。