1. 简介
DataBinding 是谷歌官方发布的一个框架,使数据能够单向或双向绑定到 layout 文件中,能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码
2. 使用
启用DataBinding
在build.gradle文件Android代码块下加入以下代码,就可以启用DataBinding啦
//启用DataBinding
dataBinding {
enabled = true
}
修改布局文件
在布局文件中,选择跟布局,按下Alt + 回车键,选择 Convert to data binding layout 就自动生成DataBinding布局啦
我们来看看转换后的布局
可以看到,转换后我们的跟布局变成了 layout , 中间多了 data 标签,我们的布局文件被移动到下方去啦
其他的都好理解,我们来看看新增的这个 data 标签是干嘛的呢
data 标签就是用来声明在页面中要用到的变量以及变量类型
下面我们来看看怎么使用
首先我们定义一个Model类
public class Account {
private String accountName;
private int level;
....
}
在页面 data 标签中声明需要使用的变量和类
<data>
<variable
name="account"
type="com.chxip.databinding.Account" />
</data>
然后我们在下方布局定义两个TextView,用于显示账号名称和等级,怎么和上方声明的变量进行绑定呢
答案就是使用 @{变量名},下面我们看具体使用
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
.....
<TextView
android:id="@+id/tv_account_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{account.accountName}" />
...
<TextView
android:id="@+id/tv_account_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{account.level}" />
</LinearLayout>
@{account.accountName} 就可以把 accountName 的值设置到TextView 上啦
最后我们修改一下Activity
ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
Account account=new Account();
account.setAccountName("chxip6");
account.setLevel(100);
binding.setAccount(account);
}
首先我们定义当前ActivityBinding 的变量
然后覆盖setContentView() 为 DataBindingUtil.setContentView() ,这个方法会返回当前ActivityBinding的对象
ActivityMainBinding 这个类为自动生成,如果没有,Clean 一下项目就会出现啦,生成规则为Activity的名称+Binding
然后定义一个Account 对象,赋值账号名称和等级,最后调用binding.setAccount()方法,传入account
我们现在来运行一下看看
我们运行发现报错了,查看错误发现等级是int,但是TextView 不能直接setTex(int ),我们需要修改一下
<TextView
android:id="@+id/tv_account_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{Integer.toString(account.level)}" />
我们需要在赋值的时候,把int 转成String
从这里可以看出,DataBinding 在内部也是使用TextView的setText() 方法赋值的
java.lang.* 包中的类会自动导入进来,可以直接使用
我们现在来运行看看效果
可以看到,账号名称和等级已经正确的显示到页面上啦
那我们的值改变了后,怎么刷新页面呢?
下面我们就来改变等级看看,添加一个按钮,点击按钮,等级加一
binding.btnLevelAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
account.setLevel(account.getLevel()+1);
binding.setAccount(account);
}
});
我们这里不用findViewById啦,直接使用binding对象就可以找到这个按钮,设置其点击事件,在点击事件中,我们修改了account的等级,在调用binding.setAccount(),这样就可以改变页面的值啦,我们来看一看运行效果
当然我们也可以直接在布局文件中绑定点击事件,后续我们在介绍这种方式
我们看到等级的值已经成功改变啦
值改变后,可不可以自定刷新页面呢?
答案是可以的,下面我们来看看怎么在等级改变时,自动刷新页面
我们可以让我们的Model类继承BaseObservable这个类,BaseObservable 提供了 notifyChange() 和 notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BR 的 flag,该 BR 的生成通过注释 @Bindable 生成DataBinding框架会在BR这个生成类中,为name属性生成一个唯一的标识符
首先我们先修改Model类
//如果是 private 修饰符,则在成员变量的 get 方法上添加 @Bindable 注解
//只有在需要在单独更新这个属性的时候,才需要加入这个注解
private String accountName;
//如果是 public 修饰符,则可以直接在成员变量上方加上 @Bindable 注解
@Bindable
public int level;
...
public void setLevel(int level) {
this.level = level;
//自动更新值
notifyPropertyChanged(BR.level);
}
/**
* 更新账号和密码
* @param accountName
* @param level
*/
public void setData(String accountName,int level){
this.accountName = accountName;
this.level = level;
notifyChange();
}
然后我们定义两个按钮,第一个修改等级,第二个修改账号名称和等级
binding.btnLevelAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//此处只需要调用setLevel方法
account.setLevel(account.getLevel()+1);
}
});
binding.btnUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//此处调用setData方法
account.setData("无敌就是我",1000);
}
});
我们再来看看效果
我们可以看到,页面上的值都可以更新啦
这样写还需要每次都调用notifyChange 方法更新,还是没有在数据改变时,自动更新,我们可以使用ObservableField 来实现真正的自动更新
ObservableField 可以理解为官方对 BaseObservable 中字段的注解和刷新等操作的封装,官方原生提供了对基本数据类型的封装,例如 ObservableBoolean、ObservableByte、ObservableChar、ObservableShort、ObservableInt、ObservableLong、ObservableFloat、ObservableDouble 以及 ObservableParcelable ,也可通过 ObservableField 泛型来申明其他类型
如果属性是集合,DataBinding框架也提供了专门的类:
ObservableArrayMap
ObservableArrayList
下面我们来修改一下Modle的代码
private ObservableField<String> accountName;
private ObservableInt level;
public ObservableField<String> getAccountName() {
if(accountName==null){
accountName = new ObservableField<>();
}
return accountName;
}
public void setAccountName(ObservableField<String> accountName) {
this.accountName = accountName;
}
public ObservableInt getLevel() {
if(level == null ){
level = new ObservableInt();
}
return level;
}
public void setLevel(ObservableInt level) {
this.level = level;
}
/**
* 更新账号和密码
* @param accountName
* @param level
*/
public void setData(String accountName,int level){
getAccountName().set(accountName);
getLevel().set(level);
}
在修改一下点击事件 的代码
binding.btnLevelAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
account.getLevel().set(account.getLevel().get()+1);
}
});
binding.btnUpdate.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
account.setData("无敌就是我",1000);
}
});
这样就可以自动更新数据啦
使用 ObservableField 等 ObservableXXX类,可以让我们省去继承BaseObservable、添加注解,通知DataBinding框架等麻烦,但天下没有免费的午餐,代价就是性能会降低,所以只能在少量属性上这样用,如果大量使用,用户体验可能不太好。
双向绑定
双向绑定的意思即为当数据改变时同时使视图刷新,而视图改变时也可以同时改变数据,双向绑定只是在页面绑定数据的时候,多加一个等号
我们在页面中添加一个EditText,使其双向绑定accountName账号
<EditText
android:id="@+id/et_account_level"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入账号"
android:text="@={account.accountName}" />
我们来运行看看效果
我们看到,在输入框中改变账号的值,对应TextView中的值也改变啦
好了,对于DataBinding基本的接收和使用就到这里了,欢迎大家留言交流
有兴趣了解其实现原理的,大家可以看这个
实现原理:DataBinding实现原理探析