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
,你可以在项目module
的android.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:contentPadding
与android:contentPadding
处理行为可以不一样。
app:contentPadding
与custom: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.white
是int
,但是通过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包由四部分组成,
baseLibrary-2.1.0-rc1.jar
作为运行时类库被打进APK中;DataBinderPlugin(gradle plugin)
在编译期使用,利用gradle-api(之前叫transform-api,1.5生,2.0改名)处理xml文件,生成DataBindingInfo.java;compiler-2.1.0-rc1.jar
在编译器使用,入口类继承自AbstractProcessor,用于处理注解,并生成Binding类,DataBindingCompoent.java,DataBinderMapper.java类;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