Android Data Binding(數(shù)據(jù)綁定)用戶(hù)指南

轉(zhuǎn)自:http://www.reibang.com/p/b1df61a4df77

1. 介紹

這篇文章介紹了如何使用Data Binding庫(kù)來(lái)寫(xiě)聲明的layouts文件,并且用最少的代碼來(lái)綁定你的app邏輯和layouts文件。Data Binding庫(kù)不僅靈活而且廣泛兼容- 它是一個(gè)support庫(kù)翻具,因此你可以在所有的Android平臺(tái)最低能到Android 2.1(API等級(jí)7+)上使用它。

需求:Android Plugin for Gradle **1.5.0-alpha1 **或 更高版本。

2. 構(gòu)建環(huán)境

要開(kāi)始使用Data Binding荚醒,首先需要在Android SDK Manager的支持庫(kù)里下載該庫(kù)滓技。
你的app要使用Data Binding,需要添加Data Binding到gradle構(gòu)建文件里冯吓,如下:

android {
    ....
    dataBinding {
        enabled = true    
    }    
}

Data Binding插件將會(huì)在你的項(xiàng)目?jī)?nèi)添加必需提供的以及編譯配置依賴(lài)倘待。請(qǐng)確保您使用的是Android Studio的兼容版本。Android Studio的Data Binding插件需要Android Studio **1.3.0 **或 更高版本组贺。

3. Data Binding Layout文件

3.1 Data Binding表達(dá)式

Data Binding layout文件有點(diǎn)不同的是:起始根標(biāo)簽是layout凸舵,接下來(lái)一個(gè)data元素以及一個(gè)view的根元素。這個(gè)view元素就是你沒(méi)有使用Data Binding的layout文件的根元素失尖。舉例說(shuō)明如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

在data內(nèi)描述了一個(gè)名為user的變量屬性啊奄,使其可以在這個(gè)layout中使用:

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

在layout的屬性表達(dá)式寫(xiě)作@{},下面是一個(gè)TextView的text設(shè)置為user的firstName屬性:

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{user.firstName}"/>

3.2 Data對(duì)象

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

public class User {
   public final String firstName;
   public final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
}

這個(gè)類(lèi)型的對(duì)象擁有從不改變的數(shù)據(jù)掀潮。在app中它是常見(jiàn)的菇夸,可以讀取一次并且之后從不改變。當(dāng)然也可以使用JavaBeans對(duì)象:

public class User {
   private final String firstName;
   private final String lastName;
   public User(String firstName, String lastName) {
       this.firstName = firstName;
       this.lastName = lastName;
   }
   public String getFirstName() {
       return this.firstName;
   }
   public String getLastName() {
       return this.lastName;
   }
}

從Data Binding的角度來(lái)看仪吧,這兩個(gè)類(lèi)是等價(jià)的庄新。用于TextView中的android:text屬性的表達(dá)式@{user.firstName}將訪問(wèn)前者POJO對(duì)象中的firstName和后者JavaBeans對(duì)象中的getFirstName()方法。

3.3 Binding數(shù)據(jù)

默認(rèn)情況下薯鼠,一個(gè)Binding類(lèi)會(huì)基于layout文件的名稱(chēng)而產(chǎn)生择诈,將其轉(zhuǎn)換為Pascal case(譯注:首字母大寫(xiě)的命名規(guī)范)并且添加“Binding”后綴。上述的layout文件是main_activity.xml出皇,因此生成的類(lèi)名是MainActivityBinding羞芍。此類(lèi)包含從layout屬性到layout的Views中所有的bindings(例如user變量),并且它還知道如何給Binding表達(dá)式分配數(shù)值恶迈。創(chuàng)建bindings的最簡(jiǎn)單的方式是在inflating(譯注:layout文件與Activity/Fragment的“鏈接”)期間如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
}

就是這樣涩金,運(yùn)行app后谱醇,你將會(huì)看到Test User〔阶觯或者你可以通過(guò)如下獲取View:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);
   User user = new User("Test", "User");
   binding.setUser(user);
}

如果你在ListView或者RecyclerView adapter使用Data Binding時(shí)副渴,你可能會(huì)使用:

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

