0、前言
Android传统框架中,Activity承载了太多的任务,业务逻辑的处理、view逻辑处理等等都写在Activity中,代码过于臃肿耦合。Fragment出现后,臃肿情况稍微好转一点,毕竟代码大都迁移到了Fragment中。我们可以对Fragment再次进行分块,进一步降低代码臃肿现象,但是代码耦合的现象仍然无法得以解决。
Google官方提供的基于MVVM架构Data Binding框架,一定程度上有效解决了问题,而且对于现有框架的侵入几乎没有。
1、准备工作
(1)Android Studio 需要版本1.3及以上
(2)Gradle 需要版本1.5.0及以上
(3)module build.gradle 添加data binding配置
android {
....
dataBinding {
enabled = true
}
}
2、入门
(1)首先定义一个简单的model-com.example.User。
public class User {
public String firstName;
public String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
(2)布局文件data_bind_test.xml,写法有所改动,导入Model可以直接对View进行数据绑定。
<?xml version="1.0" encoding="utf-8"?>
// 根节点必须为layout,表明这是一个data binding布局文件;
<layout xmlns:android="http://schemas.android.com/apk/res/android">
// 可指定class属性,即自动生成的ViewDataBinding名称;如果未指定,根据布局文件名生成,例如:DataBindTestBinding
<data class="com.example.UserDataBinding">
<import type="com.example.StringUtil" alias="StringFormatUtil" /> // 导入工具类
<variable name="user" type="com.example.User"/> // 声明Model变量
<variable name="sex" type="String"/> // 也可以声明基本类型或者String类型变量
</data>
//传统的布局代码
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/> //直接绑定model数据
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{sex}"/> //基本类型数据
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{StringFormatUtil.format(user.lastName)}"/> //使用导入的工具类
</LinearLayout>
</layout>
当import两个同名不同包的Java class时,框架是无法区分的,这时可以使用alias 属性指定一个别名来进行区分,这点和Mybatis框架相似!
官方文档上还介绍了data binding框架可以直接为view 绑定事件监听,例如onClick单击事件、onLongClick长按事件。但是试验下发现,没有效果,可能测试的框架版本还没有支持。
(3)以上两步,已经将view和model进行了绑定,但是数据从哪里来呢?
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 返回ViewDataBinding,并将rootView添加到activity布局中
UserDataBinding binding = DataBindingUtil.setContentView(this, R.layout.data_bind_test.xml);
User user = new User("Kobe", "Bryant");
binding.setUser(user);
}
DataBindingUtil类的setContentView方法可以返回指定布局生成的ViewDataBinding,ViewDataBinding会根据布局文件声明的变量,自动为我们生成setter接口,直接传入数据即可。
获取ViewDataBinding还可以有以下方式:
// 只inflate布局,返回ViewDataBinding
UserDataBinding binding = UserDataBinding.inflate(getLayoutInflater());
或者
// 在ListView、RecyclerView的Adapter中使用如下方法
UserDataBinding binding = UserDataBinding.inflate(layoutInflater, viewGroup, false);
//or
UserDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.data_bind_test.xml, viewGroup, false);
如果想获取到inflate的view,可以使用ViewDataBinding.getRoot()方法。
3、Observable
现在View已经可以加载Model的数据了,但是当Model发生改变时,发现View并没有更新。Data Binding框架把这部分工作交由我们自己来实现,提供了以下几种实现方式供我们选择。
(1)实现Observable接口
Model改变时,手到调用方法进行notify。为了方便,框架已经帮我们封装了下,提供了BaseObservable类,我们的Model直接extends就好。
private static class User extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName() {
return this.firstName;
}
@Bindable
public String getLastName() {
return this.lastName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public void setLastName(String lastName) {
this.lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}
@Bindable注解的getter方法对应的属性会在BR(类似R文件)生成一个标识,在数据改变时,调用notifyPropertyChanged方法可以对指定的Model属性数据更新进行通知,View收到通知后更新绑定的数据。
(2)使用ObservableField
上面的方法略为繁琐,需要我们手动调用方法去通知view更新。因此,框架为我们做了进一步封装,提供了ObservableField、ObservableXxx(Xxx为基本数据类型)、ObservableParcelable等封装类。这些类都直接或者间接继承自BaseObservable,提供get()和set()方法,在set()方法中直接调用了notifyChange()方法通知View更新。
/**
* @return the stored value.
*/
public T get() {
return mValue;
}
/**
* Set the stored value.
*/
public void set(T value) {
if (value != mValue) {
mValue = value;
notifyChange(); // notify
}
}
使用方式:
public class User {
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
User user = new User();
user.firstName.set("Harden");
user.age.set(25);
另外,对于集合类型,Data Binding框架提供了ObservableArrayList、ObservableArrayMap等封装,基本都是在集合的数据操作中,加入了框架的notify机制。
官方教程:https://developer.android.com/tools/data-binding/guide.html