[关闭]
@zongwu 2017-01-12T07:46:53.000000Z 字数 7757 阅读 422

Databinding

技术交流


android.databinding

@Bindable

The Bindable annotation should be applied to any getter accessor method of an Observable class.

@Bindable用于修饰方法变量

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

继承自BaseObservable的数据类仍需手动的通知监听者们数据变更。因而,你可以在setter方法中通知变更,在getter方法中标记注解@Bindable

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

  1. private static class User extends BaseObservable {
  2. private String firstName;
  3. @Bindable
  4. public String getFirstName() {
  5. return this.firstName;
  6. }
  7. public void setFirstName(String firstName) {
  8. this.firstName = firstName;
  9. notifyPropertyChanged(BR.firstName);
  10. }
  11. }

@BindingAdapter

BindingAdapter is applied to methods that are used to manipulate how values with expressions are set to views.

@BindingAdapter用于修饰方法

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

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

  1. public class CardViewBindingAdapter {
  2. @BindingAdapter("contentPadding")
  3. public static void setContentPadding(CardView view, int padding) {
  4. view.setContentPadding(padding, padding, padding, padding);
  5. }
  6. }

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

  1. @BindingAdapter("contentPadding")

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

  1. @BindingAdapter("android:contentPadding")

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

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

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

  1. public interface ILogAction{
  2. void login();
  3. void logout();
  4. }

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

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

@BindingBuildInfo

  1. @BindingBuildInfo(
  2. buildId="3fefc6ba-1e95-4dcf-8ffa-278fe0f449bd",
  3. modulePackage="com.ipudong.library",
  4. sdkRoot="/Users/robert/Library/Android/sdk",
  5. layoutInfoDir="/Users/robert/android/develops/pudong-d-android/lib_basic/build/intermediates/data-binding-info/debug",
  6. exportClassListTo="/Users/robert/android/develops/pudong-d-android/lib_basic/build/intermediates/data-binding-info/debug/_generated.txt",
  7. isLibrary=true,
  8. minSdk=14,
  9. enableDebugLogs=false,
  10. printEncodedError=true
  11. )
  12. 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

  1. @BindingConversion
  2. public static Drawable convertColorToDrawable(int drawable) {
  3. return new ColorDrawable(drawable);
  4. }

@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

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

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

@InverseBindingAdapter

InverseBindingAdapter is associated with a method used to retrieve the value for a View when setting values gathered from the View.

InverseBindingAdapter is associated with a method used to retrieve the value for a View when setting values gathered from the View. This is similar to BindingAdapters:

  1. @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
  2. public static String captureTextValue(TextView view, CharSequence originalValue) {
  3. CharSequence newValue = view.getText();
  4. CharSequence oldValue = value.get();
  5. if (oldValue == null) {
  6. value.set(newValue);
  7. } else if (!contentEquals(newValue, oldValue)) {
  8. value.set(newValue);
  9. }
  10. }

he default value for event is the attribute name suffixed with "AttrChanged". In the above example, the default value would have been android:textAttrChanged even if it wasn't provided.

The event attribute is used to notify the data binding system that the value has changed. The developer will typically create a BindingAdapter to assign the event. For example:

  1. @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
  2. "android:afterTextChanged", "android:textAttrChanged"},
  3. requireAll = false)
  4. public static void setTextWatcher(TextView view, final BeforeTextChanged before,
  5. final OnTextChanged on, final AfterTextChanged after,
  6. final InverseBindingListener textAttrChanged) {
  7. TextWatcher newValue = new TextWatcher() {
  8. ...
  9. @Override
  10. public void onTextChanged(CharSequence s, int start, int before, int count) {
  11. if (on != null) {
  12. on.onTextChanged(s, start, before, count);
  13. }
  14. if (textAttrChanged != null) {
  15. textAttrChanged.onChange();
  16. }
  17. }
  18. }
  19. TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
  20. if (oldValue != null) {
  21. view.removeTextChangedListener(oldValue);
  22. }
  23. view.addTextChangedListener(newValue);
  24. }

Like BindingAdapters, InverseBindingAdapter methods may also take DataBindingComponent as the first parameter and may be an instance method with the instance retrieved from the DataBindingComponent.

InverseBindingListener is useful. See InverseBindingListener

@InverseBindingMethod

InverseBindingMethod is used to identify how to listen for changes to a View property and which getter method to call.

InverseBindingMethod is used to identify how to listen for changes to a View property and which getter method to call. InverseBindingMethod should be associated with any class as part of InverseBindingMethods.

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

method is optional. If it isn't provided, the attribute name is used to find the method name, either prefixing with "is" or "get". For the attribute android:text, data binding will search for a public CharSequence getText() method on TextView.

event is optional. If it isn't provided, the event name is assigned the attribute name suffixed with AttrChanged. For the android:text attribute, the default event name would be android:textAttrChanged. The event should be set using a BindingAdapter. For example:

  1. @BindingAdapter(value = {"android:beforeTextChanged", "android:onTextChanged",
  2. "android:afterTextChanged", "android:textAttrChanged"},
  3. requireAll = false)
  4. public static void setTextWatcher(TextView view, final BeforeTextChanged before,
  5. final OnTextChanged on, final AfterTextChanged after,
  6. final InverseBindingListener textAttrChanged) {
  7. TextWatcher newValue = new TextWatcher() {
  8. ...
  9. @Override
  10. public void onTextChanged(CharSequence s, int start, int before, int count) {
  11. if (on != null) {
  12. on.onTextChanged(s, start, before, count);
  13. }
  14. if (textAttrChanged != null) {
  15. textAttrChanged.onChange();
  16. }
  17. }
  18. }
  19. TextWatcher oldValue = ListenerUtil.trackListener(view, newValue, R.id.textWatcher);
  20. if (oldValue != null) {
  21. view.removeTextChangedListener(oldValue);
  22. }
  23. view.addTextChangedListener(newValue);
  24. }

@InverseBindingMethods

Used to enumerate attribute, getter, and event association.

@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都会扫描一遍现有的资源,生成不包含的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

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注