[譯]Data Binding

本文為官網(wǎng)文章翻譯 原文地址
這篇文檔將展示如何使用 Data Binding 庫聲明布局以及如何最低化應用邏輯和布局之間的耦合胸嘴。
Data Binding Library 非常靈活并且具有很高的兼容性---對于這個兼容庫宴树,你可以使用Android2.1以上的所有版本的Android平臺。
為了使用 data binding,Gradle的 android插件需要是1.5.0-alpha1或者更高。

構建環(huán)境

為了使用 Data Binding御蒲,通過sdk manager下載 support repository 中的依賴庫签财。
為了配置app應用 data binding间唉,在 app module 的 build.gradle 文件中,添加 dataBinding 元素施逾。
使用下面的代碼片段進行data binding 的配資:
android{
....
dataBinding{
enabled=true
}
}
如果你的app module依賴于一個使用了 data binding 的庫敷矫,你的 app module 也必須在build.gradle 文件中配置 data binding。
同樣汉额,你需要確認你的android studio 應該不低于1.3

Data Binding 布局文件

第一個data binding 表達式曹仗。

data binding布局文件和普通布局文件稍微有些不同。它以layout為根標簽(root tag)蠕搜,隨后緊跟data元素和一個view的根元素怎茫。view根元素中布局的寫法于普通非綁定的布局文件相同。示例:

<?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中user變量描述了一個會在布局中用到的屬性讥脐。
<variable name="user" type="com.example.User"/>
在布局文件中遭居,表達式使用"@{}"語法描述屬性特征啼器。下面是一個示例旬渠,將TextView的text屬性設置為設置用戶名。

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

Data 對象

假設有一個User普通類

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

這個類型中的數(shù)據(jù)不能夠改變端壳。在一個應用中告丢,數(shù)據(jù)一旦被讀取就不再改變是很常見的場景。當然损谦,也可以使用下面的JavaBeans

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;
   }
}

從數(shù)據(jù)綁定的觀點來看岖免,上面兩個類是等價的岳颇。表達式 @{user.firstName}被用來設置TextView的text屬性。對于前面的類來說颅湘,通過直接訪問 firstName 屬性的方式话侧;對于后面的類來說,通過getFirstName()的方法闯参。當然瞻鹏,也可以通過firstName()來解決這個問題,如果這個方法存在的話鹿寨。

綁定數(shù)據(jù)

默認的新博,綁定類將被創(chuàng)建,它的名字以布局文件名為基礎脚草,遵循Pascal原則赫悄,以"Binding"為后綴。比如馏慨,上面的布局文件名為 main_activity.xml埂淮,因此將產(chǎn)生的類就是MainActivityBinding。這個類持有所有從特征(比如user變量)到布局view的綁定熏纯,并且知道怎樣為綁定表達式賦值同诫。創(chuàng)建綁定的最簡單的方式,就是在它被inflating的時候樟澜。

@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);
}

完成误窖。另外,可以通過下面的方式來得到view:

MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());
View view = binding.getRoot();

如果你在一個ListView或者RecyclerView的adapter中使用數(shù)據(jù)綁定秩贰,推薦使用:

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

事件處理

Data Binding允許寫表達式來處理view事件(比如onClick)霹俺。事件屬性名和監(jiān)聽器(listener)方法一致(也有一些例外)。舉個栗子毒费,View.OnLongClickListener 有 onLongClick()方法丙唧,所以,該事件的屬性名為 android:onLongClick觅玻。屬性名所對應的想际,就是該事件的處理方法。下面介紹兩種方式來處理事件:

  • 方法引用(Method References):在表達式中溪厘,可以引用方法胡本,該方法應該遵守監(jiān)聽器方法簽名的特征(譯者:比如參數(shù)個數(shù)、參數(shù)類型畸悬、返回值類型侧甫、訪問限制等)。當一個表達式被認為是對一個方法的引用,Data Binding 將把這個引用方法和所有者對象包裹進一個監(jiān)聽器中披粟,并將該監(jiān)聽器設置到目標view上咒锻。
  • 監(jiān)聽綁定(Listener Bindings):當事件發(fā)生時,蘭姆達表達式將被執(zhí)行守屉。Data Binding會在view上創(chuàng)建一個監(jiān)聽器惑艇,當事件被分發(fā)下來,監(jiān)聽器將計算蘭姆達表達式拇泛。

方法引用

