天天看點

Data Binding(資料綁定)使用者指南

1)介紹

這篇文章介紹了如何使用Data Binding庫來寫聲明的layouts檔案,并且用最少的代碼來綁定你的app邏輯和layouts檔案。

Data Binding庫不僅靈活而且廣泛相容- 它是一個support庫,是以你可以在所有的Android平台最低能到Android 2.1(API等級7+)上使用它。

需求:Android Studio 1.3.0-beta1 或 更高版本。

a)測試版本
請注意:Data Binding庫目前是測試版本。在Data Binding處于測試階段時,開發者應該了解以下注意事項:
  • 這是旨在生成開發者的回報功能的測試版本。它可能包含bugs,或者不适合你的使用案例,是以需要您在使用它時自擔風險。即便如此,我們非常希望得到您的回報!使用issue tracker來讓我們知道對于你的使用案例什麼可以工作或者不能工作。
  • Data Binding庫的測試版本傾向于顯著的改變,包括那些不是與您的應用程式相容的源代碼。即,在未來可能會進行大量的返工來更新此庫。
  • 雖然伴有标準Android SDK和Google Play服務條款适用警告,開發人員可以随時釋出内置了與Data Binding庫beta版本的應用程式。而且采用新的庫或工具是一個相當好的主意來徹底測試你的應用程式。
  • 我們在這個時候才剛剛開始與Android Studio的支援。未來會有進一步的Android Studio的支援。
  • 通過使用Data Binding庫beta版本,你承認這些警告。

2)建構環境

要開始使用Data Binding,首先需要在Android SDK Manager的支援庫裡下載下傳該庫。

請確定您使用的是Android Studio的相容版本。Android Studio的Data Binding插件需要Android Studio 1.3.0-beta1 或 更高版本。

a)工作環境

你的app要使用Data Binding,需要添加Data Binding到gradle建構檔案裡,如下:

  1.    dependencies {
  2.        classpath "com.android.tools.build:gradle:1.2.3"
  3.        classpath "com.android.databinding:dataBinder:1.0-rc0"
  4.    }
  5. }

然後確定jcenter在repositories清單裡,如下:

  1. allprojects {
  2.    repositories {
  3.        jcenter()
  4.    }
  5. }

在每一個你想要使用Data Binding的module,添加如下的插件:

  1. apply plugin: ‘com.android.application'
  2. apply plugin: 'com.android.databinding'

Data Binding插件将會在你的項目内添加必需提供的以及編譯配置依賴。

3)Data Binding Layout檔案

a)Data Binding表達式

Data Binding layout檔案有點不同的是:起始根标簽是layout,接下來一個data元素以及一個view的根元素。這個view元素就是你沒有使用Data Binding的layout檔案的根元素。舉例說明如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  3.    <data>
  4.        <variable name="user" type="com.example.User"/>
  5.    </data>
  6.    <LinearLayout
  7.        android:orientation="vertical"
  8.        android:layout_width="match_parent"
  9.        android:layout_height="match_parent">
  10.        <TextView android:layout_width="wrap_content"
  11.            android:layout_height="wrap_content"
  12.            android:text="@{user.firstName}"/>
  13.        <TextView android:layout_width="wrap_content"
  14.            android:layout_height="wrap_content"
  15.            android:text="@{user.lastName}"/>
  16.    </LinearLayout>
  17. </layout>

在data内描述了一個名為user的變量屬性,使其可以在這個layout中使用:

  1. <variable name="user" type="com.example.User"/>

在layout的屬性表達式寫作

@{}

,下面是一個TextView的text設定為user的firstName屬性:

  1. <TextView android:layout_width="wrap_content"
  2.           android:layout_height="wrap_content"
  3.           android:text="@{user.firstName}"/>
b)Data對象

假設你有一個user的plain-old Java Object(POJO):

  1. public class User {
  2.    public final String firstName;
  3.    public final String lastName;
  4.    public User(String firstName, String lastName) {
  5.        this.firstName = firstName;
  6.        this.lastName = lastName;
  7.    }
  8. }

