3.DataBinding

什么是DataBinding

1
2
3
4
DataBinding 是谷歌官方发布的一个框架,顾名思义即为数据绑定,是 MVVM 模式在 Android 上的一种实现,用于降低布局和逻辑的
耦合性,使代码逻辑更加清晰。
DataBinding 能够省去我们一直以来的 findViewById() 步骤,大量减少 Activity 内的代码,数据能够单向或双向绑定到 layout 文件
中,有助于防止内存泄漏,而且能自动进行空检测以避免空指针异常。

启用 DataBinding 的方法是在对应 Model 的 build.gradle 文件里加入以下代码,同步后就能引入对 DataBinding 的支持

1
2
3
4
5
6
android {
...
buildFeatures {
dataBinding true
}
}

简单使用

启用 DataBinding 后,这里先来看下如何在布局文件中绑定指定的变量

打开布局文件,选中根布局的 ViewGroup,按住 Alt + 回车键,点击 “Convert to data binding layout”,就可以生成 DataBinding 需要的布局规则

img

这里先来声明一个 Modle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class User {
private String name;
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
this.pwd = pwd;
}
}

和原始布局的区别在于多出了一个 layout 标签将原布局包裹了起来,data 标签用于声明要用到的变量以及变量类型,要实现 MVVM 的 ViewModel 就需要把数据(Model)与 UI(View)进行绑定,data 标签的作用就像一个桥梁搭建了 View 和 Model 之间的通道

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type= "com.tyl.mystudy2.User"/>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:textSize="50dp"
android:id="@+id/tv1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.name}"
/>
<TextView
android:textSize="50dp"
android:id="@+id/tv2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{user.pwd}"
/>
</LinearLayout>
</layout>

通过 @{viewModel.name} 使 TextView 引用到相关的变量,DataBinding 会将之映射到相应的 getter 方法 之后可以在 Activity 中通过 DataBindingUtil 设置布局文件,省略原先 Activity 的 setContentView() 方法,并为变量 userInfo 赋值

如果 User 类型要多处用到,也可以直接将之 import 进来,这样就不用每次都指明整个包名路径了,而 java.lang.* 包中的类会被自动导入,所以可以直接使用

1
2
3
4
5
6
<data>
<import type="com.tyl.mystudy2.User"/>
<variable
name="user"
type= "User"/>
</data>

由于 @{user.name}在布局文件中并没有明确的值,所以在预览视图中什么都不会显示,不便于观察文本的大小和字体颜色等属性,此时可以为之设定默认值(文本内容或者是字体大小等属性都适用),默认值将只在预览视图中显示,且默认值不能包含引号

1
android:text="@{user.name,default=defaultValue}"

在 Activity 中通过 DataBindingUtil 设置布局文件,省略原先 Activity 的 setContentView() 方法,并为变量 userInfo 赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DataBindingActivity extends AppCompatActivity {
private ActivityDatabindingBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//注意DataBindingUtil.setContentView()的返回值是DataBindingUtil工具生成的Binding类,
//布局文件名来生成,将之改为首字母大写的驼峰命名法来命名,并省略布局文件名包含的下划线
binding = DataBindingUtil.setContentView(this, R.layout.activity_databinding);
//数据是从网络或是数据库拿来的
User user=new User("jett","123");
binding.setUser(user);//view.setText(text);
binding.setVariable(BR.name,"jett");
}
}

此外,也可以通过 ActivityMain2Binding 直接获取到指定 ID 的控件

1
binding.tv1.setText("tyl");

Databinding 同样是支持在 FragmentRecyclerView 中使用 。例如,可以看 Databinding 在 Fragment 中的使用

1
2
3
4
5
6
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
FragmentBlankBinding fragmentBlankBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_blank, container, false);
fragmentBlankBinding.setHint("Hello");
return fragmentBlankBinding.getRoot();
}

以上实现数据绑定的方式,每当绑定的变量发生变化的时候,都需要重新向 ViewDataBinding 传递新的变量值才能刷新 UI 。接下来看如何实现自动刷新 UI

驱动 UI 刷新

实现数据变化自动驱动 UI 刷新的方式有三种:BaseObservableObservableFieldObservableCollection

BaseObservable

一个纯净的 ViewModel 类被更新后,并不会让 UI 自动更新。而数据绑定后,我们自然会希望数据变更后 UI 会即时刷新,Observable 就是为此而生的概念

BaseObservable 提供了 notifyChange()notifyPropertyChanged() 两个方法,前者会刷新所有的值域,后者则只更新对应 BRflag,该 BR 的生成通过注释 @Bindable 生成,可以通过 BR notify 特定属性关联的视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class User extends BaseObservable {
//如果是 public 修饰符,则可以直接在成员变量上方加上 @Bindable 注解
@Bindable
public String name;
//如果是 private 修饰符,则在成员变量的 get 方法上添加 @Bindable 注解
private String pwd;
public User(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
@Bindable
public String getPwd() {
return pwd;
}

public void setPwd(String pwd) {
//更新所有字段
this.pwd = pwd;
notifyChange();
}
}

setName() 方法中更新的只是本字段,而 setPwd() 方法中更新的是所有字段