Data Binding 系列(六)綁定適配器

綁定適配器的做用就是,調用 UI 框架層合適的方法,給 view 屬性進行賦值。android

好比,調用 setText() 方法給 TextViewtext 屬性賦值;調用 setOnClickListener() 方法,給 viewonClick 屬性賦值。app

綁定適配器,可讓你決定給 view 屬性賦值調用哪一個方法,還可讓你本身定製該方法的具體邏輯。框架

view 屬性賦值

當數據發生變化時,綁定類必須調用 view 的合適的 set() 方法,給 view 屬性賦值。這時候有三種選擇:一種是讓 Data Binding 庫本身決定調用哪一個方法;一種是明確指定調用哪一個方法;一種是本身定義一個方法。ui

  • Data Binding 庫自動選擇

好比 view 有一個屬性叫作 exampleData Binding 庫會試圖去找一個 setExample(args) 的方法,這個方法接收的參數類型須要和綁定表達式的返回類型一致。注意,屬性命名空間是不考慮的,尋找方法的惟一標準就是,方法名和參數類型。url

好比,對於表達式 android:text="@{user.name}"Data Binding 庫會尋找該 viewsetText(args) 方法,若是 user.name 的類型是 String ,則尋找 setText(String args) 方法;若是 user.name 的類型是 Int ,則尋找 setText(Int args) 方法。spa

即便一個 viewandroid 命名空間沒有對應的屬性,Data Binding 庫依然能夠正常工做。好比,DrawerLayout 並無 android:scrimColorandroid:drawerListener 這些屬性,卻有 setScrimColor(int)setDrawerListener(DrawerListener) 方法,這時咱們就可使用自定義命名空間的屬性 app:scrimColorapp:drawerListenercode

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}">
複製代碼
  • 明確指定要調用的方法

有時候 view 的屬性名和 set() 方法名並不匹配。好比 ImageView 有一個 android:tint 屬性,對應的方法名倒是 setImageTintList(ColorStateList) ,這時咱們可使用 BindingMethods 註解,BindingMethods 用於註解一個類,它能夠包含多個 BindingMethod 註解,BindingMethod 註解就聲明瞭 view 屬性要匹配哪一個 set() 方法:rem

@BindingMethods(value = [
    BindingMethod(
        type = android.widget.ImageView::class,
        attribute = "android:tint",
        method = "setImageTintList")])
複製代碼

上面示例代表了:ImageViewandroid:tint 屬性,須要調用 setImageTintList() 方法。get

  • 本身定義一個方法

有時候 view 屬性須要自定義綁定邏輯。好比 view 屬性有 android:paddingLeft 屬性卻沒有 setPaddingLeft(int) 方法,可是有 setPadding(int left, int top, int right, int bottom) 方法,這時候咱們須要自定義邏輯。這時,咱們使用 BindingAdapter 註解一個 static 方法,以下所示:string

@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, padding: Int) {
    view.setPadding(padding,
                view.getPaddingTop(),
                view.getPaddingRight(),
                view.getPaddingBottom())
}
複製代碼

上面的示例代表了:Viewandroid:paddingLeft 的屬性,會調用 viewsetPadding(int left, int top, int right, int bottom)

注意,該方法的參數很重要,第一個參數指明瞭 view 的類型,第二個參數指明瞭綁定表達式的返回值是 Int 類型。

若是自定義的適配器和系統的適配器有衝突,自定義的適配器會覆蓋系統的。

BindingAdapter 註解能夠接收多個參數,以下所示:

@BindingAdapter("imageUrl", "error")
fun loadImage(view: ImageView, url: String, error: Drawable) {
    Picasso.get().load(url).error(error).into(view)
}
複製代碼
<ImageView app:imageUrl="@{venue.imageUrl}" app:error="@{@drawable/venueError}" />
複製代碼

上述示例代表了:view 類型是 ImageView ,有兩個屬性 imageUrlerror ,這兩個屬性對應綁定表達式的返回類型分別是 StringDrawable

注意,只有當 ImageView 同時有 imageUrlerror 兩個屬性時,上述方法纔會被調用。若是你但願 ImageView 有任一屬性均可以調用,能夠聲明 requireAll = false

@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false)
fun setImageUrl(imageView: ImageView, url: String, placeHolder: Drawable) {
    if (url == null) {
        imageView.setImageDrawable(placeholder);
    } else {
        MyImageLoader.loadInto(imageView, url, placeholder);
    }
}
複製代碼

有時BindingAdapter 可能須要屬性的舊值,能夠這麼使用:

@BindingAdapter("android:paddingLeft")
fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
    if (oldPadding != newPadding) {
        view.setPadding(padding,
                    view.getPaddingTop(),
                    view.getPaddingRight(),
                    view.getPaddingBottom())
    }
}
複製代碼
@BindingAdapter("android:onLayoutChange")
fun setOnLayoutChangeListener(
        view: View,
        oldValue: View.OnLayoutChangeListener?,
        newValue: View.OnLayoutChangeListener?
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        if (oldValue != null) {
            view.removeOnLayoutChangeListener(oldValue)
        }
        if (newValue != null) {
            view.addOnLayoutChangeListener(newValue)
        }
    }
}
複製代碼

上述示例中,第二個參數是舊值,第三個參數是新值。

數據類型轉換

  • 自動轉換

在綁定表達式中,表達式的返回值類型須要與對應的 set() 方法接收的參數類型一致,若是不一致咱們須要在綁定表達式中進行轉換,例如 android:text=@{user.age}user.age 返回值類型是 IntsetText(String text) 接收的參數類型是 String ,此時咱們就須要進行轉換:

android:text=@{String.valueOf(user.age)}
複製代碼

上述示例中:調用了 String.valueOf() 方法,把 Int 轉換爲了 String

  • 自定義轉換

有些場景是固定地在兩種類型之間進行轉換,這時候可使用自定義轉換。好比 viewandroid:background 屬性接收 Drawable 類型的數據,可是在綁定表達式中使用的倒是 Int 類型,以下所示:

<View
   android:background="@{isError ? @color/red : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
複製代碼

這時候,Int 須要轉換爲 Drawable ,咱們可使用 BindingConversion 註解一個靜態方法,以下所示:

@BindingConversion
fun convertColorToDrawable(color: Int) = ColorDrawable(color)
複製代碼

注意,綁定表達式中的返回值類型必須是固定的,以下所示是錯誤的:

<View
   android:background="@{isError ? @drawable/error : @color/white}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
複製代碼

上面示例中,綁定表達式的返回值類型是不肯定的,多是 Drawable 或者 Int ,這種使用方法是不行的。

相關文章
相關標籤/搜索