3.4 事件處理

數(shù)據(jù)綁定允許你編寫(xiě)表達(dá)式來(lái)處理view分派的事件。事件屬性名字取決于監(jiān)聽(tīng)器方法名字全度。例如View.OnLongClickListener有onLongClick()的方法煮剧,因此這個(gè)事件的屬性是android:onLongClick。處理事件有兩種方法:

  • Method References
  • Listener Bindings

4. 深入Layout文件

4.1 Import

零個(gè)或多個(gè)import元素可能在data元素中使用将鸵。這些只用在你的layout文件中添加引用勉盅,就像在Java中:

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

現(xiàn)在,View可以使用你的Binding表達(dá)式:

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

當(dāng)類(lèi)名有沖突時(shí)顶掉,其中一個(gè)類(lèi)名可以重命名為alias::

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

這樣草娜,在該layout文件中Vista對(duì)應(yīng)com.example.real.estate.View,而View對(duì)應(yīng)android.view.View痒筒。導(dǎo)入的類(lèi)型可以在Variable和表達(dá)式中使用作為引用來(lái)使用:

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List<User>"/>
 </data>

注意:Android Studio還沒(méi)有處理imports宰闰,所以自動(dòng)導(dǎo)入Variable在你的IDE不能使用。您的app仍會(huì)正常編譯簿透,你可以在您的Variable定義中使用完全符合規(guī)定的名稱(chēng)來(lái)解決該IDE問(wèn)題移袍。

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

導(dǎo)入的類(lèi)型還可以在表達(dá)式中使用static屬性和方法:

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

就像在Java中,java.lang老充。*是自動(dòng)導(dǎo)入的葡盗。

4.2 Variables

在data中可以使用任意數(shù)量的variable元素。每一個(gè)variable元素描述了一個(gè)用于layout文件中Binding表達(dá)式的屬性啡浊。

<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user"  type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note"  type="String"/>
</data>

該Variable類(lèi)型在編譯時(shí)檢查觅够,因此如果一個(gè)Variable實(shí)現(xiàn)了Observable或observable collection,這應(yīng)該反映在類(lèi)型中虫啥。(譯注:需要查找資料來(lái)理解)如果variable是一個(gè)沒(méi)有實(shí)現(xiàn)Observable接口的基本類(lèi)或者接口蔚约,Variables不會(huì)被observed!當(dāng)對(duì)于多種配置有不同的layout文件時(shí)(如涂籽,橫向或縱向),Variables會(huì)被合并砸抛。這些layout文件之間必須不能有沖突的Variable定義评雌。

產(chǎn)生的Binding類(lèi)對(duì)于每一個(gè)描述的Variables都會(huì)有setter和getter。這些Variables會(huì)使用默認(rèn)的Java值 - null(引用類(lèi)型)直焙、0(int)景东、false(boolean)等等,直到調(diào)用setter時(shí)奔誓。

4.3 自定義Binding類(lèi)名稱(chēng)

默認(rèn)情況下斤吐,Binding類(lèi)的命名是基于所述layout文件的名稱(chēng)搔涝,用大寫(xiě)開(kāi)頭,除去下劃線()以及()后的第一個(gè)字母大寫(xiě)和措,然后添加“Binding”后綴庄呈。這個(gè)類(lèi)將被放置在一個(gè)模塊封裝包里的databinding封裝包下。例如派阱,所述layout文件contact_item.xml將生成ContactItemBinding诬留。如果模塊包是com.example.my.app,那么它將被放置在com.example.my.app.databinding贫母。

Binding類(lèi)可通過(guò)調(diào)整data元素中的class屬性來(lái)重命名或放置在不同的包中文兑。例如:

<data class="ContactItem">
    ...
</data>

在模塊封裝包的databinding包中會(huì)生成名為ContactItem的Binding類(lèi)。如果要想讓該類(lèi)生成在不同的包種腺劣,你需要添加前綴.绿贞,如下:

<data class=".ContactItem">
    ...
</data>

在這個(gè)情況下,ContactItem類(lèi)直接在模塊包種生成橘原〖或者你可以提供整個(gè)包名:

<data class="com.example.ContactItem">
    ...
