DataBinding(四)屬性設置

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)所要求的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章