這個類型的對象擁有從不改變的資料。在app中它是常見的,可以讀取一次并且之後從不改變。當然也可以使用JavaBeans對象:

  1. public class User {
  2.    private final String firstName;
  3.    private final String lastName;
  4.    public User(String firstName, String lastName) {
  5.        this.firstName = firstName;
  6.        this.lastName = lastName;
  7.    }
  8.    public String getFirstName() {
  9.        return this.firstName;
  10.    }
  11.    public String getLastName() {
  12.        return this.lastName;
  13.    }
  14. }

從Data Binding的角度來看,這兩個類是等價的。用于TextView中的

android:text

屬性的表達式

@{user.firstName}

将通路前者POJO對象中的

firstName

和後者JavaBeans對象中的

getFirstName()

方法。

c)Binding資料

預設情況下,一個Binding類會基于layout檔案的名稱而産生,将其轉換為Pascal case(譯注:首字母大寫的命名規範)并且添加“Binding”字尾。上述的layout檔案是

activity_main.xml

,是以生成的類名是

ActivityMainBinding

。此類包含從layout屬性到layout的Views中所有的bindings(例如

user

變量),并且它還知道如何給Binding表達式配置設定數值。建立bindings的最簡單的方式是在inflating(譯注:layout檔案與Activity/Fragment的“連結”)期間如下:

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3.    super.onCreate(savedInstanceState);
  4.    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
  5.    User user = new User("Test", "User");
  6.    binding.setUser(user);
  7. }

就是這樣,運作app後,你将會看到Test User。或者你可以通過如下擷取View:

  1. MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());

如果你在ListView或者RecyclerView adapter使用Data Binding時,你可能會使用:

  1. ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup,
  2. false);
  3. //or
  4. ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

4)深入Layout檔案

a)Import

零個或多個

import

元素可能在

data

元素中使用。這些隻用在你的layout檔案中添加引用,就像在Java中:

  1. <data>
  2.     <import type="android.view.View"/>
  3. </data>

現在,View可以使用你的Binding表達式:

  1. <TextView
  2.    android:text="@{user.lastName}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"
  5.    android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

當類名有沖突時,其中一個類名可以重命名為

alias:

  1. <import type="android.view.View"/>
  2. <import type="com.example.real.estate.View"
  3.         alias="Vista"/>

這樣,在該layout檔案中

Vista

對應

com.example.real.estate.View

,而

View

對應

android.view.View

。導入的類型可以在Variable和表達式中使用作為引用來使用:

  1. <data>
  2.     <import type="com.example.User"/>
  3.     <import type="java.util.List"/>
  4.     <variable name="user" type="User"/>
  5.     <variable name="userList" type="List<User>"/>
  6.  </data>
注意:Android Studio還沒有處理imports,是以自動導入Variable在你的IDE不能使用。您的app仍會正常編譯,你可以在您的Variable定義中使用完全符合規定的名稱來解決該IDE問題。
  1. <TextView
  2.    android:text="@{((User)(user.connection)).lastName}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

導入的類型還可以在表達式中使用static屬性和方法:

  1. <data>
  2.     <import type="com.example.MyStringUtils"/>
  3.     <variable name="user" type="com.example.User"/>
  4. </data>
  5. <TextView
  6.    android:text="@{MyStringUtils.capitalize(user.lastName)}"
  7.    android:layout_width="wrap_content"
  8.    android:layout_height="wrap_content"/>

就像在Java中,

java.lang。*

是自動導入的。

b)Variables

data

中可以使用任意數量的

variable

元素。每一個

variable

元素描述了一個用于layout檔案中Binding表達式的屬性。

  1. <data>
  2.     <import type="android.graphics.drawable.Drawable"/>
  3.     <variable name="user"  type="com.example.User"/>
  4.     <variable name="image" type="Drawable"/>
  5.     <variable name="note"  type="String"/>
  6. </data>

