今天投稿的胡笛同学是我共事多年的同事,他是我见过最优秀的程序员之一,他运营的技术公众号极客联盟干货很多,值得大家关注。今天他将为我们介绍Android MVVM框架Data Binding Library的使用。
什么是MVVM
我们一步步来,从MVC开始。 MVC 我们都知道,模型——视图——控制器。为了使得程序的各个部分分离降低耦合性,我们对代码的结构进行了划分。



Data Binding Library
今年的Google IO 大会上,Android 团队发布了一个数据绑定框架(Data Binding Library)。以后可以直接在 layout 布局 xml 文件中绑定数据了,无需再 findViewById 然后手工设置数据了。其语法和使用方式和 JSP 中的 EL 表达式非常类似。 下面就来介绍怎么使用Data Binding Library。
目前,最新版的Android Studio已经内置了该框架的支持,配置起来也很简单,只需要编辑app目录下的build.gradle文件,添加下面的内容就好了
android {
....
dataBinding {
enabled = true
}
}Data Binding Layout文件
Data Binding layout文件有点不同的是:起始根标签是 layout,接下来一个 data 元素以及一个 view 的根元素。这个 view 元素就是你没有使用Data Binding的layout文件的根元素。举例说明如下:
上面定义了一个com.example.User类型的变量user,然后接着android:text="@{user.firstName}"把变量user的firstName属性的值和TextView的text属性绑定起来。
Data Object
我们来看下上面用到的com.example.User对象。
public class {
public final String firstName;
public final String lastName;
public (String firstName, String lastName) {
.firstName = firstName;
.lastName = lastName;
}
}他有两个public的属性firstName,lastName,这和上面layout文件里面的@{user.firstName}和@{user.lastName}对应
或者下面这种形式的对象也是支持的。
public class {
private final String firstName;
private final String lastName;
public (String firstName, String lastName) {
.firstName = firstName;
.lastName = lastName;
}
// getXXX形式
public String getFirstName {
return .firstName;
}
// 或者属性名和方法名相同
public String lastName {
return .lastName;
}
}添加完标签后,Android Studio就会根据xml的文件名自动生成一个继承ViewDataBinding的类。例如: activity_main.xml就会生成ActivityMainBinding, 然后我们在Activity里面添加如下代码:
@Override
protected onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MainActivityBinding binding = DataBindingUtil.setContentView(, R.layout.main_activity);
User user = User("Test", "User");
binding.setUser(user);
}就像你可以在xml文件里面使用属性android:onClick绑定Activity里面的一个方法一样,Data Binding Library扩展了更多的事件可以用来绑定方法,比如View.OnLongClickListener有个方法onLongClick(), 你就可以使用android:onLongClick属性来绑定一个方法,需要注意的是绑定的方法的签名必须和该属性原本对应的方法的签名完全一样,否则编译阶段会报错。
下面举例来说明具体怎么使用,先看用来绑定事件的类:
public class MyHandlers {
public onClickButton(View view) { ... }
public afterFirstNameChanged(Editable s) { ... }
}然后就是layout文件:
表达式语言(Expression Language)
你可以直接在layout文件里面使用常见的表达式:
数学表达式 + – / * %
字符串链接 +
逻辑操作符 && ||
二元操作符 & | ^
一元操作符 + – ! ~
Shift >> >>> <<
比较 == > < >= <=< p="">
instanceof
Grouping ()
Literals – character, String, numeric, null
值域引用(Field access)
通过[]访问数组里面的对象
三元操作符 ?: 示例:
android:text="@{String.valueOf(index + 1)}" android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}'
更多语法可以参考官网文档:http://developer.android.com/tools/data-binding/guide.html#expression_language
有些时候,代码会修改我们绑定的对象的某些属性,那么怎么通知界面刷新呢?下面就给出两种方案。
让你的绑定数据类继承BaseObservable,然后通过调用notifyPropertyChanged方法来通知界面属性改变,如下:
private static class extends BaseObservable {
private String firstName;
private String lastName;
@Bindable
public String getFirstName {
return .firstName;
}
@Bindable
public String getLastName {
return .lastName;
}
public setFirstName(String firstName){
.firstName = firstName;
notifyPropertyChanged(BR.firstName);
}
public setLastName(String lastName) { .lastName = lastName;
notifyPropertyChanged(BR.lastName);
}
}在需要通知的属性的get方法上加上@Bindable,这样编译阶段会生成BR.[property name],然后使用这个调用方法notifyPropertyChanged就可以通知界面刷新了。如果你的数据绑定类不能继承BaseObservable,那你就只能自己实现Observable接口,可以参考BaseObservable的实现。
Data Binding Library提供了很便利的类ObservableField,还有ObservableBoolean, ObservableByte, ObservableChar, ObservableShort, ObservableInt, ObservableLong, ObservableFloat, ObservableDouble, 和 ObservableParcelable,基本上涵盖了各种我们需要的类型。用法很简单,如下:
private static class {
public final ObservableField firstName = ObservableField<>();
public final ObservableField lastName = ObservableField<>();
public final ObservableInt age = ObservableInt();
}然后使用下面的代码来访问:
user.firstName.set("Google"); age = user.age.get();调用set方法时,Data Binding Library就会自动的帮我们通知界面刷新了。
绑定AdapterView
在一个实际的项目中,相信AdapterView是使用得很多的,使用官方提供给的API来进行AdapterView的绑定需要写很多代码,使用起来不方便,但是由于Data Binding Library提供丰富的扩展功能,所以出现了很多第三方的库来扩展它,下面就来介绍一个比较好用的库binding-collection-adapter,Github地址:https://github.com/evant/binding-collection-adapter
使用的时候在你的build.gradle文件里面添加
compile 'me.tatarka:bindingcollectionadapter:0.16'
如果你要是用RecyclerView,还需要添加
compile 'me.tatarka:bindingcollectionadapter-recyclerview:0.16'
下面就是ViewModel的写法:
public class ViewModel {
public final ObservableList items = ObservableArrayList<>();
public final ItemView itemView = ItemView.of(BR.item, R.layout.item);
}这里用到了ObservableList, 他会在items变化的时候自动刷新界面
然后下面是layout文件:
<>
然后是item layout:
<>
如果有多种样式的布局,那么就需要把ItemView换成ItemViewSelector, 如下:
public final ItemViewSelector itemView = BaseItemViewSelector() {
@Override
public select(ItemView itemView, position, String item) {
itemView.set(BR.item, position == ? R.layout.item_header : R.layout.item);
}
// This is only needed if you are using a BindingListViewAdapter
@Override
public viewTypeCount {
return ;
}
};自定义绑定
正常情况下,Data Binding Library会根据属性名去找对应的set方法,但是我们有时候需要自定义一些属性,Data Binding Library也提供了很便利的方法让我们来实现。 比如我们想在layout文件里面设置ListView的emptyView,以前这个是无法做到的,只能在代码里面通过调用setEmptyView来做; 但是现在借助Data Binding Library,我们可以很容易的实现这个功能了。先看layout文件:
<>
app:emptyView="@{@id/empty_view}"这个代码就用来指定emptyView,下面来看下实现的代码:
@BindingAdapter("emptyView")
public static setEmptyView(AdapterView adapterView, viewId) {
View rootView = adapterView.getRootView();
View emptyView = rootView.findViewById(viewId);
(emptyView != ) {
adapterView.setEmptyView(emptyView);
}
}下面我们来分析上面的代码,@{@id/empty_view}表示引用了@id/empty_view这个id,所以它的值就是int,再看上面的setEmptyView方法,第一个参数AdapterView adapterView表示使用emptyView这个属性的控件,而第二个参数int viewId则是emptyView属性传进来的值,上面的layout可以看出来它就是R.id.empty_view,然后通过id找到控件,然后调用原始的setEmptyView来设置。
上面的代码来自我写的一个Data Binding Library的示例项目DataBinding-album-sample,Github地址:https://github.com/derron/DataBinding-album-sample 它基本上包含了开发一个app常用到的东西,大家有兴趣可以通过去看看。
扫描或长按下方二维码可以关注胡笛同学的微信技术公众号

“崇尚自由,推崇技术,拥抱开源” - 极客联盟:传播新技术理念,分享技术经验。 打造华中区最有影响力的技术公众号。
顺便友情推广下胡笛同学正在做的互联网金融产品,有兴趣的同学可以试试。

如果喜欢这篇文章,记得点赞与分享给好友。
如果你还想了解更多Android开发最佳实践、经验分享、最好用的工具与服务,请继续关注Android程序员,我会保持精品。