简介
DataBinding 是实现视图和数据双向绑定的工具,是Jectpack套件中的一个库;使用DataBinding可以省略重复的代码,例如findViewById()、setText()、setOnClickListener()等等;
启用DataBinding
如果在项目中要使用DataBinding,需要在module下的build.gradle中配置
android {
// ...
dataBinding {
enabled = true
}
}
如果上面不能启用需要换成如下配置,高版本已经更新配置方式:
android {
// ...
buildFeatures {
dataBinding true
}
}
基本使用
此方法指示单纯使用的角度出发,不考虑MVVM架构的情况;
新建Activity,名称为DemoActivity
public class DemoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
}
}
布局文件activity_demo.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_user_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
以上时没有通过DataBinding改造之前的模板代码;
引入DataBinding首先修改布局文件,可以通过AndroidStudio快速键:Alt + 回车键 ,选择Convert to data binding layout” 生成使用DataBinding后需要的布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.ang.school.databinding.DemoActivity">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:text="@{Login.userName,default= 老马}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@{Login.phone,default= 110}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_user_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 中配置变量名和类型,以MVVM为例解释,比如要实现 MVVM 的 ViewModel ,就需要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道;
创建一个model
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
public class LoginInfo extends BaseObservable {
private String userName;
private String phone;
private String userPassword;
@Bindable
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
notifyPropertyChanged(BR.userName);
}
@Bindable
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
notifyPropertyChanged(BR.phone);
}
@Bindable
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
notifyPropertyChanged(BR.userPassword);
}
}
数据绑定
为了实现绑定还需要对数据实体类做处理,实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservable、ObservableField、ObservableCollection
BaseObservable 提供了 notifyChange() 和 notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BR 的 flag,该 BR 的生成通过注释 @Bindable 生成,可以通过 BR notify 特定属性关联视图,学习了这些上面的model就很好理解了;
在布局文件中的data标签下声明要使用到的变量名、类的全路径
<data>
<variable
name="info"
type="cn.ang.school.databinding.LoginInfo" />
</data>
如果 LoginInfo 类型要多处用到,也可以直接将之 import 进来,这样就不用每次都指明整个包名路径了,而 java.lang.* 包中的类会被自动导入,所以可以直接使用
<data>
<import type="cn.ang.school.databinding.LoginInfo"/>
<variable
name="info"
type="LoginInfo"/>
</data>
如果存在 import 的类名相同的情况,可以使用 alias 指定别名
<data>
<import type="cn.ang.school.databinding.LoginInfo" />
<import
alias="LoginUser"
type="cn.ang.school.databinding.model2.LoginInfo" />
<variable
name="info"
type="LoginInfo" />
<variable
name="userInfo"
type="LoginUser" />
</data>
这里声明了一个 LoginInfo类型的变量 info,我们要做的就是使这个变量与两个 TextView 控件挂钩,通过设置 info的变量值同时使 TextView 显示相应的文本;<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="info"
type="cn.ang.school.databinding.LoginInfo" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="cn.ang.school.databinding.DemoActivity">
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
android:text="@{info.userName,default= 老马}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@{info.phone,default= 110}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_user_name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
通过 @{info.userName} 使 TextView 引用到相关的变量,DataBinding 会将之映射到相应的 getter 方法,那么其他控件也是通过@{}或@={}的方式进行引用,其中@={}的方式表示双向绑定。目前支持双向绑定的控件如下:
AbsListView android:selectedItemPosition ; CalendarView android:date ;
CompoundButton android:checked ;DatePicker android:year, android:month, android:day ;NumberPicker android:value ;RadioGroup android:checkedButton ;
RatingBar android:rating ;SeekBar android:progress ;
TabHost android:currentTab ;TextView android:text ;
TimePicker android:hour, android:minute
然后可以在 DemoActivity 中通过 DataBindingUtil 设置布局文件,省略原先 Activity 的 setContentView() 方法,并为变量 info赋值
public class DemoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDemoBinding demoBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
demoBinding.tvUserName.setText("老马老师");
LoginInfo info = new LoginInfo();
info.setPhone("18899988866");
info.setUserName("小马哥");
info.setUserPassword("10000");
demoBinding.setInfo(info);
}
}
通过DataBindingUtil可以动态生成一个ViewDataBinding的子类,类名以layout文件名大写加Binding组成例如上面使用的:
ActivityDemoBinding demoBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
DataBinding在RecycleView中的使用
DataBinding在Fragment中的使用
总结
之所以要单独学习DataBinding,主要原因是如果没有接触过MVVM架构的,直接学习MVVM有点吃力不好理解,所以单独拿出来学习比较好;领略了DataBinding框架的魅力和优点之后再接触MVVM比较容易上手;但是实现MVVM不一定非要使用DataBinding,MVVM是一种思想,DataBinding是谷歌推出的方便实现MVVM的工具。在google推出DataBinding之前,因为xml layout功能较弱,想实现MVVM非常困难。而DataBinding的出现可以让我们很方便的实现MVVM