Variable

類型在編譯時檢查,是以如果一個Variable實作了Observable或observable collection,這應該反映在類型中。(譯注:需要查找資料來了解)如果variable是一個沒有實作Observable接口的基本類或者接口,Variables不會被observed!

當對于多種配置有不同的layout檔案時(如,橫向或縱向),Variables會被合并。這些layout檔案之間必須不能有沖突的Variable定義。

産生的Binding類對于每一個描述的Variables都會有setter和getter。這些Variables會使用預設的Java值 - null(引用類型)、0(int)、false(boolean)等等,直到調用setter時。

c)自定義Binding類名稱

預設情況下,Binding類的命名是基于所述layout檔案的名稱,用大寫開頭,除去下劃線()以及()後的第一個字母大寫,然後添加“Binding”字尾。這個類将被放置在一個子產品封裝包裡的

databinding

封裝包下。例如,所述layout檔案

contact_item.xml

将生成

ContactItemBinding

。如果子產品包是

com.example.my.app

,那麼它将被放置在

com.example.my.app.databinding

Binding類可通過調整

data

元素中的

class

屬性來重命名或放置在不同的包中。例如:

  1. <data class="ContactItem">
  2.     ...
  3. </data>

在子產品封裝包的databinding包中會生成名為

ContactItem

的Binding類。如果要想讓該類生成在不同的包種,你需要添加字首

.

,如下:

  1. <data class=".ContactItem">
  2.     ...
  3. </data>

在這個情況下,

ContactItem

類直接在子產品包種生成。或者你可以提供整個包名:

  1. <data class="com.example.ContactItem">
  2.     ...
  3. </data>
d)Includes

通過使用application namespace以及在屬性中的Variable名字從容器layout中傳遞Variables到一個被包含的layout:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <layout xmlns:android="http://schemas.android.com/apk/res/android"
  3.         xmlns:bind="http://schemas.android.com/apk/res-auto">
  4.    <data>
  5.        <variable name="user" type="com.example.User"/>
  6.    </data>
  7.    <LinearLayout
  8.        android:orientation="vertical"
  9.        android:layout_width="match_parent"
  10.        android:layout_height="match_parent">
  11.        <include layout="@layout/name"
  12.            bind:user="@{user}"/>
  13.        <include layout="@layout/contact"
  14.            bind:user="@{user}"/>
  15.    </LinearLayout>
  16. </layout>

注意:在

name.xml

以及

contact.xml

兩個layout檔案中必需要有

user

 variable