</data>

4.4 Includes

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

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:bind="http://schemas.android.com/apk/res-auto">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </LinearLayout>
</layout>

注意:在name.xml以及contact.xml兩個(gè)layout文件中必需要有user variable

4.5 表達(dá)式

常用表達(dá)式跟Java表達(dá)式很像,以下這些是一樣的:

- 描述 - - 運(yùn)算符 -
數(shù)學(xué) + - * / %
字符串連接 +
邏輯 && ||
二進(jìn)制 & ^
一元運(yùn)算 + - ! ~
移位 >> >>> <<<
比較 == > < >= <=
實(shí)例 instanceof
分組 ()
null
類(lèi)型轉(zhuǎn)換 cast
方法調(diào)用 .
數(shù)據(jù)訪問(wèn) []
三元運(yùn)算 ?:

示例:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
  • 缺少的操作:
    • this
    • super
    • new
    • 顯式泛型調(diào)用
  • Null合并操作

?? - 左邊的對(duì)象如果它不是null靠柑,選擇左邊的對(duì)象寨辩;或者如果它是null,選擇右邊的對(duì)象:

android:text="@{user.displayName ?? user.lastName}"

函數(shù)上的寫(xiě)法如下:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"
  • 屬性引用
    第一個(gè)已經(jīng)在前邊提到了a)Data Binding表達(dá)式:JavaBean引用的簡(jiǎn)短格式歼冰。
    當(dāng)一個(gè)表達(dá)式引用一個(gè)類(lèi)的屬性靡狞,它仍使用同樣的格式對(duì)于字段、getters以及ObservableFields隔嫡。
android:text="@{user.lastName}"
  • 避免 NullPointerException
    Data Binding代碼生成時(shí)自動(dòng)檢查是否為nulls來(lái)避免出現(xiàn)null pointer exceptions錯(cuò)誤甸怕。例如,在表達(dá)式@{user.name}中腮恩,如果user是null梢杭,user.name會(huì)賦予它的默認(rèn)值(null)。如果你引用user.age秸滴,age是int類(lèi)型武契,那么它的默認(rèn)值是0。

  • 集合
    常用的集合:arrays荡含、lists咒唆、sparse lists以及maps,為了簡(jiǎn)便都可以使用[]來(lái)訪問(wèn)释液。

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List<String>"/>
    <variable name="sparse" type="SparseArray<String>"/>
    <variable name="map" type="Map<String, String>"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"
  • 字符串
    當(dāng)使用單引號(hào)包含屬性值時(shí)全释,在表達(dá)式中使用雙引號(hào)很容易:
    android:text='@{map["firstName"]}'
    使用雙引號(hào)來(lái)包含屬性值也是可以的。字符串前后需要使用"`":
android:text="@{map[`firstName`]}"
android:text="@{map["firstName"]}"
  • Resources
    使用正常的表達(dá)式來(lái)訪問(wèn)resources也是可行的:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

格式化字符串和復(fù)數(shù)可以通過(guò)提供參數(shù)來(lái)判斷

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

當(dāng)復(fù)數(shù)需要多個(gè)參數(shù)時(shí)误债,所有的參數(shù)都會(huì)通過(guò):

  Have an orange
  Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"

一些資源需要顯式類(lèi)型判斷:

類(lèi)型 正常引用 表達(dá)式引用
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

5. Data 對(duì)象

任何Plain old Java object(PO??JO)可用于Data Binding浸船,但修改POJO不會(huì)導(dǎo)致UI更新妄迁。Data Binding的真正能力是當(dāng)數(shù)據(jù)變化時(shí),可以通知給你的Data對(duì)象李命。有三種不同的數(shù)據(jù)變化通知機(jī)制:Observable對(duì)象登淘、ObservableFields以及observable collections。當(dāng)這些可觀察Data對(duì)象??綁定到UI项戴,Data對(duì)象屬性的更改后形帮,UI也將自動(dòng)更新。

5.1 Observable 對(duì)象

