这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战
Android DataBinding扫盲篇(一)
前几天和朋友探讨小程序上的问题,发现居然也是使用的双向绑定的模式,我就说,我们大Android很早就有双向绑定啊。这篇文章记录实现数据绑定的过程,给没有接触过的数据绑定的同学一点帮助。
一、DataBinding是什么
简单的来说,DataBinding是一个实现数据和UI绑定的支持库,同时也是实现MVVM模式所依赖的工具。
该库在2015年就已经诞生了,MVVM的盛行使得更多人了解它,后来发现很多前端框架中也有数据绑定这种技术,比如我看到的小程序是这么写的:
_this.setData({
treeData:[item]
})
<view list='{{treeData}}' />
这种写法就和 Android 中的写法很相似了,也许这些语言学到深处,都是相通的。
二、实现简单的DataBinding
DataBinding是MVVM架构所依赖的工具;可以省掉findViewById的操作。
2.1 开启DataBinding
我们仅需要在module的build.gradle中进行开启即可,示例代码
android {
compileSdkVersion 28
buildToolsVersion "28.0.3"
...
// 启用dataBinding
dataBinding{
enabled true
}
}
2.2 新建一个对象
没什么好说的,一个普通的对象
public class Person {
private int id;
private String name;
private String sex;
public Person() {
}
public Person(int id, String name, String sex) {
this.id = id;
this.name = name;
this.sex = sex;
}
// get set
}
2.3 修改布局文件
将layout文件更改,使其符合数据绑定的规则。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 增加部分-->
<data class="MainBinding">
<variable
name="person"
type="com.demo.mvvmdemo.Person" />
</data>
<!-- 原有layout部分-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(person.id)}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{person.name}" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{person.sex}" />
</LinearLayout>
</layout>
重点是增加了<data>这个标签,class是指定了绑定后的名称,在activity中获取,不明白就接着看。
其中android:text="@{String.valueOf(person.id)} 对应着我们创建对象的id的属性。
2.4 创建对应的Activity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
MainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
Person person = new Person(1, "张三", "男");
mainBinding.setPerson(person);
}
}
将原始的setContentView换成上诉代码中的显示方式。其中MainBinding就对应着layout中的<data class="MainBinding">, 如果不指定class,默认的就是layout的名字加上‘Binding’,比如获取activity_layout.xml,就是ActivityMainBinding。
setPerson 是将对象绑定。
至此,一个简单的数据绑定就已经完成了。
三、即时更改UI
上面的代码虽然已经实现了数据绑定,但我们每次更改数据,想要更新页面时,都需要重新调用setData(Object)来更新试图,这样显然不太方便。
怎么实现一次绑定,随时更新数据即更新试图呢
这种场景第一就想到观察者模式,看看是怎么实现的。
3.1 单向绑定方式一、继承BaseObservable
- 让对象继承
BaseObservable - 在每一个get方法上方加上
@Bindable - 在每一个set方法中加入
notifyPropertyChanged(BR.对应的属性)
代码示例
public class Person extends BaseObservable {
private int id;
private String name;
private String sex;
public Person() {
}
public Person(int id, String name, String sex) {
this.id = id;
this.name = name;
this.sex = sex;
}
@Bindable
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
notifyPropertyChanged(BR.id);
}
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
notifyPropertyChanged(BR.sex);
}
}
Activity
public class MainActivity extends AppCompatActivity {
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
final ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
person = new Person(1, "张三", "男");
mainBinding.setPerson(person);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
person.setId(2);
person.setName("李四");
person.setSex("女");
}
},2000);
}
}
延迟两秒刷新数据,在两秒后,更新的对象的属性,可以看到界面也刷新了。
3.1 单向绑定方式二、定义ObserverField对象
- 改变了实体类的属性。
- 设置属性时方法也变化了。
person.getId().set(2);
person.getName().set("李四");
person.getSex().set("女");
直接看代码吧。
实体类
public class Person {
public ObservableField<Integer> id = new ObservableField<>();
public ObservableField<String> name = new ObservableField<>();
public ObservableField<String> sex = new ObservableField<>();
public ObservableField<Integer> getId() {
return id;
}
public void setId(ObservableField<Integer> id) {
this.id = id;
}
public ObservableField<String> getName() {
return name;
}
public void setName(ObservableField<String> name) {
this.name = name;
}
public ObservableField<String> getSex() {
return sex;
}
public void setSex(ObservableField<String> sex) {
this.sex = sex;
}
}
Activity
public class MainActivity extends AppCompatActivity {
Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
final ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
person = new Person();
mainBinding.setPerson(person);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
person.getId().set(2);
person.getName().set("李四");
person.getSex().set("女");
}
},2000);
}
}
可以看到,两种方式最后的效果是一致的。