1. 默認setter
對於設置了DataBinding表達式的XML屬性,DataBinding會根據屬性表達式的返回值查找該屬性的setter方法,比如android:text="@{"str"}"
屬性是去找setText(String)方法,所以表達式的返回值類型是十分重要的,影響着DataBinding查找的具體方法。
如果View不提供某個屬性的XML屬性,但是其實包含了該屬性的setter,這時候可以直接使用屬性名,DataBinding會自動幫我們調用它的setter,比如DrawerLayout的xml沒有scrimColor和drawerListener屬性,但是存在其setter方法,則可以如下聲明:
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
2. 重命名setter
如果屬性名存在且有設置該屬性的方法,但是方法名不符合傳統setter規範,例如tint屬性存在setImageTintList()
方法,則可以通過@BindingMethods註解來綁定屬性名及其設置方法:
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
3. 自定義setter
假如不存在剛好設置該屬性的方法,可以通過@BindingAdapter註解自定義屬性所要調用的靜態方法,例如在XML中自定leftPadding屬性,命名空間在這裏是忽略掉的,Activity中添加以下代碼:
@BindingAdapter("android:leftPadding")
public static void setLeftPadding(View view, int leftPadding){
view.setPadding(leftPadding,view.getPaddingTop(),view.getPaddingRight(),view.getPaddingBottom());
}
通過這種方式可以把在XML不方便進行的操作轉移到Java代碼中,或者複用Java代碼中已有的方法。BindingAdapter方法可以放置到Activity當中或者另外定義一個類用來存放BindingAdapter方法。
BindingAdapter方法可以接收多個參數,比如
@BindingAdapter({"bind:imageUrl", "bind:error"})
public static void loadImage(ImageView view, String url, Drawable error) {
Picasso.with(view.getContext()).load(url).error(error).into(view);
}
<!-- XML -->
<ImageView app:imageUrl="@{venue.imageUrl}"
app:error="@{@drawable/venueError}"/>
但是隻有當ImageView中使用了imageUrl和error屬性,並且屬性類型與方法類型相對應纔會調用定義的BindingAdapter方法。
3.1 獲取原值
BindingAdapter的方法可以獲取到原先的值,此時方法的聲明應該是列出所有的屬性原值,然後纔是新的值:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int oldPadding, int newPadding) {
if (oldPadding != newPadding) {
view.setPadding(newPadding, view.getPaddingTop(), view.getPaddingRight(), view.getPaddingBottom());
}
}
3.2 事件自定義處理
事件處理只能用在帶有單個方法的接口或者抽象類上,比如說:
@BindingAdapter("android:onLayoutChange")
public static void setOnLayoutChangeListener(View view, View.OnLayoutChangeListener oldValue,
View.OnLayoutChangeListener newValue) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if (oldValue != null) {
view.removeOnLayoutChangeListener(oldValue);
}
if (newValue != null) {
view.addOnLayoutChangeListener(newValue);
}
}
}
如果一個監聽有多個方法,此時必須要分成多個監聽才行。比如 View.OnAttachStateChangeListener有兩個回調方法,onViewAttachedToWindow()和onViewDetachedFromWindow(),那我們就必須手動創建兩個接口來區分開:
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
void onViewAttachedToWindow(View v);
}
因爲改變一個監聽器會影響到另外一個,所以必須要有三個Binding Adapter,兩個是單獨一個方法回調的,還有一個是所有方法回調的。
@BindingAdapter("android:onViewAttachedToWindow")
public static void setListener(View view, OnViewAttachedToWindow attached) {
setListener(view, null, attached);
}
@BindingAdapter("android:onViewDetachedFromWindow")
public static void setListener(View view, OnViewDetachedFromWindow detached) {
setListener(view, detached, null);
}
@BindingAdapter({"android:onViewDetachedFromWindow", "android:onViewAttachedToWindow"})
public static void setListener(View view, final OnViewDetachedFromWindow detach,
final OnViewAttachedToWindow attach) {
if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1) {
final OnAttachStateChangeListener newListener;
if (detach == null && attach == null) {
newListener = null;
} else {
newListener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
if (attach != null) {
attach.onViewAttachedToWindow(v);
}
}
@Override
public void onViewDetachedFromWindow(View v) {
if (detach != null) {
detach.onViewDetachedFromWindow(v);
}
}
};
}
final OnAttachStateChangeListener oldListener = ListenerUtil.trackListener(view,
newListener, R.id.onAttachStateChangeListener);
if (oldListener != null) {
view.removeOnAttachStateChangeListener(oldListener);
}
if (newListener != null) {
view.addOnAttachStateChangeListener(newListener);
}
}
}
上述的例子比普通的更復雜一點,因爲View使用add和remove來操作監聽器,而不是單個的set方法。android.databinding.adapters.ListenerUtil這個類幫忙記錄設置的監聽器,所以我們纔有辦法去移除掉。
上述代碼的@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
註釋表明從該api開始纔會生成監聽器,這個是根據添加監聽器方法addOnAttachStateChangeListener(View.OnAttachStateChangeListener)
所要求的。