實(shí)現(xiàn)android.databinding.Observable接口的類(lèi)可以允許附加一個(gè)監(jiān)聽(tīng)器到Bound對(duì)象以便監(jiān)聽(tīng)對(duì)象上的所有屬性的變化周叮。Observable接口有一個(gè)機(jī)制來(lái)添加和刪除監(jiān)聽(tīng)器辩撑,但通知與否由開(kāi)發(fā)人員管理。為了使開(kāi)發(fā)更容易仿耽,一個(gè)BaseObservable的基類(lèi)為實(shí)現(xiàn)監(jiān)聽(tīng)器注冊(cè)機(jī)制而創(chuàng)建合冀。Data實(shí)現(xiàn)類(lèi)依然負(fù)責(zé)通知當(dāng)屬性改變時(shí)。這是通過(guò)指定一個(gè)Bindable注解給getter以及setter內(nèi)通知來(lái)完成的项贺。

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getFirstName() {
       return this.lastName;
   }
   public void setFirstName(String firstName) {
       this.firstName = firstName;
       notifyPropertyChanged(BR.firstName);
   }
   public void setLastName(String lastName) {
       this.lastName = lastName;
       notifyPropertyChanged(BR.lastName);
   }
}

在編譯期間君躺,Bindable注解在BR類(lèi)文件中生成一個(gè)Entry。BR類(lèi)文件會(huì)在模塊包內(nèi)生成开缎。如果用于Data類(lèi)的基類(lèi)不能改變棕叫,Observable接口通過(guò)方便的PropertyChangeRegistry來(lái)實(shí)現(xiàn)用于儲(chǔ)存和有效地通知監(jiān)聽(tīng)器。

5.2 Observable 字段

一些小工作會(huì)涉及到創(chuàng)建Observable類(lèi)奕删,因此那些想要節(jié)省時(shí)間或者幾乎沒(méi)有幾個(gè)屬性的開(kāi)發(fā)者可以使用ObservableFields俺泣。ObservableFields是自包含具有單個(gè)字段的observable對(duì)象。它有所有基本類(lèi)型和一個(gè)是引用類(lèi)型完残。要使用它需要在data對(duì)象中創(chuàng)建public final字段:

private static class User {
   public final ObservableField<String> firstName =
       new ObservableField<>();
   public final ObservableField<String> lastName =
       new ObservableField<>();
   public final ObservableInt age = new ObservableInt();
}

就是這樣伏钠,要訪問(wèn)該值,使用set和get方法:

user.firstName.set("Google");
int age = user.age.get();

5.3 Observable 集合

一些app使用更多的動(dòng)態(tài)結(jié)構(gòu)來(lái)保存數(shù)據(jù)谨设。Observable集合允許鍵控訪問(wèn)這些data對(duì)象熟掂。ObservableArrayMap用于鍵是引用類(lèi)型,如String。

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

在layout文件中扎拣,通過(guò)String鍵可以訪問(wèn)map:

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap<String, Object>"/>
</data>
…
<TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

ObservableArrayList用于鍵是整數(shù):

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

在layout文件中赴肚,通過(guò)索引可以訪問(wèn)list:

<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList<Object>"/>
</data>
…
<TextView
   android:text='@{user[Fields.LAST_NAME]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

6. Binding生成

Binding類(lèi)的生成鏈接了layout中variables與Views。如前面所討論的二蓝,Binding的名稱(chēng)和包名可以定制尊蚁。所生成的Binding類(lèi)都擴(kuò)展了android.databinding.ViewDataBinding。

6.1 創(chuàng)建

Binding應(yīng)在inflation之后就立馬創(chuàng)建侣夷,以確保View層次結(jié)構(gòu)不在之前打擾layout中的binding到views上的表達(dá)式。有幾個(gè)方法可以綁定到一個(gè)layout仑乌。最常見(jiàn)的是在Binding類(lèi)上使用靜態(tài)方法.inflate方法載入View的層次結(jié)構(gòu)并且綁定到它只需這一步百拓。還有一個(gè)更簡(jiǎn)單的版本琴锭,只需要LayoutInflater還有一個(gè)是采用ViewGroup:

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

如果使用不同的機(jī)制載入layout,他可一分開(kāi)綁定:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有時(shí)Binding不能提前知道衙传,對(duì)于這種情況决帖,可以使用DataBindingUtil類(lèi)來(lái)創(chuàng)建Binding:

ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

