本系列:
- 从零开始的 Android 新项目(1):架构搭建篇
- 从零开始的 Android 新项目(2):Gradle 篇
- 从零开始的 Android 新项目(3):谁告诉你MVP和MVVM是互斥的
- 从零开始的 Android 新项目(4):Dagger2 篇
- 从零开始的 Android 新项目(5):Repository 层(上)
- 从零开始的 Android 新项目(6):Repository 层(下)
- 从零开始的 Android 新项目(7):Data Binding 入门篇
承接上篇,本篇继续讲解一些更加进阶的内容,包括:列表绑定、自定义属性、双向绑定、表达式链、Lambda表达式、动画、Component注入(测试)等。
Demo源码库:DataBindingSample。
列表绑定
App中经常用到列表展示,Data Binding在列表中一样可以扮演重要的作用,直接绑定数据和事件到每一个列表的item。
RecyclerView
过去我们往往会使用ListView、GridView、或者GitHub上一些自定义的View来做瀑布流。自从RecyclerView出现后,我们有了新选择,只需要使用LayoutManager就可以。RecyclerView内置的垃圾回收,ViewHolder、ItemDecoration装饰器机制都让我们可以毫不犹豫地替换掉原来的ListView和GridView。
所以本篇仅拿RecyclerView做例子。
Generic Binding
我们只需要定义一个基类ViewHolder,就可以方便地使用上Data Binding:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class BindingViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder { protected final T mBinding; public BindingViewHolder(T binding) { super(binding.getRoot()); mBinding = binding; } public T getBinding() { return mBinding; } } |
Adapter可以直接使用该ViewHolder,或者再继承该ViewHolder,T使用具体Item的Binding类(以便直接访问内部的View)。至于Listener,可以在onBindViewHolder
中进行绑定,做法类似于普通View,不做赘述。
由于同一个adapter未必只有一种ViewHolder,可能有好几种View type,所以在onBindViewHolder
中,我们只能获取基类的ViewHolder类型,也就是BindingViewHolder
,所以无法去做具体的set操作,如setEmployee。这时候就可以使用setVariable
接口,然后通过BR来指定variable的name。
又比如我们可能有多重view type对应的xml,可以将对应的variable name全都写为item,这样可以避免强制转换Binding类去做set操作。类似地,监听器也能都统一取名为listener或者presenter。
开源方案及其局限性
evant / binding-collection-adapter
radzio / android-data-binding-recyclerview
均提供了简化的RV data binding方案。
前者可以直接在layout的RV上,设置对应的items和itemView进去,也支持多种view type,还能直接设定对应的LayoutManager。
后者类似地,提供了xml中直接绑定RV的items和itemView的功能。
相比来说前者的功能更强大一些。但这些开源库对应地都丧失了灵活性,ViewModel需要遵循规范,事件的绑定也比较死板,不如自己继承Adapter来得强大。唯一的好处也就是可以少写点代码了。
自定义属性
默认的android命名空间下,我们会发现并不是所有的属性都能直接通过data binding进行设置,比如margin,padding,还有自定义View的各种属性。
遇到这些属性,我们就需要自己去定义它们的绑定方法。
Setter
就像Data Binding会自动去查找get方法一下,在遇到属性绑定的时候,它也会去自动寻找对应的set方法。
拿DrawerLayout举一个例子:
1 2 3 4 5 |
<android.support.v4.widget.DrawerLayout android:layout_width=“wrap_content” android:layout_height=“wrap_content” app:scrimColor=“@{@color/scrimColor}”/> |
如此,通过使用app命名空间,data binding就会去根据属性名字找对应的set方法,scrimColor -> setScrimColor:
1 2 3 4 5 |
public void setScrimColor(@ColorInt int color) { mScrimColor = color; invalidate(); } |
如果找不到的话,就会在编译期报错。
利用这种特性,对一些第三方的自定义View,我们就可以继承它,来加上我们的set函数,以对其使用data binding。
比如Fresco的SimpleDraweeView
,我们想要直接在xml指定url,就可以加上:
1 2 3 4 |
public void setUrl(String url) { view.setImageURI(TextUtils.isEmpty(url) ? null : Uri.parse(url)); } |
这般,就能直接在xml中去绑定图片的url。这样是不是会比较麻烦呢,而且有一些系统的View,难道还要继承它们然后用自己实现的类?其实不然,我们还有其他方法可以做到自定义属性绑定。
BindingMethods
如果View本身就支持这种属性的set,只是xml中的属性名字和java代码中的方法名不相同呢?难道就为了这个,我们还得去继承View,使代码产生冗余?
当然没有这么笨,这时候我们可以使用BindingMethods注释。
android:tint是给ImageView加上着色的属性,可以在不换图的前提下改变图标的颜色。如果我们直接对android:tint使用data binding,由于会去查找setTint方法,而该方法不存在,则会编译出错。而实际对应的方法,应该是setImageTintList
。
这时候我们就可以使用BindingMethod指定属性的绑定方法:
1 2 3 4 5 6 |
@BindingMethods({ @BindingMethod(type = “android.widget.ImageView”, attribute = “android:tint”, method = “setImageTintList”), }) |
我们也可以称BindingMethod为Setter重命名。
BindingAdapter
如果没有对应的set方法,或者方法签名不同怎么办?BindingAdapter注释可以帮我们来做这个。
比如View的android:paddingLeft属性,是没有对应的直接进行设置的方法的,只有setPadding(left, top, right, bottom),而我们又不可能为了使用Data Binding去继承修改这种基础的View(即便修改了,还有一堆继承它的View呢)。又比如那些margin,需要修改必须拿到LayoutParams,这些都无法通过简单的set方法去做。
这时候我们可以使用BindingAdapter定义一个静态方法:
1 2 3 4 5 6 7 8 |
@BindingAdapter("android:paddingLeft") oid 新项目(2):Gradle 篇
承接上篇,本篇继续讲解一些更加进阶的内容,包括:列表绑定、自定义属性、双向绑定、表达式链、Lambda表达式、动画、Component注入(测试)等。 Demo源码库:DataBindingSample。 列表绑定App中经常用到列表展示,Data Binding在列表中一样可以扮演重要的作用,直接绑定数据和事件到每一个列表的item。 RecyclerView过去我们往往会使用ListView、GridView、或者GitHub上一些自定义的View来做瀑布流。自从RecyclerView出现后,我们有了新选择,只需要使用LayoutManager就可以。RecyclerView内置的垃圾回收,ViewHolder、ItemDecoration装饰器机制都让我们可以毫不犹豫地替换掉原来的ListView和GridView。 所以本篇仅拿RecyclerView做例子。 Generic Binding我们只需要定义一个基类ViewHolder,就可以方便地使用上Data Binding:
Adapter可以直接使用该ViewHolder,或者再继承该ViewHolder,T使用具体Item的Binding类(以便直接访问内部的View)。至于Listener,可以在 由于同一个adapter未必只有一种ViewHolder,可能有好几种View type,所以在 又比如我们可能有多重view type对应的xml,可以将对应的variable name全都写为item,这样可以避免强制转换Binding类去做set操作。类似地,监听器也能都统一取名为listener或者presenter。 开源方案及其局限性evant / binding-collection-adapter 均提供了简化的RV data binding方案。 前者可以直接在layout的RV上,设置对应的items和itemView进去,也支持多种view type,还能直接设定对应的LayoutManager。 后者类似地,提供了xml中直接绑定RV的items和itemView的功能。 相比来说前者的功能更强大一些。但这些开源库对应地都丧失了灵活性,ViewModel需要遵循规范,事件的绑定也比较死板,不如自己继承Adapter来得强大。唯一的好处也就是可以少写点代码了。 自定义属性默认的android命名空间下,我们会发现并不是所有的属性都能直接通过data binding进行设置,比如margin,padding,还有自定义View的各种属性。 遇到这些属性,我们就需要自己去定义它们的绑定方法。 Setter就像Data Binding会自动去查找get方法一下,在遇到属性绑定的时候,它也会去自动寻找对应的set方法。 拿DrawerLayout举一个例子:
如此,通过使用app命名空间,data binding就会去根据属性名字找对应的set方法,scrimColor -> setScrimColor:
如果找不到的话,就会在编译期报错。 利用这种特性,对一些第三方的自定义View,我们就可以继承它,来加上我们的set函数,以对其使用data binding。 比如Fresco的
这般,就能直接在xml中去绑定图片的url。这样是不是会比较麻烦呢,而且有一些系统的View,难道还要继承它们然后用自己实现的类?其实不然,我们还有其他方法可以做到自定义属性绑定。 BindingMethods如果View本身就支持这种属性的set,只是xml中的属性名字和java代码中的方法名不相同呢?难道就为了这个,我们还得去继承View,使代码产生冗余? 当然没有这么笨,这时候我们可以使用BindingMethods注释。 android:tint是给ImageView加上着色的属性,可以在不换图的前提下改变图标的颜色。如果我们直接对android:tint使用data binding,由于会去查找setTint方法,而该方法不存在,则会编译出错。而实际对应的方法,应该是 这时候我们就可以使用BindingMethod指定属性的绑定方法:
我们也可以称BindingMethod为Setter重命名。 BindingAdapter如果没有对应的set方法,或者方法签名不同怎么办?BindingAdapter注释可以帮我们来做这个。 比如View的android:paddingLeft属性,是没有对应的直接进行设置的方法的,只有setPadding(left, top, right, bottom),而我们又不可能为了使用Data Binding去继承修改这种基础的View(即便修改了,还有一堆继承它的View呢)。又比如那些margin,需要修改必须拿到LayoutParams,这些都无法通过简单的set方法去做。 这时候我们可以使用BindingAdapter定义一个静态方法: |