e)表達式
  • 常用表達式跟Java表達式很像,以下這些是一樣的:
    • 數學 

      +

      -

      /

      *

      %

    • 字元串連接配接 

      +

    • 邏輯 

      &&

      ||

    • 二進制 

      &

      |

      ^

    • 一進制運算 

      +

      -

      !

      ~

    • 移位 

      >>

      >>>

      <<

    • 比較 

      ==

      >

      <

      >=

      <=

    • instanceof

    • 分組 

      ()

    • null

    • Cast

    • 方法調用
    • 資料通路 

      []

    • 三元運算 

      ?:

    • 示例:
      1. android:text="@{String.valueOf(index + 1)}"
      2. android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
      3. android:transitionName='@{"image_" + id}'
  • 缺少的操作:
    • this

    • super

    • new

    • 顯式泛型調用
  • Null合并操作
    • ??

       - 左邊的對象如果它不是null,選擇左邊的對象;或者如果它是null,選擇右邊的對象:
      1. android:text="@{user.displayName ?? user.lastName}"
  • 函數上的寫法如下:
    1. android:text="@{user.displayName != null ? user.displayName : user.lastName}"
  • 屬性引用

    第一個已經在前邊提到了

    a)Data Binding表達式

    :JavaBean引用的簡短格式。

    當一個表達式引用一個類的屬性,它仍使用同樣的格式對于字段、getters以及ObservableFields。

    1. android:text="@{user.lastName}"
  • 避免 NullPointerException

    Data Binding代碼生成時自動檢查是否為nulls來避免出現null pointer exceptions錯誤。例如,在表達式

    @{user.name}

    中,如果

    user

    是null,

    user.name

    會賦予它的預設值(null)。如果你引用

    user.age

    ,age是

    int

    類型,那麼它的預設值是0。
  • 集合

    常用的集合:arrays、lists、sparse lists以及maps,為了簡便都可以使用

    []

    來通路。
    1. <data>
    2.   <import type="android.util.SparseArray"/>
    3.   <import type="java.util.Map"/>
    4.   <import type="java.util.List"/>
    5.   <variable name="list" type="List<String>"/>
    6.   <variable name="sparse" type="SparseArray<String>"/>
    7.   <variable name="map" type="Map<String, String>"/>
    8.   <variable name="index" type="int"/>
    9.   <variable name="key" type="String"/>
    10. </data>
    11. android:text="@{list[index]}"
    12. android:text="@{sparse[index]}"
    13. android:text="@{map[key]}"
  • 字元串

    當使用單引号包含屬性值時,在表達式中使用雙引号很容易:

    1. android:text='@{map["firstName"]}'
    使用雙引号來包含屬性值也是可以的。字元串前後需要使用"`":
    1. android:text="@{map[`firstName`]}"
    2. android:text="@{map["firstName"]}"
  • Resources

    使用正常的表達式來通路resources也是可行的:

    1. android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
    格式化字元串和複數可以通過提供參數來判斷
    1. android:text="@{@string/nameFormat(firstName, lastName)}"
    2. android:text="@{@plurals/banana(bananaCount)}"
    當複數需要多個參數時,所有的參數都會通過:
    1. Have an orange
    2. Have %d oranges
    3. android:text="@{@plurals/orange(orangeCount, orangeCount)}"
    一些資源需要顯式類型判斷:
類型 正常引用 表達式引用
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

5)Data 對象

任何Plain old Java object(POJO)可用于Data Binding,但修改POJO不會導緻UI更新。Data Binding的真正能力是當資料變化時,可以通知給你的Data對象。有三種不同的資料變化通知機制:

Observable

對象、

ObservableFields

以及

observable  collections

當這些可觀察Data對象綁定到UI,Data對象屬性的更改後,UI也将自動更新。

a)Observable 對象

實作

android.databinding.Observable

接口的類可以允許附加一個監聽器到Bound對象以便監聽對象上的所有屬性的變化。

Observable

接口有一個機制來添加和删除監聽器,但通知與否由開發人員管理。為了使開發更容易,一個

BaseObservable

的基類為實作監聽器注冊機制而建立。Data實作類依然負責通知當屬性改變時。這是通過指定一個

Bindable

注解給getter以及setter内通知來完成的。

  1. private static class User extends BaseObservable {
  2.    private String firstName;
  3.    private String lastName;
  4.    @Bindable
  5.    public String getFirstName() {
  6.        return this.firstName;
  7.    }
  8.    @Bindable
  9.    public String getFirstName() {
  10.        return this.lastName;
  11.    }
  12.    public void setFirstName(String firstName) {
  13.        this.firstName = firstName;
  14.        notifyPropertyChanged(BR.firstName);
  15.    }
  16.    public void setLastName(String lastName) {
  17.        this.lastName = lastName;
  18.        notifyPropertyChanged(BR.lastName);
  19.    }
  20. }

在編譯期間,

Bindable

注解在BR類檔案中生成一個Entry。BR類檔案會在子產品包内生成。如果用于Data類的基類不能改變,

Observable

接口通過友善的

PropertyChangeRegistry

來實作用于儲存和有效地通知監聽器。

b)Observable 字段

一些小工作會涉及到建立Observable類,是以那些想要節省時間或者幾乎沒有幾個屬性的開發者可以使用