6.2 帶ID的Views

在layout中對(duì)于每個(gè)帶ID的View會(huì)生成一個(gè)public final字段。Binding在View層次結(jié)構(gòu)上做單一的傳遞蓖捶,提取帶ID的Views地回。這種機(jī)制比起某些Views使用findViewById還要快。例如:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
   android:id="@+id/firstName"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
  android:id="@+id/lastName"/>
   </LinearLayout>
</layout>

它會(huì)生成如下的Binding類(lèi):

public final TextView firstName;
public final TextView lastName;

IDs不像沒(méi)有Data Bindings那樣幾乎沒(méi)有必要俊鱼,但是仍然會(huì)有一些實(shí)例需要從代碼中訪問(wèn)Views刻像。

6.3 Variables

每個(gè)Variable會(huì)有訪問(wèn)方法。

<data>
    <import type="android.graphics.drawable.Drawable"/>
    <variable name="user"  type="com.example.User"/>
    <variable name="image" type="Drawable"/>
    <variable name="note"  type="String"/>
</data>

它會(huì)在Binding中生成setters和getters:

public abstract com.example.User getUser();
public abstract void setUser(com.example.User user);
public abstract Drawable getImage();
public abstract void setImage(Drawable image);
public abstract String getNote();
public abstract void setNote(String note);

6.4 ViewStubs

ViewStubs跟正常的Views略有不同并闲。他們開(kāi)始時(shí)是不可見(jiàn)的细睡,當(dāng)他們要么設(shè)置為可見(jiàn)或被明確告知要載入時(shí),它們通過(guò)載入另外一個(gè)layout取代了自己帝火。由于ViewStub基本上從View的層次結(jié)構(gòu)上消失溜徙,在Binding對(duì)象的View也必須消失來(lái)允許被收集。因?yàn)閂iews是最后的犀填,一個(gè)ViewStubProxy對(duì)象取帶ViewStub蠢壹,給開(kāi)發(fā)者獲得了ViewStub,當(dāng)它存在以及還可以訪問(wèn)載入的View層次結(jié)構(gòu)時(shí)當(dāng)ViewStub已被載入時(shí)九巡。當(dāng)載入另一個(gè)layout图贸,為新的布局必需創(chuàng)建一個(gè)Binding比庄。因此,ViewStubProxy必需監(jiān)聽(tīng)ViewStub的OnInflateListener監(jiān)聽(tīng)器并在那個(gè)時(shí)候建立Binding溉委。因?yàn)橹挥幸粋€(gè)可以存在藻三,ViewStubProxy允許開(kāi)發(fā)者在其上設(shè)置一個(gè)OnInflateListener它會(huì)在建立Binding后調(diào)用渣玲。

6.5 Binding進(jìn)階

  • 動(dòng)態(tài)Variables
    有時(shí)枚钓,不知道具體的Binding類(lèi)指煎,例如,一個(gè)RecyclerView適配器對(duì)layouts任意操作并不知道具體的Binding類(lèi)木西。它仍然必需在onBindViewHolder期間賦值給Binding恋捆。在這個(gè)例子中爽茴,該RecyclerView綁定的所有l(wèi)ayouts有一個(gè)“item”的Variable。該BindingHolder有一個(gè)getBinding方法返回ViewDataBinding劲装。
public void onBindViewHolder(BindingHolder holder, int position) {
   final T item = mItems.get(position);
   holder.getBinding().setVariable(BR.item, item);
   holder.getBinding().executePendingBindings();
}
  • 直接Binding
    當(dāng)一個(gè)variable或observable變化時(shí),binding會(huì)在下一幀之前被計(jì)劃要改變占业。有很多次绒怨,但是在Binding時(shí)必須立即執(zhí)行南蹂。要強(qiáng)制執(zhí)行疗疟,使用executePendingBindings()方法匣摘。

  • 后臺(tái)線程
    只要它不是一個(gè)集合,你可以在后臺(tái)線程中改變你的數(shù)據(jù)模型。在判斷是否要避免任何并發(fā)問(wèn)題時(shí),Data Binding會(huì)對(duì)每個(gè)Varialbe/field本地化。