事件可以直接和事件處理器關聯(lián)綁定敦捧,就是android:onClick被指派到Activity中的onClick方法一樣。和View#onClick屬性相比碰镜,方法引用的優(yōu)勢在于兢卵,表達式將在編譯的時候進行處理,因此绪颖,如果方法不存在或者簽名不正確秽荤,將報編譯期錯誤。
方法引用和監(jiān)聽器綁定主要的不同在于柠横,方法引用中窃款,真正的監(jiān)聽的實現(xiàn)是在數(shù)據(jù)被綁定的時候,而不是在事件被觸發(fā)的時候牍氛。如果你更喜歡在事件發(fā)生的時候計算表達式晨继,應該是用監(jiān)聽綁定(listener binding)
為了給事件指定處理器handler搬俊,應該使用一般的綁定的表達式紊扬,它的值就是處理事件的方法名。舉個栗子

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

綁定表達式給View指定了一個點擊監(jiān)聽器:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.Handlers"/>
       <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:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

注意唉擂,表達式中方法的簽名應該和監(jiān)聽器對象中的方法簽名保持匹配餐屎。

監(jiān)聽綁定

監(jiān)聽表達式將在事件發(fā)生的時候綁定事件。這種綁定方式和方法引用類似玩祟,但是腹缩,這種綁定允許你運行任意的綁定表達式。這個屬性需要gradle版本2.0以上空扎。
在方法引用中藏鹊,方法的參數(shù)必須匹配事件監(jiān)聽器方法。在監(jiān)聽器綁定中转锈,僅僅只需要返回值和監(jiān)聽器方法的返回值匹配即可盘寡。舉個栗子

public class Presenter {
    public void onSaveClick(Task task){}
}

綁定點擊事件如下:

 <?xml version="1.0" encoding="utf-8"?>
  <layout xmlns:android="http://schemas.android.com/apk/res/android">
      <data>
          <variable name="task" type="com.android.example.Task" />
          <variable name="presenter" type="com.android.example.Presenter" />
      </data>
      <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
          <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
          android:onClick="@{() -> presenter.onSaveClick(task)}" />
      </LinearLayout>
  </layout>

監(jiān)聽器被表現(xiàn)為蘭姆達表達式,蘭姆達表達式必須作為整個表達式的根元素黑忱。當在表達式中使用回調(diào)函數(shù)宴抚,Data Binding將自動創(chuàng)建必要的監(jiān)聽并注冊在事件上。當view上的事件發(fā)生甫煞,Data Binding將計算給定的表達式菇曲。
注意,在上面的栗子中抚吠,我們并沒有定義要傳入到onClick(View view)中的view參數(shù)常潮。監(jiān)聽器綁定對于監(jiān)聽器參數(shù)提供了兩種選擇:如果不需要監(jiān)聽器(各種listener)參數(shù),你可以選擇忽略方法中的所有參數(shù)不寫楷力,如果需要使用時喊式,應該寫出使用所有監(jiān)聽器參數(shù)。舉個栗子:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"
android:onClick="@{() -> presenter.onSaveClick(task)}"

第一種寫了監(jiān)聽器參數(shù)萧朝,第二種沒有寫岔留,但是它們是等價的,因為在事實上检柬,處理方法onSavaClick中并沒有需要view献联。
如果你想在表達式中使用監(jiān)聽器參數(shù),可以這樣寫:

public class Presenter {
    public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

你可以在蘭姆達表達式中使用多個參數(shù):

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}
  <CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

如果監(jiān)聽函數(shù)沒有返回void何址,你的表達式要注意返回值的一致性.例如里逆,如果你想監(jiān)聽一個long click事件,你的表達式應該返回一個布爾值:

public class Presenter {
    public boolean onLongClick(View view, Task task){}
}
  android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果表達式因為null對象而不能進行計算用爪,Data Binding 會返回該類型下默認的Java值原押。對于引用類型是null,對于int就是0偎血,對于布爾值就是false诸衔。

Data Objects

任何普通對象(POJO)都可以用來進行數(shù)據(jù)綁定。但是修改一個POJO對象并不能觸發(fā)UI的更新颇玷。數(shù)據(jù)綁定真正的力量在于當數(shù)據(jù)更改時UI層能得到更新署隘。這里,將提供三種不同的數(shù)據(jù)更改通知機制:Observable objects, observable fields 和 ovservable collections.
當這些之中的一個任意一個observable 數(shù)據(jù)對象被綁定到 UI亚隙,當數(shù)據(jù)對象發(fā)生變化時磁餐,UI將得到自動更新。

Observable Objects