ObservableFields

ObservableFields

是自包含具有單個字段的observable對象。它有所有基本類型和一個是引用類型。要使用它需要在data對象中建立public final字段:

  1. private static class User extends BaseObservable {
  2.    public final ObservableField<String> firstName =
  3.        new ObservableField<>();
  4.    public final ObservableField<String> lastName =
  5.        new ObservableField<>();
  6.    public final ObservableInt age = new ObservableInt();
  7. }

就是這樣,要通路該值,使用set和get方法:

  1. user.firstName.set("Google");
  2. int age = user.age.get();
c)Observable 集合

一些app使用更多的動态結構來儲存資料。Observable集合允許鍵控通路這些data對象。

ObservableArrayMap

用于鍵是引用類型,如

String

  1. ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
  2. user.put("firstName", "Google");
  3. user.put("lastName", "Inc.");
  4. user.put("age", 17);

在layout檔案中,通過String鍵可以通路map:

  1. <data>
  2.     <import type="android.databinding.ObservableMap"/>
  3.     <variable name="user" type="ObservableMap<String, Object>"/>
  4. </data>
  5. <TextView
  6.    android:text='@{user["lastName"]}'
  7.    android:layout_width="wrap_content"
  8.    android:layout_height="wrap_content"/>
  9. <TextView
  10.    android:text='@{String.valueOf(1 + (Integer)user["age"])}'
  11.    android:layout_width="wrap_content"
  12.    android:layout_height="wrap_content"/>

ObservableArrayList

用于鍵是整數:

  1. ObservableArrayList<Object> user = new ObservableArrayList<>();
  2. user.add("Google");
  3. user.add("Inc.");
  4. user.add(17);

在layout檔案中,通過索引可以通路list:

  1. <data>
  2.     <import type="android.databinding.ObservableList"/>
  3.     <import type="com.example.my.app.Fields"/>
  4.     <variable name="user" type="ObservableList<Object>"/>
  5. </data>
  6. <TextView
  7.    android:text='@{user[Fields.LAST_NAME]}'
  8.    android:layout_width="wrap_content"
  9.    android:layout_height="wrap_content"/>
  10. <TextView
  11.    android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
  12.    android:layout_width="wrap_content"
  13.    android:layout_height="wrap_content"/>

6)Binding生成

Binding類的生成連結了layout中variables與Views。如前面所讨論的,Binding的名稱和包名可以定制。所生成的Binding類都擴充了

android.databinding.ViewDataBinding

a)建立

Binding應在inflation之後就立馬建立,以確定View層次結構不在之前打擾layout中的binding到views上的表達式。有幾個方法可以綁定到一個layout。最常見的是在Binding類上使用靜态方法.

inflate

方法載入View的層次結構并且綁定到它隻需這一步。還有一個更簡單的版本,隻需要

LayoutInflater

還有一個是采用

ViewGroup

  1. MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
  2. MyLayoutBinding binding = MyLayoutBinding.inflate(LayoutInflater, viewGroup, false);

如果使用不同的機制載入layout,他可一分開綁定:

  1. MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有時Binding不能提前知道,對于這種情況,可以使用

DataBindingUtil

類來建立Binding:

  1. ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
  2.     parent, attachToParent);
  3. ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
b)帶ID的Views

在layout中對于每個帶ID的View會生成一個public final字段。Binding在View層次結構上做單一的傳遞,提取帶ID的Views。這種機制比起某些Views使用

findViewById

還要快。例如:

  1. <layout xmlns:android="http://schemas.android.com/apk/res/android">
  2.    <data>
  3.        <variable name="user" type="com.example.User"/>
  4.    </data>
  5.    <LinearLayout
  6.        android:orientation="vertical"
  7.        android:layout_width="match_parent"
  8.        android:layout_height="match_parent">
  9.        <TextView android:layout_width="wrap_content"
  10.            android:layout_height="wrap_content"
  11.            android:text="@{user.firstName}"
  12.    android:id="@+id/firstName"/>
  13.        <TextView android:layout_width="wrap_content"
  14.            android:layout_height="wrap_content"
  15.            android:text="@{user.lastName}"
  16.   android:id="@+id/lastName"/>
  17.    </LinearLayout>
  18. </layout>

