DataBinding是可以用来实现MVVM的一种很合适的方案,可以极大的精简Activity的逻辑代码。他能够在xml中引入简单的逻辑代码,也能够非常灵活的利用View自带的属性进行设置,同时也支持数据的双向绑定或者是单纯的View绑定。
基础使用
简单的使用或者是引入可以去官网查看,我们直接看如何使用。
- 在xml中,我们可以通过
<data class="BindingName">
来指定生成的DataBinding文件的名称而不使用默认的layout布局作为Binding名称 - 在值
@{}
中,常见的转义字符替换,使用&&
需要使用&&
代替,<
使用<
代替,>
使用>
,空格使用 
,"
引号"
,格式化字符串android:text="@{@string/string_format(
吴彦祖,
男)}"
。 - 在布局中,假如
merge
作为根节点,include
作为merge
的直接子节点,这种情况DataBinding是不支持的。 - 在xml中一个View可以通过另外一个View的id去引用它,需要注意对应的属性应该是支持双向绑定的。
- 在xml中,把父布局的相关变量传递给
include
布局通过bind
方式,比如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// 父布局
<layout
xmlns:bind="http://schemas.android.com/apk/res-auto" >
<data>
<variable name="name" type="String”/>
</data>
<Linearlayout
....
>
<include
....
layout="@layout/include_layout"
bind:includeName="@{name}"
</Linearlayout>
</layout>
//include_layout布局
<layout
xmlns:bind="http://schemas.android.com/apk/res-auto" >
<data>
<variable includeName="name" type="String”/>
</data>
<Linearlayout
.... >
<TextView
...
android:text="@{includeName}"
</Linearlayout>
</layout> - 使用相关的Observable类(ObservableField,ObservableBoolean等),然后通过
@{}
进行单向数据绑定(data->View)。使用@={}
进行双向的数据绑定,假如我们同时还需要观测数据的变化回调,则可以使用MutableLiveData
进行双向数据绑定。 - 把布局与Activity/Fragment/View绑定的方式有以下几种
1
2
3
4
5
6// Act直接绑定xml,不需要另外调用setContentView
setContentView(@NonNull Activity activity, int layoutId) //返回Binding对象
// 这种是用在Fragment中,通过返回的Binding对象去获取跟View然后返回,自定义View获取布局也可以用这种,RecycleView的Item布局也可以用这种
inflate(@NonNull LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent) //返回Binding对象
// 直接使用自动生成的Binding类的inflate或者是bind静态方法,然后得到Binding对象,例如有一个MainBinding的文件
MainBinding.inflate(LayoutInflater) - 我们在得到Binding对象之后,就可以通过
mBinding.viewId
的方式来引用xml对应的组件实例而不需要通过findViewById
- 给xml的变量设置值除了可以通过获取Binding对象得到变量赋值之外,还可以通过BR类得到对应的id然后赋值,BR类是编译之后生成的一个映射变量的id。例子如
1
2
3
4
5
6
7
8
9
10
11
12
13
14// 布局 main.xml
...
<data>
<variable name="name" type="String"/>
<variable name="age" type="Integer"/>
</data>
...
//变量赋值方式
//第一种
mBinding.name = "name"
mBinding.age= 10
//第二种
mBinding.setVariable(BR.name,"name");
mBinding.setVariable(BR.age,10);
BindingAdapter
@BindingAdapter
注解,他的作用是类似增加View对应属性的set方法。我们能在xml中使用自身属性绑定Data,是因为要么View对应的属性提供了set
方法或者库默认提供的BindingAdapter中有注解到,比如ViewBindingAdapter
,TextViewBindingAdapter
,CardViewBindingAdapter
等。总得来说,只有View的属性有对应的set方法,无论是否有对应的android属性,我们都可以用来进行数据绑定。比如ImageView的setImageBitmap
没有对应的imageBitmap
属性,但是可以在xml中使用imageBitmap
,例如
1 | <layout> |
但是当我们的View的属性没有提供这种set方法,而我们又需要这个属性与Data进行绑定,或者是我们需要给这个View额外增加一个属性去绑定数据,那么我们就可以使用 @BindingAdapter
,它注解在静态方法上,作用是类似给View属性和他的对应方法做一个映射,或者是类似给View增加一个额外的属性和方法。例如LinearLayout的android:layout_marginTop
属性,他没有对应的set方法,我们就可以通过下面的例子进行映射,也可以为另一个View增加属性进行数据绑定。
1 | @JvmStatic |
当然我们也可以给一个View一次设置多个额外的属性去进行数据绑定,比如
1 | @JvmStatic |
其中requireAll=false
表示不需要全部属性都赋值,默认为true,使用吐下
1 | <androidx.appcompat.widget.AppCompatImageView |
BindingMethods
@BindingMethods
注解是包含了一个或者是多个@BindingMethod
组件的注解,他的作用常见就是把View的属性和方法绑定在一起,当然我们也可以使用上面的了BindingAdapter,但是需要新增方法的形式。假如说我们的View存在一个属性(没有提供set方法),在数据与他绑定的时候,就需要调用View的某个方法,这时候就可以使用到@BindingMethods
,例如ViewBindingAdapter
的
1 | @BindingMethods({ |
总的来说他是直接建立起View属性和方法的映射而不需要额外的代码。@BindingMethods的声明是在何处都可以的,不一定要在BindingAdapter
里面,也可以在对应的View上面(假如自定义的话)。
BindingConversion
@BindingConversion
注解,他是在数据层的数据不符合的时候,会自动去查询被@BindingConversion
注解的静态方法,是否有可以把当前类型转换为目标类型的参数,如果存在则编译通过可以执行,否则不可以(需要注意的是TextView的text如果想要使用Int值是不可以的,因为他会被识别为resource id,BindingConversion无法起效)。比如
1 | @BindingConversion |
Inverse系列
包括@InverseMethod
,@InverseBindingMethods
,InverseBindingMethod
,InverseBindingAdapter
。
他们一般用于双向绑定之中而不是单向的数据绑定,下面逐一举例说明。
InverseMethod
@InverseMethod
,当我们的ViewModel提供的数据类型与View需要的数据类型不匹配,而且他们是双向绑定的情况的话,我们就可以使用 @InverseMethod
。例如
1 |
|
InverseBindingAdapter
首先是他的参数
attribute
,属性值,是必填的
2.event
,非必填,属性值+AttrChanged
后缀,比如假如attribute=text
,那么event=textAttrChanged
他作用于方法,也是需要静态方法,并且需要结合@BindingAdapter
使用。也是需要用在双向绑定中,双向绑定的是BindingAdapter
的对应的属性,BindingAdapter
是ViewModel到View,InverseBindingAdapter
则是相反的,比如库给的例子
1 | // 监听text属性改变的回调,这里使用到textAttrChanged,则知道是下面InverseBindingAdapter对应的 |
InverseBindingMethods
他的参数是InverseBindingMethod
,可以有多个值。他必须与BindingAdapter
一起使用,他的作用与BindingMethods
的类型,InverseBindingMethods
是把自身的属性值与对应的get
方法映射出来,他是使用在双向绑定当中的。ViewMode通过BindingAdapter
设置值到View中,View的属性改变了ViewModel通过InverseBindingMethods
注解的属性对应的get方法获取。InverseBindingMethod
的参数
type
,必填的,是那个View类attribute
,必填,是那个属性,method
,选填,他是属性值的getter形式作为默认值通过属性指定变化监听和返回方法。假如存在attribute
对应的get方法则可以不填,否则是手动填写具体对应的View的那个方法event
,选填,他是属性值+AttrChanged
,类似InverseBindingAdapter的event
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44@InverseBindingMethods({
@InverseBindingMethod(type = AdapterView.class, attribute = "android:selectedItemPosition"),
@InverseBindingMethod(type = AdapterView.class, attribute = "android:selection",
method = "getSelectedItemPosition",
event = "android:selectedItemPositionAttrChanged"),
})
public class AdapterViewBindingAdapter {
@BindingAdapter("android:selectedItemPosition")
public static void setSelectedItemPosition(AdapterView view, int position) {
if (view.getSelectedItemPosition() != position) {
view.setSelection(position);
}
}
@BindingAdapter("android:selection")
public static void setSelection(AdapterView view, int position) {
setSelectedItemPosition(view, position);
}
@BindingAdapter({"android:selectedItemPosition", "android:adapter"})
public static void setSelectedItemPosition(AdapterView view, int position, Adapter adapter) {
if (adapter != view.getAdapter()) {
//noinspection unchecked
view.setAdapter(adapter);
view.setSelection(position);
} else if (view.getSelectedItemPosition() != position) {
view.setSelection(position);
}
}
@BindingAdapter({"android:selection", "android:adapter"})
public static void setSelection(AdapterView view, int position, Adapter adapter) {
setSelectedItemPosition(view, position, adapter);
}
@BindingAdapter(value = {"android:onItemSelected", "android:onNothingSelected",
"android:selectedItemPositionAttrChanged" }, requireAll = false)
public static void setOnItemSelectedListener(AdapterView view, final OnItemSelected selected,
final OnNothingSelected nothingSelected, final InverseBindingListener attrChanged) {
if (selected == null && nothingSelected == null && attrChanged == null) {
view.setOnItemSelectedListener(null);
} else {
view.setOnItemSelectedListener(
new OnItemSelectedComponentListener(selected, nothingSelected, attrChanged));
}
}
}
其他注解
Bindable
是用于生成BR
类的,BindingBuildInfo
和Untaggable
是用于生成DataBinding自动生成的Java类使用的。