當一個類實現(xiàn)了Observable接口阿弃,這將
Observable接口有一種機制增添和刪除listener诊霹,但是,nofitying取決于開發(fā)者渣淳。為了簡化開發(fā)工作脾还,一個基類,BaseObservable被創(chuàng)建用來實現(xiàn)listener的注冊機制入愧。而數(shù)據(jù)類只需要負責當屬性發(fā)生的變化的時候進行通知鄙漏。這個做法嗤谚,通過對getter方法進行Bindable注解,在setter中進行通知來完成怔蚌。

private static class User extends BaseObservable {
   private String firstName;
   private String lastName;
   @Bindable
   public String getFirstName() {
       return this.firstName;
   }
   @Bindable
   public String getLastName() {
       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注解會在編輯期間產(chǎn)生進入BR類文件的入口巩步。BR類文件被創(chuàng)建在module package中。

ObservableFields

創(chuàng)建一個Observable對象需要做一些工作桦踊,因此椅野,如果開發(fā)者想要節(jié)省時間或者需要更改的屬性很少,就可以使用ObservableField籍胯。相似的竟闪,還有ObserableBoolean,ObserableByte,ObservableChar,ObservableShort,ObservableInt,ObserableLong,ObserableFloat,ObservableDouble,ObservableParcelable等。ObservableFields 是一種自包含的被觀察者對象杖狼,擁有一個field炼蛤。

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

為了訪問值,使用set和個體方法:

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

Observable Collections

有些應用使用更加動態(tài)的結(jié)構來持有數(shù)據(jù)蝶涩。Observable collections允許通過鍵值對訪問這些數(shù)據(jù)鲸湃。當鍵為引用類型時,ObservableArrayMap將是非常有用的子寓。比如暗挑,當key為字符串時:

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

在布局文件中,可以通過key來訪問數(shù)據(jù):

<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"/>

當key為Integer時斜友,ObservableArrayList是非常有用的:

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

在布局文件中炸裆,應該這樣通過索引訪問:

<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"/>
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鲜屏,隨后出現(xiàn)的幾起案子烹看,更是在濱河造成了極大的恐慌,老刑警劉巖洛史,帶你破解...
    沈念sama閱讀 212,657評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惯殊,死亡現(xiàn)場離奇詭異,居然都是意外死亡也殖,警方通過查閱死者的電腦和手機土思,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,662評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忆嗜,“玉大人己儒,你說我怎么就攤上這事±粒” “怎么了闪湾?”我有些...
    開封第一講書人閱讀 158,143評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绩卤。 經(jīng)常有香客問我途样,道長江醇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,732評論 1 284
  • 正文 為了忘掉前任何暇,我火速辦了婚禮陶夜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赖晶。我一直安慰自己,他們只是感情好辐烂,可當我...
    茶點故事閱讀 65,837評論 6 386
  • 文/花漫 我一把揭開白布遏插。 她就那樣靜靜地躺著,像睡著了一般纠修。 火紅的嫁衣襯著肌膚如雪胳嘲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,036評論 1 291
  • 那天扣草,我揣著相機與錄音了牛,去河邊找鬼。 笑死辰妙,一個胖子當著我的面吹牛鹰祸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播密浑,決...
    沈念sama閱讀 39,126評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼蛙婴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尔破?” 一聲冷哼從身側(cè)響起街图,我...
    開封第一講書人閱讀 37,868評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎懒构,沒想到半個月后餐济,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,315評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡胆剧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,641評論 2 327
  • 正文 我和宋清朗相戀三年絮姆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片秩霍。...
    茶點故事閱讀 38,773評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡滚朵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出前域,到底是詐尸還是另有隱情辕近,我是刑警寧澤,帶...
    沈念sama閱讀 34,470評論 4 333
  • 正文 年R本政府宣布匿垄,位于F島的核電站移宅,受9級特大地震影響归粉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜漏峰,卻給世界環(huán)境...
    茶點故事閱讀 40,126評論 3 317
  • 文/蒙蒙 一糠悼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浅乔,春花似錦倔喂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,859評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至贤壁,卻和暖如春悼枢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脾拆。 一陣腳步聲響...
    開封第一講書人閱讀 32,095評論 1 267
  • 我被黑心中介騙來泰國打工馒索, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人名船。 一個月前我還...
    沈念sama閱讀 46,584評論 2 362
  • 正文 我出身青樓绰上,卻偏偏與公主長得像,于是被迫代替她去往敵國和親渠驼。 傳聞我的和親對象是個殘疾皇子渔期,可洞房花燭夜當晚...
    茶點故事閱讀 43,676評論 2 351

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