它會生成如下的Binding類:

  1. public final TextView firstName;
  2. public final TextView lastName;

IDs不像沒有Data Bindings那樣幾乎沒有必要,但是仍然會有一些執行個體需要從代碼中通路Views。

c)Variables

每個Variable會有通路方法。

  1. <data>
  2.     <import type="android.graphics.drawable.Drawable"/>
  3.     <variable name="user"  type="com.example.User"/>
  4.     <variable name="image" type="Drawable"/>
  5.     <variable name="note"  type="String"/>
  6. </data>

它會在Binding中生成setters和getters:

  1. public abstract com.example.User getUser();
  2. public abstract void setUser(com.example.User user);
  3. public abstract Drawable getImage();
  4. public abstract void setImage(Drawable image);
  5. public abstract String getNote();
  6. public abstract void setNote(String note);
d)ViewStubs

ViewStubs跟正常的Views略有不同。他們開始時是不可見的,當他們要麼設定為可見或被明确告知要載入時,它們通過載入另外一個layout取代了自己。

由于ViewStub基本上從View的層次結構上消失,在Binding對象的View也必須消失來允許被收集。因為Views是最後的,一個

ViewStubProxy

對象取帶ViewStub,給開發者獲得了ViewStub,當它存在以及還可以通路載入的View層次結構時當ViewStub已被載入時。

當載入另一個layout,為新的布局必需建立一個Binding。是以,

ViewStubProxy

必需監聽

ViewStub

OnInflateListener

監聽器并在那個時候建立Binding。因為隻有一個可以存在,

ViewStubProxy

允許開發者在其上設定一個

OnInflateListener

它會在建立Binding後調用。

e)Binding進階
  • 動态Variables

有時,不知道具體的Binding類,例如,一個

RecyclerView

擴充卡對layouts任意操作并不知道具體的Binding類。它仍然必需在

onBindViewHolder

期間指派給Binding。

在這個例子中,該

RecyclerView

綁定的所有layouts有一個“item”的Variable。該

BindingHolder

有一個

getBinding

方法傳回

ViewDataBinding

  1. public void onBindViewHolder(BindingHolder holder, int position) {
  2.    final T item = mItems.get(position);
  3.    holder.getBinding().setVariable(BR.item, item);
  4.    holder.getBinding().executePendingBindings();
  5. }
  • 直接Binding

當一個variable或observable變化時,binding會在下一幀之前被計劃要改變。有很多次,但是在Binding時必須立即執行。要強制執行,使用

executePendingBindings()

方法。

  • 背景線程

隻要它不是一個集合,你可以在背景線程中改變你的資料模型。在判斷是否要避免任何并發問題時,Data Binding會對每個Varialbe/field本地化。

7)屬性Setters

每當綁定值的變化,生成的Binding類必須調用setter方法。Data Binding架構有可以自定義指派的方法。

a)自動Setters

對于一個屬性,Data Binding試圖找到

setAttribute

方法。與該屬性的namespace并不什麼關系,僅僅與屬性本身名稱有關。

例如,有關TextView的

android:text

屬性的表達式會尋找一個

setText(String)

的方法。如果表達式傳回一個

int

,Data Binding會搜尋的

setText(int)

方法。注意:要表達式傳回正确的類型,如果需要的話使用

casting

。Data Binding仍會工作即使沒有給定名稱的屬性存在。然後,您可以通過Data Binding輕松地為任何setter“創造”屬性。例如,

DrawerLayout

