DataBinding·常用注解说明

android.databinding

@Bindable

Observable接口提供给开发者添加/移除监听者的机制。为了使开发更便捷,我们创建了BaseObservable类,它已经实现了Observable接口中的注册监听者的机制。

继承自BaseObservable的数据类,仍需手动的通知监听者们数据已发生变更。你可以在setter方法中发出变更消息,记住同时在getter方法上标记注解@Bindable

@Bindable 注解的推荐用法 是修饰继承自Observable类中的getter accessor方法,但其实getter accessor的属性也是可以应用该注解的。

注解@Bindable在编译期间生成一个BR类,以此持有对应的实例,作用同R类。

private static class User extends BaseObservable {
   private String firstName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
}

@BindingAdapter

应用于用于操作表达式的值如何设置为视图的方法。

@BindingAdapter用于修饰方法

一些属性需要定制绑定逻辑,一个用@BindingAdapter修饰的静态方法可以自定义属性的setter操作。

android自身实现了大量的Adapter,你可以在项目moduleandroid.databinding.adapters包下找到这些代码。

public class CardViewBindingAdapter {
    @BindingAdapter("contentPadding")
    public static void setContentPadding(CardView view, int padding) {
        view.setContentPadding(padding, padding, padding, padding);
    }
}

1、默认的你的自定义的命名空间,在匹配时会被忽略。

@BindingAdapter("contentPadding")

2、允许重写android的命名空间。

@BindingAdapter("android:contentPadding")

app:contentPaddingandroid:contentPadding处理行为可以不一样。
app:contentPaddingcustom:contentPadding处理行为是一致的。(仅android是特殊的命名空间)。

需要注意,当你创建的适配器属性与系统默认的产生冲突时,你的自定义适配器将会覆盖掉系统原先定义的注解,这将会产生一些意外的问题。

假设需要对下面接口,做适配。

public interface ILogAction{
      void login();
      void logout();
}

则需要一个方法一个接口,这么做的原因是避免login()的修改影响到logout()。所以根据业务需要,可能需要排列组合适配这两个接口。

1、适配 login
2、适配 logout
3、适配 login + logout

@BindingBuildInfo

@BindingBuildInfo(
buildId="3fefc6ba-1e95-4dcf-8ffa-278fe0f449bd",
modulePackage="com.ipudong.library",
sdkRoot="/Users/robert/Library/Android/sdk",
layoutInfoDir="/Users/robert/android/develops/pudong-d-android/lib_basic/build/intermediates/data-binding-info/debug",
exportClassListTo="/Users/robert/android/develops/pudong-d-android/lib_basic/build/intermediates/data-binding-info/debug/_generated.txt",
isLibrary=true,
minSdk=14,
enableDebugLogs=false,
printEncodedError=true
)
public class DataBindingInfo {}

SOURCE阶段会自动生成DataBindingInfo.class,并标记注解如上。

@BindingConversion

Annotate methods that are used to automatically convert from the expression type to the value used in the setter.

有时候会遇到类型不匹配的问题,比如R.color.whiteint,但是通过Data Binding赋值给android:background属性后,需要把int转换为ColorDrawable

@BindingConversion
public static Drawable convertColorToDrawable(int drawable) {
  return new ColorDrawable(drawable);
}

@BindingMethod && @BindingMethods

Used within an BindingMethods annotation to describe a renaming of an attribute to the setter used to set that attribute.
Used to enumerate attribute-to-setter renaming.

@BindingMethods用于修饰类。

一些属性虽然拥有setters但是并不与名字相匹配,这些方法的属性可以通过 @BindingMethod && @BindingMethods 注释 setters

@BindingMethods({
       @BindingMethod(type = "android.widget.ImageView",
                      attribute = "android:tint",
                      method = "setImageTintList"),
})

开发人员不太可能需要重命名 setters ,因为android框架属性已经实现了这一部分。

@InverseBindingAdapter

InverseBindingAdapter用于关联某个用于接收View变更的方法,典型的例子EditText.TextWatcher接收输入字符的变更。这与BindingAdapters有一定的相似性:

 @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
 public static String captureTextValue(TextView view, CharSequence originalValue) {
     CharSequence newValue = view.getText();
     CharSequence oldValue = value.get();
     if (oldValue == null) {
         value.set(newValue);
     } else if (!contentEquals(newValue, oldValue)) {
         value.set(newValue);
     }
 }

事件的默认值是带有AttrChanged的属性名称。在上面的例子中,默认值是android:textAttrChanged,即使它没有提供。

事件属性用于通知数据绑定系统值已更改。开发人员通常会创建一个BindingAdapter来分配事件。比如:

@BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
                          "android:afterTextChanged", "android:textAttrChanged"},
                          requireAll = false)
 public static void setTextWatcher(TextView view, final BeforeTextChanged before,
                                   final OnTextChanged on, final AfterTextChanged after,
                                   final InverseBindingListener textAttrChanged) {
     TextWatcher newValue = new TextWatcher() {
         ...
         @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {
             if (on != null) {
                 on.onTextChanged(s, start, before, count);
             }
             if (textAttrChanged != null) {
                 textAttrChanged.onChange();
             }
         }
     }
     TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
     if (oldValue != null) {
         view.removeTextChangedListener(oldValue);
     }
     view.addTextChangedListener(newValue);
 }
 

如同BindingAdapters一样, InverseBindingAdapter方法 也可以将 DataBindingComponent作为第一个参数,可以是具有从DataBindingComponent检索的实例的实例方法。

InverseBindingListener 非常有用。 参考 InverseBindingListener

@InverseBindingMethod

InverseBindingMethod用于标识如何监听对View属性的更改以及要调用的getter方法。 InverseBindingMethod 应该与InverseBindingMethods的部分方法相关联。

@InverseBindingMethods({@InverseBindingMethod(
     type = android.widget.TextView.class,
     attribute = "android:text",
     event = "android:textAttrChanged",
     method = "getText")})
 public class MyTextViewBindingAdapters { ... }

@InverseBindingMethods中的属性method 是可选的。

如果其没有提供, 属性名称会查找如下几种可能性:方法名称,前缀为is或者get的方法名称。 如属性android:text, 数据绑定框架会在TextView中搜索public CharSequence getText() 方法。

@InverseBindingMethods中的属性event是可选的。

如果其没有提供,默认会使用属性名+AttrChanged后缀。如属性android:text, 默认的事件名称android:textAttrChanged

这个事件也需要配置相关的@BindingAdapter,如下:

 @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
                          "android:afterTextChanged", "android:textAttrChanged"},
                          requireAll = false)
 public static void setTextWatcher(TextView view, final BeforeTextChanged before,
                                   final OnTextChanged on, final AfterTextChanged after,
                                   final InverseBindingListener textAttrChanged) {
     TextWatcher newValue = new TextWatcher() {
         ...
         @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {
             if (on != null) {
                 on.onTextChanged(s, start, before, count);
             }
             if (textAttrChanged != null) {
                 textAttrChanged.onChange();
             }
         }
     }
     TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
     if (oldValue != null) {
         view.removeTextChangedListener(oldValue);
     }
     view.addTextChangedListener(newValue);
 }
 

@InverseBindingMethods

用于枚举属性,getter和事件关联。

@Untaggable

Data Binding相关的jar包由四部分组成,

  1. baseLibrary-2.1.0-rc1.jar
    作为运行时类库被打进APK中;

  2. DataBinderPlugin(gradle plugin)
    在编译期使用,利用gradle-api(之前叫transform-api,1.5生,2.0改名)处理xml文件,生成DataBindingInfo.java;

  3. compiler-2.1.0-rc1.jar
    在编译器使用,入口类继承自AbstractProcessor,用于处理注解,并生成Binding类,DataBindingCompoent.java,DataBinderMapper.java类;

  4. compilerCommon-2.1.0-rc1.jar
    被DataBinderPlugin和compiler-2.1.0-rc1.jar所依赖

相关编译流程

STEP1 资源处理

aapt或者gradle执行时,都会触发资源处理。
在资源处理过程中,DataBinding都会扫描一遍现有的资源,生成不包含<layout>的data-binding-layout-out以及DataBinding所需要的data-binding-info;

STEP2 DataBindingInfo.class生成

在完成资源处理后,aapt或者gradle-api都会去执行DataBindingInfo.class生成操作,把相关的信息写入DataBindingInfo.class的@BindingBuildInfo注解中;

STEP3 监听到注解变化

生成@BindingBuildInfo注解,或者code中发现有新的注解写入,AbstractProcessor注解处理器就开始执行注解处理。
DataBinding中有一个ProcessDataBinding.java类专门来处理DataBinding相关的注解;

STEP4 ProcessDataBinding处理注解,生成bin

ProcessDataBinding中处理注解永远会按顺执行3步,ProcessMethodAdapter,ProcessExpressions,ProcessBindable。
每次执行都会从磁盘反序列化对应的bin文件,然后往bin中写入新的,完成后再序列化到磁盘;

STEP5 生成最终产物

执行ProcessMethodAdapter生成DataBindingComponents.class;
执行ProcessExpressions生成ViewDataBinding.class子类(ActivityDetail2Binding.class),并触发DataBindingMapper.class更新;
执行ProcessBindable生成BR.class,并触发DataBindingMapper.class更新;


参考链接:
http://www.jianshu.com/p/eb29c691d370
https://developer.android.com/topic/libraries/data-binding/index.html
https://developer.android.com/reference/android/databinding/InverseBindingAdapter.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容