7. 屬性Setters

每當(dāng)綁定值的變化刃榨,生成的Binding類(lèi)必須調(diào)用setter方法??。Data Binding框架有可以自定義賦值的方法。

7.1 自動(dòng)Setters

對(duì)于一個(gè)屬性,Data Binding試圖找到setAttribute方法。與該屬性的namespace并不什么關(guān)系,僅僅與屬性本身名稱(chēng)有關(guān)。例如,有關(guān)TextView的android:text屬性的表達(dá)式會(huì)尋找一個(gè)setText(String)的方法。如果表達(dá)式返回一個(gè)int,Data Binding會(huì)搜索的setText(int)方法。注意:要表達(dá)式返回正確的類(lèi)型,如果需要的話使用casting赠法。Data Binding仍會(huì)工作即使沒(méi)有給定名稱(chēng)的屬性存在款侵。然后壕鹉,您可以通過(guò)Data Binding輕松地為任何setter“創(chuàng)造”屬性脊凰。例如,DrawerLayout沒(méi)有任何屬性帕胆,但大量的setters驯用。您可以使用自動(dòng)setters來(lái)使用其中的一個(gè)薇正。

<android.support.v4.widget.DrawerLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:scrimColor="@{@color/scrim}"
    app:drawerListener="@{fragment.drawerListener}"/>

7.2 重命名的Setters

一些有setters的屬性按名稱(chēng)并不匹配晦炊。對(duì)于這些方法,屬性可以通過(guò)BindingMethods注解相關(guān)聯(lián)。這必須與一個(gè)包含BindingMethod注解的類(lèi)相關(guān)聯(lián)板丽,每一個(gè)用于一個(gè)重命名的方法砚殿。例如辛萍,android:tint屬性與setImageTintList相關(guān)聯(lián)耳幢,而不與setTint相關(guān)倒慧。

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

以上例子侍郭,開(kāi)發(fā)者需要重命名setters是不太可能了,android架構(gòu)屬性已經(jīng)實(shí)現(xiàn)了。

7.3 自定義Setters

有些屬性需要自定義綁定邏輯。例如,對(duì)于android:paddingLeft屬性并沒(méi)有相關(guān)setter惩激。相反,setPadding(left, top, right, bottom)是存在在羞反。一個(gè)帶有BindingAdapter注解的靜態(tài)綁定適配器方法允許開(kāi)發(fā)者自定義setter如何對(duì)于一個(gè)屬性的調(diào)用肛搬。

Android的屬性已經(jīng)創(chuàng)造了BindingAdapters。舉例來(lái)說(shuō)瓜晤,對(duì)于paddingLeft:

@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
   view.setPadding(padding,
                   view.getPaddingTop(),
                   view.getPaddingRight(),
                   view.getPaddingBottom());
}

Binding適配器對(duì)其他定制類(lèi)型非常有用。例如,自定義loader可以用來(lái)異步載入圖像。

當(dāng)有沖突時(shí)力喷,開(kāi)發(fā)人員創(chuàng)建的Binding適配器將覆蓋Data Binding默認(rèn)適配器陈症。

您也可以創(chuàng)建可以接收多個(gè)參數(shù)的適配器秀又。

@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);
}
<ImageView app:imageUrl=“@{venue.imageUrl}”
app:error=“@{@drawable/venueError}”/>

如果對(duì)于一個(gè)ImageViewimageUrl和error都被使用并且imageUrl是一個(gè)string類(lèi)型以及error是一個(gè)drawable時(shí),該適配器會(huì)被調(diào)用。匹配的過(guò)程中自定義namespaces將被忽略官还。你也可以為Android namespaces寫(xiě)適配器语卤。

8. 轉(zhuǎn)換

8.1 對(duì)象轉(zhuǎn)換

當(dāng)從Binding表達(dá)式返回一個(gè)對(duì)象,一個(gè)setter會(huì)從自動(dòng)荧库、重命名以及自定義的setters中選擇堰塌。該對(duì)象將被轉(zhuǎn)換為所選擇的setter的參數(shù)類(lèi)型。