沒有任何屬性,但大量的setters。您可以使用自動setters來使用其中的一個。

  1. <android.support.v4.widget.DrawerLayout
  2.     android:layout_width="wrap_content"
  3.     android:layout_height="wrap_content"
  4.     app:scrimColor="@{@color/scrim}"
  5.     app:drawerListener="@{fragment.drawerListener}"/>
b)重命名的Setters

一些有setters的屬性按名稱并不比對。對于這些方法,屬性可以通過

BindingMethods

注解相關聯。這必須與一個包含

BindingMethod

注解的類相關聯,每一個用于一個重命名的方法。例如,

android:tint

屬性與

setImageTintList

相關聯,而不與

setTint

相關。

  1. @BindingMethods({
  2.        @BindingMethod(type = "android.widget.ImageView",
  3.                       attribute = "android:tint",
  4.                       method = "setImageTintList"),
  5. })

以上例子,開發者需要重命名setters是不太可能了,android架構屬性已經實作了。

c)自定義Setters

有些屬性需要自定義綁定邏輯。例如,對于

android:paddingLeft

屬性并沒有相關setter。相反,

setPadding(left, top, right, bottom)

是存在在。一個帶有

BindingAdapter

注解的靜态綁定擴充卡方法允許開發者自定義setter如何對于一個屬性的調用。

Android的屬性已經創造了

BindingAdapters

。舉例來說,對于

paddingLeft

  1. @BindingAdapter("android:paddingLeft")
  2. public static void setPaddingLeft(View view, int padding) {
  3.    view.setPadding(padding,
  4.                    view.getPaddingTop(),
  5.                    view.getPaddingRight(),
  6.                    view.getPaddingBottom());
  7. }

Binding擴充卡對其他定制類型非常有用。例如,自定義loader可以用來異步載入圖像。

當有沖突時,開發人員建立的Binding擴充卡将覆寫Data Binding預設擴充卡。

您也可以建立可以接收多個參數的擴充卡。

  1. @BindingAdapter({"bind:imageUrl", "bind:error"})
  2. public static void loadImage(ImageView view, String url, Drawable error) {
  3.    Picasso.with(view.getContext()).load(url).error(error).into(view);
  4. }
  1. <ImageView app:imageUrl=“@{venue.imageUrl}”
  2. app:error=“@{@drawable/venueError}”/>

如果對于一個

ImageView

imageUrl和error都被使用并且

imageUrl

是一個string類型以及error是一個drawable時,該擴充卡會被調用。

  • 比對的過程中自定義namespaces将被忽略。
  • 你也可以為Android namespaces寫擴充卡。

8)轉換

a)對象轉換

當從Binding表達式傳回一個對象,一個setter會從自動、重命名以及自定義的setters中選擇。該對象将被轉換為所選擇的setter的參數類型。

這是為了友善那些使用

ObservableMaps

來儲存資料。例如:

  1. <TextView
  2.    android:text='@{userMap["lastName"]}'
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

userMap

傳回一個對象并且該對象将自動轉換為

setText(CharSequence)

的參數類型。當有關參數類型可能混亂時,開發人員需要在表達式中轉換。

b)自定義轉換

有時候轉換應該是自動的在特定類型之間。例如,設定背景的時候:

  1. <View
  2.    android:background="@{isError ? @color/red : @color/white}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

這裡,背景需要

Drawable

對象,但顔色是一個整數。不管何時有

Drawable

并且傳回值是一個整數,那麼整數類型會被轉換為

ColorDrawable

。這個轉換是通過使用帶有

BindingConversion

注解的靜态方法完成的:

  1. @BindingConversion
  2. public static ColorDrawable convertColorToDrawable(int color) {
  3.    return new ColorDrawable(color);
  4. }

注意:轉換僅僅發生在setter級别,是以它是不允許以下混合類型:

  1. <View
  2.    android:background="@{isError ? @drawable/error : @color/white}"
  3.    android:layout_width="wrap_content"
  4.    android:layout_height="wrap_content"/>

---------------------<完>-----------------------