文章有点长,请做好心理准备
有哪些注解
- @Bindable
- @BindingAdapter
- @BindingBuildInfo
- @BindingConversion
- @BindingMethod
- @BindingMethods
- @InverseBindingAdapter
- @InverseBindingMethod
- @InverseBindingMethods
- @InverseMethod
- @Untaggable
以上就是DataBinding中所有的注解,一共11个注解,其中@BindingBuildInfo与@Untaggable这两个注解是hide的,除了这两个之外,其他9个注解在我们日常开发中都有可能用到,但是最常用的只有如下2个注解:
- @Bindable
- @BindingAdapter
本文会讲解所有的注解,而@BindingAdapter注解的使用又相对较难,所以会着重讲解,其他注解也都会有不同深度的讲解。
1. @BindingBuildInfo
作用:在dataBinding生成相关代码时,该注解用来生成相关的databinding信息,信息中包含一个buildId字段。
在源码中也能找到BindingBuildInfo类的身影:
/**
* 路径:android.databinding.tool.LayoutXmlProcessor
*/
public void writeEmptyInfoClass() {
Class annotation = BindingBuildInfo.class;
String classString = "package android.databinding.layouts;\n\nimport " + annotation.getCanonicalName() + ";\n\n@" + annotation.getSimpleName() + "(buildId=\"" + this.mBuildId + "\")\npublic class " + "DataBindingInfo" + " {}\n";
this.mFileWriter.writeToFile("android.databinding.layouts.DataBindingInfo", classString);
}
2.@Untaggable
作用:在dataBinding中,通过为view设置tag的方式来标记一个view,然后dataBinding生成代码时,会根据这些tag找到对应的view,然后设置相关的属性。@Untaggable注解有一个String数组,用来存储相关的viewType,在生成相关代码时,dataBinding会从该数组中查找viewType,判断该view是否可设置tag。
难道还有DataBinding不支持的操作?那必须的,在fragment标签中就不支持dataBinding相关的开发方式,一用就会抛异常的,自然不能给fragment这种viewType设置tag。
3.@BindingConversion
- 作用于方法
- 被该注解标记的方法,被视为dataBinding的转换方法。
- 方法必须为公共静态(public static)方法,且有且只能有1个参数
问:那到底什么属性要被转换?又要把属性转换成什么呢?
答:举个例子,比如View的android:background=""
属性,属性值为drawable类型的对象,如果你要放置一个@color/black
的属性值,在不使用dataBinding的情况下没有任何问题,但是如果你要使用了dataBinding方式开发,则会报错,原因在于android:background=""
的目标值类型为drawable,而databinding会把@color/black
先解析成int类型的颜色值,这时如果把int类型的颜色值直接赋值到目标为drawable类型的参数中去,那绝逼是要报错的,如果我们可以在int类型的颜色值赋值之前,让int类型的颜色值自动转换为colorDrawable对象,就可以解决这个问题,而@BindingConversion注解就是干这个事的。
我们先来看一个官网上的示例:
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
如果只有上面那段代码,那肯定会报错的,原因上面也说了,所以我们需要定义一个转换方法,把整型的颜色值转换为drawable对象:
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
这时再运行就不会出错了,因为在int型颜色值赋值之前已经被自动转换为ColorDrawable了。
肯定有人会有疑问:
- 为什么我没有定义convertColorToDrawable这样的方法,直接写完布局文件就运行也没有报错呢?
- convertColorToDrawable这个方法是什么时机被调用的呢?
首先之所以没有报错的原因,其实是因为convertColorToDrawable这个方法在dataBinding的jar包中已经帮我们定义好了,我们不用再定义了,所以不会报错。源码路径为:android.databinding.adapters.Converters
。
下面再说说调用时机:Android中的每个xml中的属性其实都对应着相应的java方法的,如果在xml中设置的属性值的类型与对应的Java方法的参数类型不符,这时 dataBinding就会去寻找可以让属性值转换为正确类型的方法,而寻找的根据就是所有被@BindingConversion注解标记的方法,这时convertColorToDrawable方法就会被调用了。
如果我们自己定义了一个功能相同的convertColorToDrawable方法,那么dataBinding会优先使用我们自己定义的方法。
如果我们自己定义了多个功能相同的convertColorToDrawable方法,比如convertColorToDrawable01,convertColorToDrawable02,convertColorToDrawable03,dataBinding会选择顺序靠后的方法去使用,说起来很抽象,举个例子:
public class CustomConversion {
@BindingConversion
public static ColorDrawable convertColorDrawable01(int color) {
return new ColorDrawable(color);
}
@BindingConversion
public static ColorDrawable convertColorToDrawable02(int color) {
return new ColorDrawable(color);
}
@BindingConversion
public static ColorDrawable convertColorToDrawable03(int color) {
return new ColorDrawable(color);
}
}
如上代码所示,convertColorToDrawable03方法排在最后面,所以dataBinding会选择convertColorToDrawable03去使用。
但是个人强烈建议不要定义功能重复的方法,你这不是闲的蛋疼吗
但是话说回来,如果我们真的需要定义多个参数类型相同,返回值类型也相同但是方法体中的实现却各不相同的方法,我们希望dataBinding能分辨出方法之间的不同,但是很不幸的是,至少目前来说,dataBinding是看不出这些方法的区别的,所以还是会选择顺序靠后的那个方法,即便我们真正想让它调用的是其他方法,所以这也算是DataBinding的一个不足之处吧。
4.@BindingMethod与@BindingMethods
- @BindingMethods注解一般用于标记类
- @BindingMethod注解需要与@BindingMethods注解结合使用才能发挥其功效
- 用法极其简单,但是使用场景很少(因为大多数场景,dataBinding已经帮我们做好了)
问:@BindingMethod与@BindingMethods存在的意义?
答:为了说明@BindingMethod与@BindingMethods存在的意义,我强行写了一个例子:
...
<import type="android.view.Gravity"/>
<TextView
android:layout_width="match_parent"
android:layout_height="55dp"
android:gravity="@{Gravity.CENTER}"/>
...
在android:gravity="@{Gravity.CENTER}"
属性中我使用了dataBinding表达式设置文本对齐方式,在编译时,dataBinding会根据属性名gravity
到TextView源码中寻找对应的setGravity()
方法,然后把我们的Gravity.CENTER
值设置进去。生成的代码如下:
this