這是為了方便那些使用ObservableMaps來(lái)保存數(shù)據(jù)分衫。例如:

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

在userMap返回一個(gè)對(duì)象并且該對(duì)象將自動(dòng)轉(zhuǎn)換為setText(CharSequence)的參數(shù)類(lèi)型场刑。當(dāng)有關(guān)參數(shù)類(lèi)型可能混亂時(shí),開(kāi)發(fā)人員需要在表達(dá)式中轉(zhuǎn)換蚪战。

8.2 自定義轉(zhuǎn)換

有時(shí)候轉(zhuǎn)換應(yīng)該是自動(dòng)的在特定類(lèi)型之間摇邦。例如恤煞,設(shè)置背景的時(shí)候:

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

這里,背景需要Drawable對(duì)象施籍,但顏色是一個(gè)整數(shù)居扒。不管何時(shí)有Drawable并且返回值是一個(gè)整數(shù),那么整數(shù)類(lèi)型會(huì)被轉(zhuǎn)換為ColorDrawable丑慎。這個(gè)轉(zhuǎn)換是通過(guò)使用帶有BindingConversion注解的靜態(tài)方法完成的:

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
   return new ColorDrawable(color);
}

注意:轉(zhuǎn)換僅僅發(fā)生在setter級(jí)別喜喂,因此它是不允許以下混合類(lèi)型:

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

9. Android Studio支持

Android Studio為數(shù)據(jù)綁定支持許多的代碼編輯。例如竿裂,它支持以下功能:

  • 語(yǔ)法高亮
  • 標(biāo)記表達(dá)式的語(yǔ)法錯(cuò)誤
  • XML代碼補(bǔ)全
  • 引用玉吁,包括navigation(如導(dǎo)航到聲明處)以及快速文檔查詢(xún)

注意: 數(shù)組以及通用類(lèi)型,比如說(shuō)Observable類(lèi)腻异,可能會(huì)顯示錯(cuò)誤事實(shí)上并沒(méi)有錯(cuò)誤进副。預(yù)覽面板會(huì)顯示數(shù)據(jù)綁定的默認(rèn)值。在以下例子中悔常,面板會(huì)在TextView中顯示PLACEHOLDER默認(rèn)值

<TextView android:layout_width="wrap_content"   android:layout_height="wrap_content"   android:text="@{user.firstName, default=PLACEHOLDER}"/>

如果你需要在設(shè)計(jì)階段就顯示默認(rèn)值影斑,你可以使用工具屬性來(lái)代替默認(rèn)表達(dá)數(shù)值,參考:Designtime Layout Attributes

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末机打,一起剝皮案震驚了整個(gè)濱河市矫户,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌残邀,老刑警劉巖皆辽,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異芥挣,居然都是意外死亡驱闷,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)空免,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)遗嗽,“玉大人,你說(shuō)我怎么就攤上這事鼓蜒。” “怎么了征字?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵都弹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我匙姜,道長(zhǎng)畅厢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任氮昧,我火速辦了婚禮框杜,結(jié)果婚禮上浦楣,老公的妹妹穿的比我還像新娘。我一直安慰自己咪辱,他們只是感情好振劳,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著油狂,像睡著了一般历恐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上专筷,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天弱贼,我揣著相機(jī)與錄音,去河邊找鬼磷蛹。 笑死吮旅,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的味咳。 我是一名探鬼主播庇勃,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼莺葫!你這毒婦竟也來(lái)了匪凉?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤捺檬,失蹤者是張志新(化名)和其女友劉穎再层,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體堡纬,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聂受,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了烤镐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛋济。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖炮叶,靈堂內(nèi)的尸體忽然破棺而出碗旅,到底是詐尸還是另有隱情,我是刑警寧澤镜悉,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布祟辟,位于F島的核電站,受9級(jí)特大地震影響侣肄,放射性物質(zhì)發(fā)生泄漏旧困。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吼具。 院中可真熱鬧僚纷,春花似錦、人聲如沸拗盒。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)锣咒。三九已至侵状,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間毅整,已是汗流浹背趣兄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悼嫉,地道東北人艇潭。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像戏蔑,于是被迫代替她去往敵國(guó)和親蹋凝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容