android.databinding
@Bindable
Observable接口提供給開發(fā)者添加/移除監(jiān)聽者的機(jī)制。為了使開發(fā)更便捷,我們創(chuàng)建了BaseObservable類,它已經(jīng)實(shí)現(xiàn)了Observable接口中的注冊監(jiān)聽者的機(jī)制。
繼承自BaseObservable的數(shù)據(jù)類,仍需手動(dòng)的通知監(jiān)聽者們數(shù)據(jù)已發(fā)生變更。你可以在setter方法中發(fā)出變更消息,記住同時(shí)在getter方法上標(biāo)記注解@Bindable。
@Bindable注解的推薦用法 是修飾繼承自Observable類中的getter accessor方法,但其實(shí)getter accessor的屬性也是可以應(yīng)用該注解的。
注解
@Bindable在編譯期間生成一個(gè)BR類,以此持有對應(yīng)的實(shí)例,作用同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
應(yīng)用于用于操作表達(dá)式的值如何設(shè)置為視圖的方法。
@BindingAdapter用于修飾方法。
一些屬性需要定制綁定邏輯,一個(gè)用@BindingAdapter修飾的靜態(tài)方法可以自定義屬性的setter操作。
android自身實(shí)現(xiàn)了大量的Adapter,你可以在項(xiàng)目module的android.databinding.adapters包下找到這些代碼。
public class CardViewBindingAdapter {
@BindingAdapter("contentPadding")
public static void setContentPadding(CardView view, int padding) {
view.setContentPadding(padding, padding, padding, padding);
}
}
1、默認(rèn)的你的自定義的命名空間,在匹配時(shí)會被忽略。
@BindingAdapter("contentPadding")
2、允許重寫android的命名空間。
@BindingAdapter("android:contentPadding")
app:contentPadding與android:contentPadding處理行為可以不一樣。
app:contentPadding與custom:contentPadding處理行為是一致的。(僅android是特殊的命名空間)。
需要注意,當(dāng)你創(chuàng)建的適配器屬性與系統(tǒng)默認(rèn)的產(chǎn)生沖突時(shí),你的自定義適配器將會覆蓋掉系統(tǒng)原先定義的注解,這將會產(chǎn)生一些意外的問題。
假設(shè)需要對下面接口,做適配。
public interface ILogAction{
void login();
void logout();
}
則需要一個(gè)方法一個(gè)接口,這么做的原因是避免login()的修改影響到logout()。所以根據(jù)業(yè)務(wù)需要,可能需要排列組合適配這兩個(gè)接口。
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階段會自動(dòng)生成DataBindingInfo.class,并標(biāo)記注解如上。
@BindingConversion
Annotate methods that are used to automatically convert from the expression type to the value used in the setter.
有時(shí)候會遇到類型不匹配的問題,比如R.color.white是int,但是通過Data Binding賦值給android:background屬性后,需要把int轉(zhuǎn)換為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"),
})
開發(fā)人員不太可能需要重命名 setters ,因?yàn)閍ndroid框架屬性已經(jīng)實(shí)現(xiàn)了這一部分。
@InverseBindingAdapter
InverseBindingAdapter用于關(guān)聯(lián)某個(gè)用于接收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);
}
}
事件的默認(rèn)值是帶有AttrChanged的屬性名稱。在上面的例子中,默認(rèn)值是android:textAttrChanged,即使它沒有提供。
事件屬性用于通知數(shù)據(jù)綁定系統(tǒng)值已更改。開發(fā)人員通常會創(chuàng)建一個(gè)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作為第一個(gè)參數(shù),可以是具有從DataBindingComponent檢索的實(shí)例的實(shí)例方法。
InverseBindingListener 非常有用。 參考 InverseBindingListener
@InverseBindingMethod
InverseBindingMethod用于標(biāo)識如何監(jiān)聽對View屬性的更改以及要調(diào)用的getter方法。 InverseBindingMethod 應(yīng)該與InverseBindingMethods的部分方法相關(guān)聯(lián)。
@InverseBindingMethods({@InverseBindingMethod(
type = android.widget.TextView.class,
attribute = "android:text",
event = "android:textAttrChanged",
method = "getText")})
public class MyTextViewBindingAdapters { ... }
@InverseBindingMethods中的屬性method 是可選的。
如果其沒有提供, 屬性名稱會查找如下幾種可能性:方法名稱,前綴為
is或者get的方法名稱。 如屬性android:text, 數(shù)據(jù)綁定框架會在TextView中搜索public CharSequence getText()方法。
@InverseBindingMethods中的屬性event是可選的。
如果其沒有提供,默認(rèn)會使用
屬性名+AttrChanged后綴。如屬性android:text, 默認(rèn)的事件名稱android:textAttrChanged。
這個(gè)事件也需要配置相關(guān)的@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和事件關(guān)聯(lián)。
@Untaggable
Data Binding相關(guān)的jar包由四部分組成,
baseLibrary-2.1.0-rc1.jar
作為運(yùn)行時(shí)類庫被打進(jìn)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所依賴
相關(guān)編譯流程
STEP1 資源處理
aapt或者gradle執(zhí)行時(shí),都會觸發(fā)資源處理。
在資源處理過程中,DataBinding都會掃描一遍現(xiàn)有的資源,生成不包含<layout>的data-binding-layout-out以及DataBinding所需要的data-binding-info;
STEP2 DataBindingInfo.class生成
在完成資源處理后,aapt或者gradle-api都會去執(zhí)行DataBindingInfo.class生成操作,把相關(guān)的信息寫入DataBindingInfo.class的@BindingBuildInfo注解中;
STEP3 監(jiān)聽到注解變化
生成@BindingBuildInfo注解,或者code中發(fā)現(xiàn)有新的注解寫入,AbstractProcessor注解處理器就開始執(zhí)行注解處理。
DataBinding中有一個(gè)ProcessDataBinding.java類專門來處理DataBinding相關(guān)的注解;
STEP4 ProcessDataBinding處理注解,生成bin
ProcessDataBinding中處理注解永遠(yuǎn)會按順執(zhí)行3步,ProcessMethodAdapter,ProcessExpressions,ProcessBindable。
每次執(zhí)行都會從磁盤反序列化對應(yīng)的bin文件,然后往bin中寫入新的,完成后再序列化到磁盤;
STEP5 生成最終產(chǎn)物
執(zhí)行ProcessMethodAdapter生成DataBindingComponents.class;
執(zhí)行ProcessExpressions生成ViewDataBinding.class子類(ActivityDetail2Binding.class),并觸發(fā)DataBindingMapper.class更新;
執(zhí)行ProcessBindable生成BR.class,并觸發(fā)DataBindingMapper.class更新;
參考鏈接:
http://www.itdecent.cn/p/eb29c691d370
https://developer.android.com/topic/libraries/data-binding/index.html
https://developer.android.com/reference/android/databinding/InverseBindingAdapter.html