導(dǎo)航:
- 搭建環(huán)境
- 數(shù)據(jù)綁定編譯器V2
- 數(shù)據(jù)綁定布局文件
- 布局細(xì)節(jié)
- 數(shù)據(jù)對(duì)象(重點(diǎn))
- 生成綁定
- 屬性setters
- 轉(zhuǎn)換器
- Android Studio支持?jǐn)?shù)據(jù)綁定
本文檔解釋了如何使用數(shù)據(jù)綁定庫(kù)來(lái)編寫(xiě)聲明式布局喷面,并盡量減少綁定應(yīng)用程序邏輯和布局所需的膠合代碼。
數(shù)據(jù)綁定庫(kù)提供了靈活性和廣泛的兼容性 - 這是一個(gè)支持庫(kù)腹备,所以你可以在Android 2.1(API級(jí)別7+)的所有Android平臺(tái)上使用它溃肪。
要使用數(shù)據(jù)綁定救湖,Android Plugin for Gradle 1.5.0-alpha1 或更高版本是必需的。了解如何更新Android Plugin for Gradle艾疟。
1.配置環(huán)境
要開(kāi)始使用數(shù)據(jù)綁定押框,請(qǐng)從Android SDK管理器的支持庫(kù)中下載庫(kù)。
要配置應(yīng)用程序以使用數(shù)據(jù)綁定尿背,請(qǐng)將dataBinding
元素添加到應(yīng)用程序模塊中的build.gradle
文件中端仰。
使用下面的代碼片段來(lái)配置數(shù)據(jù)綁定:
android {
....
dataBinding {
enabled = true
}
}
如果您的應(yīng)用程序模塊依賴于使用數(shù)據(jù)綁定的庫(kù),則您的應(yīng)用程序模塊也必須在其build.gradle
配置數(shù)據(jù)綁定残家。
另外榆俺,請(qǐng)確保您使用的是Android Studio的兼容版本售躁。Android Studio 1.3及更高版本支持?jǐn)?shù)據(jù)綁定坞淮,如Android Studio支持?jǐn)?shù)據(jù)綁定中所述。
2.數(shù)據(jù)綁定編譯器V2
Android Gradle插件3.1.0 Canary 6附帶一個(gè)可選的新編譯器陪捷。要開(kāi)始使用它回窘,請(qǐng)更新您的gradle.properties文件以包含以下行:
android.databinding.enableV2=true
在編譯器v2中:
-
ViewBinding
類是在java編譯器之前由Android Gradle Plugin生成的。這可以避免由于不相關(guān)的原因?qū)е耲ava編譯失敗而導(dǎo)致太多的錯(cuò)誤肯定錯(cuò)誤市袖。 - 在V1中啡直,編譯應(yīng)用程序時(shí)會(huì)重新生成庫(kù)的綁定類(以共享生成的代碼并訪問(wèn)最終的“BR”和“R”文件)烁涌。在V2中,庫(kù)保持其生成的綁定類以及映射器信息酒觅,這為多模塊項(xiàng)目顯著提高了數(shù)據(jù)綁定性能撮执。
請(qǐng)注意,這個(gè)新的編譯器是向后不兼容的舷丹,所以用v1編譯的庫(kù)不能被v2使用抒钱,反之亦然。
V2還會(huì)刪除一些很少使用的功能來(lái)允許這些更改:
- 在V1中颜凯,一個(gè)應(yīng)用程序能夠提供綁定適配器谋币,可以覆蓋依賴項(xiàng)中的適配器。在V2中症概,它只對(duì)自己的模塊/應(yīng)用程序及其依賴項(xiàng)中的代碼生效蕾额。
- 以前,如果一個(gè)布局文件在兩個(gè)或多個(gè)不同的資源配置中包含一個(gè)
View
具有相同id
但不同類的數(shù)據(jù)彼城,則數(shù)據(jù)綁定將查找最常見(jiàn)的父類诅蝶。View當(dāng)配置之間的類型不匹配時(shí),它將始終默認(rèn)為精肃。 - 在V2中秤涩,不同的模塊不能在清單中使用相同的包名稱,因?yàn)閿?shù)據(jù)綁定將使用該包名來(lái)生成綁定映射器類司抱。
3.數(shù)據(jù)綁定布局文件
3.1編寫(xiě)你的第一套數(shù)據(jù)綁定表達(dá)式
數(shù)據(jù)綁定布局文件稍有不同筐眷,layout
作為根標(biāo)簽,后跟data
元素和 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
標(biāo)簽中的user
變量,描述了可以在布局中使用的屬性匀谣。
<variable name="user" type="com.example.User"/>
布局中的使用@{}
語(yǔ)法表達(dá)式寫(xiě)入屬性。在這里资溃,TextView的文本被設(shè)置為用戶的firstName屬性:
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName}"/>
Data Object
3.2 數(shù)據(jù)對(duì)象
現(xiàn)在讓我們假設(shè)你有一個(gè)普通的Java對(duì)象(PO??JO)用戶:
public class User {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
這種類型的對(duì)象的數(shù)據(jù)永遠(yuǎn)不會(huì)改變武翎。在應(yīng)用程序中通常會(huì)讀取一次數(shù)據(jù),之后再也不會(huì)更改溶锭。也可以使用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;
}
}
從數(shù)據(jù)綁定的角度來(lái)看宝恶,這兩個(gè)類是等價(jià)的。@{user.firstName}
用于TextView android:text
屬性的表達(dá)式將訪問(wèn)前一類中的firstName
字段和后一類中的getFirstName()
方法趴捅〉姹校或者,如果firstName()
方法存在拱绑,也將被解析综芥。
3.3 綁定數(shù)據(jù)
默認(rèn)情況下,將根據(jù)布局文件的名稱生成一個(gè)Binding類猎拨,將其轉(zhuǎn)換為Pascal格式并將Binding
后綴添加到該文件中膀藐。上面的布局文件是main_activity.xml
這樣的生成類是MainActivityBinding
屠阻。這個(gè)類將布局屬性(例如user
變量)的所有綁定保存到布局的視圖中,并知道如何為綁定表達(dá)式賦值额各。創(chuàng)建綁定的最簡(jiǎn)單方法是在inflating
時(shí)進(jìn)行:
@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)行應(yīng)用程序,你會(huì)看到用戶界面中的測(cè)試user
虾啦◎燃樱或者,您可以通過(guò)以下方式獲取視圖:
MainActivityBinding binding =
MainActivityBinding.inflate(getLayoutInflater());
如果您在ListView或RecyclerView適配器內(nèi)使用數(shù)據(jù)綁定項(xiàng)目缸逃,則可能更愿意使用:
ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
//or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);
Event Handling
3.4 事件處理
數(shù)據(jù)綁定允許您編寫(xiě)表達(dá)式來(lái)處理從視圖中分派的事件(例如onClick)针饥。除少數(shù)例外,事件屬性名稱由偵聽(tīng)器方法的名稱來(lái)管理需频。View.OnLongClickListener 有一個(gè)方法 onLongClick()丁眼,所以這個(gè)事件的屬性是android:onLongClick
。處理事件有兩種方法昭殉。
- 方法引用:在你的表達(dá)式中苞七,你可以引用符合偵聽(tīng)器方法簽名的方法。當(dāng)表達(dá)式評(píng)估為方法引用時(shí)挪丢,數(shù)據(jù)綁定將方法引用和所有者對(duì)象包裝在偵聽(tīng)器中蹂风,并將該偵聽(tīng)器設(shè)置在目標(biāo)視圖上。如果表達(dá)式求值為null乾蓬,則數(shù)據(jù)綁定不會(huì)創(chuàng)建偵聽(tīng)器惠啄,而是設(shè)置空偵聽(tīng)器。
- 監(jiān)聽(tīng)器綁定:這些是在事件發(fā)生時(shí)被評(píng)估的lambda表達(dá)式任内。數(shù)據(jù)綁定總是創(chuàng)建一個(gè)監(jiān)聽(tīng)器撵渡,它在視圖上設(shè)置。事件發(fā)送時(shí)死嗦,監(jiān)聽(tīng)器評(píng)估lambda表達(dá)式趋距。
3.4.1方法引用
事件可以直接綁定到處理方法,類似于 android:onClick
可以分配給Activity中的方法越除。與View#onClick
屬性相比节腐,一個(gè)主要的優(yōu)點(diǎn)是表達(dá)式在編譯時(shí)被處理,所以如果方法不存在或者它的簽名不正確摘盆,你會(huì)收到一個(gè)編譯時(shí)錯(cuò)誤翼雀。
方法引用和監(jiān)聽(tīng)器綁定的主要區(qū)別在于實(shí)際的監(jiān)聽(tīng)器實(shí)現(xiàn)是在綁定數(shù)據(jù)時(shí)創(chuàng)建的,而不是在事件觸發(fā)時(shí)創(chuàng)建的骡澈。如果您喜歡在事件發(fā)生時(shí)評(píng)估表達(dá)式锅纺,則應(yīng)該使用監(jiān)聽(tīng)器綁定掷空。
要將事件分配給其處理程序肋殴,請(qǐng)使用常規(guī)綁定表達(dá)式囤锉,其值是要調(diào)用的方法名稱。例如护锤,如果你的數(shù)據(jù)對(duì)象有兩個(gè)方法:
public class MyHandlers {
public void onClickFriend(View view) { ... }
}
綁定表達(dá)式可以為View分配一個(gè)點(diǎn)擊監(jiān)聽(tīng)器:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable name="handlers" type="com.example.MyHandlers"/>
<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>
請(qǐng)注意官地,表達(dá)式中方法的簽名必須與Listener對(duì)象中方法的簽名完全匹配
3.4.2監(jiān)聽(tīng)器綁定
監(jiān)聽(tīng)器綁定是事件發(fā)生時(shí)運(yùn)行的綁定表達(dá)式。它們類似于方法引用烙懦,但是它們?cè)试S您運(yùn)行任意的數(shù)據(jù)綁定表達(dá)式驱入。此功能適用于Gradle 2.0版及更高版本的Android Gradle插件。
在方法引用中氯析,方法的參數(shù)必須與事件偵聽(tīng)器的參數(shù)匹配亏较。在監(jiān)聽(tīng)器綁定中,只有你的返回值必須與監(jiān)聽(tīng)器的期望返回值相匹配(除非它預(yù)期為void)掩缓。例如雪情,您可以有一個(gè)具有以下方法的演示者類:
public class Presenter {
public void onSaveClick(Task task){}
}
然后你可以如下綁定點(diǎn)擊事件:
<?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)聽(tīng)器僅允許只允許由lambda表達(dá)式作為根元素的表達(dá)式。在表達(dá)式中使用回調(diào)函數(shù)時(shí)你辣,數(shù)據(jù)綁定會(huì)自動(dòng)為事件創(chuàng)建必要的偵聽(tīng)器和注冊(cè)表巡通。當(dāng)視圖觸發(fā)事件時(shí),數(shù)據(jù)綁定將評(píng)估給定的表達(dá)式舍哄。就像在常規(guī)的綁定表達(dá)式中一樣宴凉,當(dāng)這些監(jiān)聽(tīng)器表達(dá)式被評(píng)估的時(shí)候,你仍然可以獲得數(shù)據(jù)綁定的null和線程安全性表悬。
請(qǐng)注意弥锄,在上面的例子中,我們沒(méi)有定義view
傳入?yún)?shù)onClick(android.view.View)蟆沫。監(jiān)聽(tīng)器綁定為監(jiān)聽(tīng)器參數(shù)提供了兩個(gè)選擇:您可以忽略該方法的所有參數(shù)或?qū)⑵淙棵婕ァH绻胍麉?shù),則可以在表達(dá)式中使用它們饥追。例如图仓,上面的表達(dá)式可以寫(xiě)成:
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
或者如果你想使用表達(dá)式中的參數(shù),它可以如下工作:
public class Presenter {
public void onSaveClick(View view, Task task){}
}
---
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
您可以使用多于一個(gè)參數(shù)的lambda表達(dá)式:
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)}" />
如果正在偵聽(tīng)的事件返回一個(gè)其類型不是void
的值但绕,則表達(dá)式必須返回相同類型的值救崔。例如,如果要監(jiān)聽(tīng)長(zhǎng)按事件捏顺,則應(yīng)該返回表達(dá)式boolean
六孵。
public class Presenter {
public boolean onLongClick(View view, Task task){}
}
--------
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
如果由于是null
對(duì)象而無(wú)法評(píng)估表達(dá)式,Data Binding將返回該類型的默認(rèn)Java值幅骄。例如劫窒,null
返回引用類型,0
返回 int
拆座, false
返回 boolean
等主巍。
如果您需要使用謂詞(例如三元)表達(dá)式冠息,則可以將 void
用作符號(hào)。
android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
避免復(fù)雜的監(jiān)聽(tīng)
監(jiān)聽(tīng)器表達(dá)式非常強(qiáng)大孕索,可以讓你的代碼非常容易閱讀逛艰。另一方面,包含復(fù)雜表達(dá)式的監(jiān)聽(tīng)會(huì)使您的布局難以閱讀和維護(hù)搞旭。這些表達(dá)式應(yīng)該像從UI中傳遞可用數(shù)據(jù)到回調(diào)方法一樣簡(jiǎn)單散怖。您應(yīng)該在您從偵聽(tīng)器表達(dá)式調(diào)用的回調(diào)方法內(nèi)實(shí)現(xiàn)任何業(yè)務(wù)邏輯。
存在一些專門的單擊事件處理程序肄渗,它們需要一個(gè)屬性镇眷, android:onClick
以避免沖突。已經(jīng)創(chuàng)建了以下屬性以避免這種沖突:
class | Listener Setter | Attribute |
---|---|---|
SearchView | setOnSearchClickListener(View.OnClickListener) | android:onSearchClick |
ZoomControls | setOnZoomInClickListener(View.OnClickListener) | android:onZoomIn |
ZoomControls | setOnZoomOutClickListener(View.OnClickListener) | android:onZoomOut |
4.布局細(xì)節(jié)
4.1 import
data
元素中可以使用零個(gè)或多個(gè)import
元素,這些就像在Java中一樣可以輕松地引用布局文件中的類翎嫡。
<data>
<import type="android.view.View"/>
</data>
現(xiàn)在偏灿,可以在你的綁定表達(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)有類名沖突時(shí),其中一個(gè)類可能會(huì)被重命名為“alias:”
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
現(xiàn)在钝的,Vista
可能會(huì)以com.example.real.estate.View
翁垂,View
可能以android.view.View
在布局文件內(nèi)引用。導(dǎo)入的類型可以用作變量和表達(dá)式中的類型引用:
<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尚未處理導(dǎo)入硝桩,因此導(dǎo)入變量的自動(dòng)填充可能無(wú)法在您的IDE中工作沿猜。您的應(yīng)用程序仍然可以正常編譯,您可以通過(guò)在變量定義中使用完全限定的名稱來(lái)解決IDE問(wèn)題碗脊。
<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
當(dāng)在表達(dá)式中引用靜態(tài)字段和方法時(shí)啼肩,也可以使用導(dǎo)入的類型:
<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
元素內(nèi)可以使用任意數(shù)量的variable
元素祈坠,每個(gè)variable
元素描述可以在布局上設(shè)置的屬性,以用于布局文件中的綁定表達(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>
變量類型在編譯時(shí)被檢查赦拘,所以如果一個(gè)變量實(shí)現(xiàn)Observable或者是一個(gè)observable collection,那么這個(gè)類型應(yīng)該被反映出來(lái)芬沉。如果變量是不實(shí)現(xiàn)Observable
接口的基類或接口躺同,將不會(huì)觀察變量!
當(dāng)不同的配置文件(例如橫向或縱向)有不同的布局文件時(shí)丸逸,變量將被合并蹋艺。這些布局文件之間不得存在沖突的變量定義。
生成的綁定類將為每個(gè)描述的變量設(shè)置一個(gè)setter和getter黄刚。變量將采用默認(rèn)的Java值捎谨,直到調(diào)用者被調(diào)用 - null
是引用類型,0
是 int
,false
是boolean
等涛救。
根據(jù)需要生成一個(gè)名為context
的特殊變量用于綁定表達(dá)式畏邢。context
是視圖的getContext()得到的Context
。context
變量將被具有該名稱的顯式變量聲明覆蓋州叠。
4.3自定義綁定類名稱
默認(rèn)情況下,根據(jù)布局文件的名稱生成一個(gè)Binding類凶赁,以大寫(xiě)字母開(kāi)頭咧栗,刪除下劃線(_)并大寫(xiě)下面的字母,然后后綴“Binding”虱肄。這個(gè)類將被放置在模塊包下的數(shù)據(jù)綁定包中致板。例如,布局文件contact_item.xml
將生成 ContactItemBinding
咏窿。如果模塊包是 com.example.my.app
斟或,那么它將被放入com.example.my.app.databinding
。
綁定類可以通過(guò)調(diào)整class
元素的屬性來(lái)重命名或放置data
在不同的包中集嵌。
<data class="ContactItem">
...
</data>
這將在模塊包中的數(shù)據(jù)綁定包中生成綁定類ContactItem
萝挤。如果該類應(yīng)該在模塊包中的其他包中生成,則可以用.
作為前綴:
<data class=".ContactItem">
...
</data>
在這種情況下根欧,ContactItem
直接在模塊包中生成怜珍。如果提供完整的軟件包,則可以在任何軟件包中:
<data class="com.example.ContactItem">
...
</data>
4.4 Includes
通過(guò)使用應(yīng)用程序名稱空間和屬性的變量名凤粗,變量可以被傳遞到包含布局的包含綁定中:
<?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è)user
變量。
數(shù)據(jù)綁定不支持include
作為merge
元素的直接子元素嫌拣。例如柔袁,不支持以下布局:
<?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>
<merge>
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</merge>
</layout>
4.5 1. 表達(dá)式語(yǔ)言
共同特征
表達(dá)式語(yǔ)言看起來(lái)很像Java表達(dá)式。這些是一樣的:
- 數(shù)學(xué)的:
+ - / * %
- 字符串連接:
+
- 邏輯:
&& ||
- 二進(jìn)制:
& | ^
- 一元:
+ - ! ~
- 轉(zhuǎn)移:
>> >>> <<
- 對(duì)照:
== > < >= <=
instanceof
- 分組:
()
- 文字 - 字符异逐,字符串捶索,數(shù)字,
null
- Cast
- 方法調(diào)用
- 字段訪問(wèn)
- 數(shù)組訪問(wèn)
[]
- 三元操作符
?:
例子:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}
不支持的操作
您可以在Java中使用的表達(dá)式語(yǔ)法中缺少一些操作灰瞻。
this
super
new
- 明確的泛型調(diào)用
空合并運(yùn)算符
null合并運(yùn)算符(??
)選擇左操作數(shù)(如果不是null)或右(如果為空)情组。
android:text="@{user.displayName ?? user.lastName}"
這在功能上等同于:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
屬性引用
第一個(gè)已經(jīng)在上面的編寫(xiě)第一個(gè)數(shù)據(jù)綁定表達(dá)式中討論過(guò)了:簡(jiǎn)短形式的JavaBean引用。當(dāng)一個(gè)表達(dá)式引用一個(gè)類的屬性時(shí)箩祥,它對(duì)字段院崇,獲取器和ObservableFields使用相同的格式。
android:text="@{user.lastName}"
避免空指針異常
生成的數(shù)據(jù)綁定代碼自動(dòng)檢查空值并避免空指針異常袍祖。例如底瓣,在 @{user.name}
表達(dá)式中如果user
為null, user.name
將被賦予其默認(rèn)值(null)。如果你是引用user.age
捐凭,年齡是一個(gè)int
拨扶,那么它將默認(rèn)為0。
集合
公共集合:數(shù)組茁肠,列表患民,稀疏列表和地圖,為了方便可以使用[]
操作符垦梆。
<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]}"
字符串文字
在屬性值周圍使用單引號(hào)時(shí)匹颤,在表達(dá)式中使用雙引號(hào)很容易:
android:text='@{map["firstName"]}'
也可以使用雙引號(hào)來(lái)包圍屬性值。這樣做時(shí)托猩,字符串文字應(yīng)該使用'
或者反引號(hào)( ` )印蓖。
android:text="@{map[`firstName`}"
android:text="@{map['firstName']}"
資源
使用正常語(yǔ)法可以將資源作為表達(dá)式的一部分進(jìn)行訪問(wèn):
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
格式字符串和復(fù)數(shù)可以通過(guò)提供參數(shù)來(lái)評(píng)估:
android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"
當(dāng)一個(gè)復(fù)數(shù)有多個(gè)參數(shù)時(shí),所有參數(shù)都應(yīng)該傳遞:
Have an orange
Have %d oranges
android:text="@{@plurals/orange(orangeCount, orangeCount)}"
有些資源需要明確的類型評(píng)估京腥。
Type | Normal Reference | Expression Reference |
---|---|---|
String[] | @array | @stringArray |
int[] | @array | @intArray |
TypedArray | @array | @typedArray |
Animator | @animator | @animator |
StateListAnimator | @animator | @stateListAnimator |
color int | @color | @color |
ColorStateList | @color | @colorStateList |
5.數(shù)據(jù)對(duì)象
任何普通的舊Java對(duì)象(PO??JO)都可以用于數(shù)據(jù)綁定赦肃,但修改POJO不會(huì)導(dǎo)致UI更新。數(shù)據(jù)綁定的真正威力可以通過(guò)給你的數(shù)據(jù)對(duì)象提供在數(shù)據(jù)改變時(shí)通知的能力公浪。有三種不同的數(shù)據(jù)更改通知機(jī)制: 可觀察對(duì)象他宛, 可觀察字段和 可觀察集合。
當(dāng)這些可觀察的數(shù)據(jù)對(duì)象之一被綁定到UI并且數(shù)據(jù)對(duì)象的屬性改變時(shí)欠气,UI將被自動(dòng)更新堕汞。
5.1可觀察的對(duì)象
實(shí)現(xiàn)Observable接口的類將允許綁定將單個(gè)偵聽(tīng)器附加到綁定對(duì)象,以偵聽(tīng)該對(duì)象上所有屬性的更改晃琳。
Observable接口具有添加和刪除偵聽(tīng)器的機(jī)制讯检,但通知由開(kāi)發(fā)人員決定。為了簡(jiǎn)化開(kāi)發(fā)卫旱,創(chuàng)建了一個(gè)BaseObservable基類來(lái)實(shí)現(xiàn)監(jiān)聽(tīng)器注冊(cè)機(jī)制人灼。數(shù)據(jù)類實(shí)現(xiàn)者仍然負(fù)責(zé)通知屬性何時(shí)更改,通過(guò)分配一個(gè)Bindable注釋給getter并且在setter中使用通知來(lái)完成的顾翼。
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注解在BR類文件中生成一個(gè)條目投放。而B(niǎo)R類文件將在模塊包中生成。如果數(shù)據(jù)類的基類不能改變适贸,那么Observable將會(huì)被實(shí)現(xiàn)灸芳,通過(guò)使用PropertyChangeRegistry去存儲(chǔ)和有效地通知偵聽(tīng)器。
5.2 可觀察字段
Observable類大有內(nèi)涵拜姿,所以開(kāi)發(fā)者想要節(jié)省時(shí)間和添加有幾個(gè)屬性烙样,可以使用ObservableField和它的兄弟姐妹
ObservableBoolean,ObservableByte蕊肥,ObservableChar谒获,ObservableShort,ObservableInt,ObservableLong批狱,ObservableFloat裸准,ObservableDouble,和ObservableParcelable赔硫。
ObservableFields
是具有單個(gè)字段的自包含可觀察對(duì)象炒俱。原始版本在訪問(wèn)操作期間避免裝箱和取消裝箱。要使用爪膊,請(qǐng)?jiān)跀?shù)據(jù)類中創(chuàng)建一個(gè)公共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();
}
就這么簡(jiǎn)單权悟!要訪問(wèn)該值,請(qǐng)使用set和get訪問(wèn)器方法:
user.firstName.set("Google");
int age = user.age.get();
5.3 可觀察的集合
一些應(yīng)用程序使用更多的動(dòng)態(tài)結(jié)構(gòu)來(lái)保存數(shù)據(jù),可觀察集合允許對(duì)這些數(shù)據(jù)對(duì)象進(jìn)行鍵控訪問(wèn)惊完。當(dāng)鍵是引用類型(如String)時(shí)ObservableArrayMap非常有用僵芹。
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
在布局中处硬,可以通過(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"/>
當(dāng)鍵是一個(gè)整數(shù)時(shí)使用ObservableArrayList:
ObservableArrayList<Object> user = new ObservableArrayList<>();
user.add("Google");
user.add("Inc.");
user.add(17);
在布局中小槐,可以通過(guò)索引來(lái)訪問(wèn)列表:
<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.生成的綁定
生成的綁定類將布局變量與布局中的視圖鏈接起來(lái)。如前所述荷辕,綁定的名稱和包可能是 自定義的凿跳。生成的綁定類全部擴(kuò)展ViewDataBinding。
6.1創(chuàng)建
應(yīng)該在inflation
之后立即創(chuàng)建綁定疮方,以確保在綁定到布局中帶有表達(dá)式的視圖之前控嗜,View層次結(jié)構(gòu)不受干擾。有幾種方法可以綁定到布局骡显。最常見(jiàn)的是在Binding類中使用靜態(tài)方法疆栏。inflate
方法inflate
了View層次結(jié)構(gòu),并將其一步綁定惫谤。有一個(gè)簡(jiǎn)單的版本壁顶,只需要一個(gè)LayoutInflater和ViewGroup:
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
如果布局使用不同的inflate
機(jī)制,它可能會(huì)被分開(kāi)綁定:
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
有時(shí)綁定不能預(yù)先知道溜歪。在這種情況下若专,綁定可以使用DataBindingUtil類創(chuàng)建:
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
6.2帶有ID的視圖
每個(gè)帶有ID的視圖將在布局中生成一個(gè)對(duì)應(yīng)的公開(kāi)fianl
字段。該綁定在View層次結(jié)構(gòu)上執(zhí)行單個(gè)傳遞蝴猪,提取帶有ID的視圖调衰。這個(gè)機(jī)制可以比調(diào)用多個(gè)視圖的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ì)生成一個(gè)綁定類:
public final TextView firstName;
public final TextView lastName;
ID在數(shù)據(jù)綁定時(shí)不是必須的自阱,但是仍然有一些情況下代碼仍然需要訪問(wèn)視圖嚎莉。
6.3變量
每個(gè)變量將被賦予訪問(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>
將在綁定中生成setter和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.4ViewStubs
ViewStub與正常view
有些不同沛豌。他們從不可見(jiàn)的時(shí)候開(kāi)始萝喘,當(dāng)他們要么變得可見(jiàn)時(shí),或者被明確地告知inflate
時(shí),他們通過(guò)inflate
另一種布局來(lái)取代布局阁簸。
由于ViewStub
本質(zhì)上從View層次消失爬早,綁定對(duì)象中的View也必須消失以允許收集。因?yàn)?code>view是最終的启妹,所以一個(gè)ViewStubProxy對(duì)象代替了這個(gè)ViewStub
視圖筛严,當(dāng)開(kāi)發(fā)者存在的時(shí)候,它允許開(kāi)發(fā)人員訪問(wèn)ViewStub饶米,并且在ViewStub
被inflate
時(shí)也可以訪問(wèn)inflate
的View層次結(jié)構(gòu)桨啃。
當(dāng)inflate
另一個(gè)布局時(shí),必須為新的布局建立綁定檬输。因此照瘾,ViewStubProxy
一定監(jiān)聽(tīng)ViewStub
的ViewStub.OnInflateListener,同時(shí)建立綁定丧慈。由于只有一個(gè)可以存在遗嗽,所以ViewStubProxy
允許開(kāi)發(fā)者設(shè)置一個(gè)OnInflateListener
肺然,在建立綁定之后它將被調(diào)用
6.5高級(jí)綁定
動(dòng)態(tài)變量
有時(shí),特定的綁定類是不知道的,例如氮昧,RecyclerView.Adapter針對(duì)任意布局的操作將不知道具體的綁定類臭墨。它仍然必須分配綁定值onBindViewHolder(VH, int)
赘来。
在這個(gè)例子中旭贬,RecyclerView綁定的所有布局都有一個(gè)“item”變量。所述的BindingHolder
具有getBinding
方法返回基礎(chǔ)的ViewDataBinding吟税。
public void onBindViewHolder(BindingHolder holder, int position) {
final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
6.6 立即綁定
當(dāng)變量或可觀察對(duì)象變化時(shí)凹耙,綁定將被安排在下一幀之前改變。但有時(shí)候肠仪,綁定必須立即執(zhí)行肖抱。要強(qiáng)制執(zhí)行,請(qǐng)使用該executePendingBindings()方法藤韵。
6.7后臺(tái)線程
只要不是集合虐沥,就可以在后臺(tái)線程中更改數(shù)據(jù)模型。數(shù)據(jù)綁定將在評(píng)估時(shí)本地化每個(gè)變量/字段泽艘,以避免任何并發(fā)問(wèn)題欲险。
7. 屬性Setters
每當(dāng)綁定值發(fā)生變化時(shí),生成的綁定類必須使用綁定表達(dá)式在視圖上調(diào)用setter方法匹涮。數(shù)據(jù)綁定框架可以自定義調(diào)用哪個(gè)方法來(lái)設(shè)置值天试。
7.1 自動(dòng) Setters
對(duì)于一個(gè)屬性,數(shù)據(jù)綁定試圖找到方法setAttribute然低。屬性的命名空間并不重要喜每,只有屬性名稱本身才是重點(diǎn)务唐。
例如,與TextView屬性關(guān)聯(lián)的表達(dá)式 android:text
將查找setText(String)带兜。如果表達(dá)式返回一個(gè)int枫笛,那么數(shù)據(jù)綁定將搜索一個(gè)setText(int)方法。請(qǐng)注意讓表達(dá)式返回正確的類型刚照,如果需要的話就進(jìn)行轉(zhuǎn)換刑巧。請(qǐng)注意,即使給定名稱不存在任何屬性无畔,數(shù)據(jù)綁定也可以工作啊楚。然后,您可以使用數(shù)據(jù)綁定輕松地為任何‘創(chuàng)建’的屬性進(jìn)行setter浑彰。例如恭理,support DrawerLayout沒(méi)有任何屬性,但是有很多setter郭变。您可以使用自動(dòng)設(shè)置器來(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
方法的屬性與名稱不符合。對(duì)于這些方法饵较,屬性可能通過(guò)BindingMethods注釋與setter
相關(guān)聯(lián)拍嵌。這必須與一個(gè)類相關(guān)聯(lián)遭赂,并包含BindingMethod注釋循诉,每個(gè)重命名的方法一個(gè)。例如撇他,android:tint
屬性確實(shí)與setImageTintList(ColorStateList)關(guān)聯(lián)茄猫,而不是 setTint
。
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
開(kāi)發(fā)人員不太可能需要重命名setter;android框架的屬性已經(jīng)實(shí)現(xiàn)了困肩。
7.3自定義setters
一些屬性需要自定義綁定邏輯划纽。例如,android:paddingLeft
屬性沒(méi)有關(guān)聯(lián)的setter
锌畸。相反勇劣,setPadding(left, top, right, bottom)
存在。帶BindingAdapter注釋的靜態(tài)綁定適配器方法,允許開(kāi)發(fā)人員定制如何調(diào)用屬性的setter潭枣。
Android屬性已經(jīng)創(chuàng)建BindingAdapter
比默。例如,這里是一個(gè)用于paddingLeft
:
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
綁定適配器對(duì)其他類型的自定義非常有用盆犁。例如命咐,一個(gè)自定義的加載器可以被調(diào)用脫機(jī)線程來(lái)加載一個(gè)圖像。
當(dāng)發(fā)生沖突時(shí)谐岁,開(kāi)發(fā)人員創(chuàng)建的綁定適配器將覆蓋數(shù)據(jù)綁定默認(rèn)適配器醋奠。
您也可以讓適配器接收多個(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}"/>
如果imageUrl和 error都用于ImageView且imageUrl是字符串,并且error是drawable
窜司,則將調(diào)用此適配器沛善。
- 自定義名稱空間在匹配過(guò)程中被忽略。
- 您也可以為android命名空間編寫(xiě)適配器塞祈。
綁定適配器方法可以選擇性的在其處理程序中使用舊值路呜。采用新舊價(jià)值的方法,首先應(yīng)該擁有屬性的舊值织咧,其次是新的值:
@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());
}
}
事件處理程序只能用于接口或一個(gè)有抽象方法的抽象類胀葱。例如:
@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);
}
}
}
當(dāng)一個(gè)監(jiān)聽(tīng)器有多個(gè)方法時(shí),它必須被分成多個(gè)監(jiān)聽(tīng)器笙蒙。比如View.OnAttachStateChangeListener有兩種方法:onViewAttachedToWindow()和onViewDetachedFromWindow()抵屿。然后我們必須創(chuàng)建兩個(gè)接口來(lái)區(qū)分它們的屬性和處理程序。
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
void onViewAttachedToWindow(View v);
}
因?yàn)楦囊粋€(gè)偵聽(tīng)器也會(huì)影響另一個(gè)偵聽(tīng)器捅位,所以我們必須有三個(gè)不同的綁定適配器轧葛,一個(gè)用于每個(gè)屬性,另一個(gè)用于兩個(gè)艇搀,如果它們都被設(shè)置尿扯。
@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);
}
}
}
上面的例子比正常情況稍微復(fù)雜,因?yàn)閂iew使用add和remove來(lái)代替View.OnAttachStateChangeListener的set方法android.databinding.adapters.ListenerUtil
類可以幫助跟蹤以前的監(jiān)聽(tīng)焰雕,讓他們可以在綁定Adaper中被刪除的衷笋。
通過(guò)用@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
注釋OnViewDetachedFromWindow
和OnViewAttachedToWindow
接口,數(shù)據(jù)綁定代碼生成器知道監(jiān)聽(tīng)器只在Honeycomb MR1和新設(shè)備生成矩屁;同樣的情況在addOnAttachStateChangeListener(View.OnAttachStateChangeListener)上發(fā)生辟宗。
8. 轉(zhuǎn)換器
8.1對(duì)象轉(zhuǎn)換
從綁定表達(dá)式返回一個(gè)對(duì)象時(shí),將從自動(dòng)吝秕,重命名和自定義setter
中選擇一個(gè)setter
泊脐。該對(duì)象將被轉(zhuǎn)換為所選setter的參數(shù)類型。
這對(duì)于那些使用ObservableMaps
來(lái)保存數(shù)據(jù)的人來(lái)說(shuō)非常方便烁峭。例如:
<TextView
android:text='@{userMap["lastName"]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
將返回一個(gè)userMap
對(duì)象容客,并且將對(duì)象自動(dòng)轉(zhuǎn)換為setText(CharSequence)
中的參數(shù)類型。當(dāng)參數(shù)類型可能混淆時(shí)约郁,開(kāi)發(fā)人員需要在表達(dá)式中輸入缩挑。
8.2 自定義轉(zhuǎn)換
有時(shí)轉(zhuǎn)換應(yīng)該在特定類型之間自動(dòng)進(jìn)行。例如棍现,設(shè)置背景時(shí):
<View
android:background="@{isError ? @color/red : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
在這里调煎,背景需要一個(gè)Drawable
,但顏色是一個(gè)integer
己肮。不管期望 Drawable
或是返回一個(gè)integer
士袄,int
應(yīng)該被轉(zhuǎn)換成一個(gè)ColorDrawable
悲关。這個(gè)轉(zhuǎn)換是通過(guò)一個(gè)帶有BindingConversion注解的靜態(tài)方法完成的:
@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
請(qǐng)注意,轉(zhuǎn)換只發(fā)生在setter級(jí)別娄柳,所以不允許混合類型如下所示:
<View
android:background="@{isError ? @drawable/error : @color/white}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
9. Android Studio支持?jǐn)?shù)據(jù)綁定
Android Studio支持?jǐn)?shù)據(jù)綁定代碼的許多代碼編輯功能寓辱。例如,它支持?jǐn)?shù)據(jù)綁定表達(dá)式的以下功能:
注意:數(shù)組和 泛型類型(如Observable類)可能會(huì)在沒(méi)有錯(cuò)誤時(shí)秫筏,顯示錯(cuò)誤。
預(yù)覽窗格顯示數(shù)據(jù)綁定表達(dá)式的默認(rèn)值(如果提供)挎挖。在下面的示例摘錄了布局XML文件中的元素后这敬,“預(yù)覽”窗口將在TextView
中顯示PLACEHOLDER
默認(rèn)的文本值。
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.firstName, default=PLACEHOLDER}"/>
如果需要在項(xiàng)目設(shè)計(jì)階段顯示默認(rèn)值蕉朵,則還可以使用工具屬性而不是默認(rèn)表達(dá)式值崔涂,如 Design Time Layout Attributes中所述。