編譯環(huán)境
要將應(yīng)用配置為使用數(shù)據(jù)綁定醋旦,請(qǐng)?jiān)趹?yīng)用模塊的build.gradle
文件中添加dataBinding
元素:
android {
...
dataBinding {
enabled = true
}
}
使用數(shù)據(jù)綁定庫(kù)
數(shù)據(jù)綁定的布局文件以根標(biāo)記layout
開(kāi)頭,后跟data
元素和view
根元素:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="user"
type="com.jetpackdemo.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
在data
中使用了包名為com.jetpackdemo
的User
文件,并為此對(duì)象設(shè)置了name:user
署惯。
在TextView
中:android:text="@{user.name}"
表示為TextView
設(shè)置user
的name
屬性值。
完成布局文件后,系統(tǒng)會(huì)為每一個(gè)布局文件生成一個(gè)綁定類,默認(rèn)情況下衫嵌,類名基于布局文件的名稱,將xml
文件的名稱轉(zhuǎn)換為駝峰大小寫彻秆,并在末尾添加Binding
一詞楔绞。
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.user = User("hello")
}
如果在Fragment
、ListView
和RecyclerView
適配器中使用數(shù)據(jù)綁定唇兑,則可以使用DataBindingUtil
的inflate
方法:
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
布局表達(dá)式
可以在布局文件的使用一下運(yùn)算符和關(guān)鍵字:
- 算術(shù)運(yùn)算符
+ - * / %
- 字符串連接運(yùn)算符
+
- 邏輯運(yùn)算符
&& ||
- 二元運(yùn)算符
& | ^
- 一元運(yùn)算符
+ - 酒朵! ~
- 移位運(yùn)算符
>> >>> <<
- 比較運(yùn)算符
== > < >= <=
(<需要轉(zhuǎn)義為<
) - instanceof
- 分組運(yùn)算符
()
- 字面量運(yùn)算符-字符、字符串扎附、數(shù)字蔫耽、
null
- 類型轉(zhuǎn)換
- 方法調(diào)用
- 字段訪問(wèn)
- 數(shù)組訪問(wèn)
[ ]
- 三元運(yùn)算符
?:
缺少的布局表達(dá)式
this
super
new
- 顯式泛型調(diào)用
Null
合并運(yùn)算符
android:text="@{user.name ?? user.sex}"
如果左邊運(yùn)算不為null
則選擇左邊運(yùn)算,否則選擇右邊運(yùn)算帕棉。
屬性引用
android:text="@{user.name}"
避免出現(xiàn)Null
指針異常
生成的數(shù)據(jù)綁定代碼會(huì)自動(dòng)檢查有沒(méi)有null
值并避免出現(xiàn)null
指針異常针肥。如果在表達(dá)式中String
為null
則分配默認(rèn)值null
饼记。
集合表達(dá)式
為了方便訪問(wèn)香伴,可使用[ ]
運(yùn)算符訪問(wèn)常見(jiàn)集合慰枕,例如數(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]}"
在集合中,必須使用轉(zhuǎn)義<字符低斋。例如:不要寫成
List<String>
形式蜂厅,而是必須寫成List<String>
。
獲取map中的值
android:text='@{map.get("name")}'
事件處理
通過(guò)數(shù)據(jù)綁定膊畴,您可以編寫從視圖分派的表達(dá)式處理事件(例如:onClick()
方法)掘猿。事件名稱由監(jiān)聽(tīng)器方法的名稱確定。
可以使用以下機(jī)制處理事件:
方法引用:在表達(dá)式中唇跨,您可以引用符合監(jiān)聽(tīng)器方法簽名的方法稠通,當(dāng)表達(dá)式求值結(jié)果為方法引用時(shí),數(shù)據(jù)綁定會(huì)將方法引用和所有者對(duì)象封裝到監(jiān)聽(tīng)器中买猖,并在目標(biāo)視圖上設(shè)置該監(jiān)聽(tīng)器改橘。如果表達(dá)式的求值結(jié)果為
null
,則數(shù)據(jù)綁定不會(huì)創(chuàng)建監(jiān)聽(tīng)器玉控,而是設(shè)置null
監(jiān)聽(tīng)器飞主。
監(jiān)聽(tīng)器綁定:這些是在事件發(fā)生時(shí)進(jìn)行求值的lambda
表達(dá)式。數(shù)據(jù)綁定始終會(huì)創(chuàng)建一個(gè)要在視圖上設(shè)置的監(jiān)聽(tīng)器高诺。事件被分派后碌识,監(jiān)聽(tīng)器會(huì)對(duì)lambda
表達(dá)式進(jìn)行求值。
方法引用
一個(gè)主要優(yōu)點(diǎn)是表達(dá)式在編譯時(shí)進(jìn)行處理虱而,因此筏餐,如果該方法不存在或其簽名不正確,則會(huì)收到編譯時(shí)錯(cuò)誤薛窥。
class MyHandlers {
fun onClickFriend(view: View) { ... }
}
<?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>
方法引用中的簽名必須和方法中的簽名保持一致,并且參數(shù)也保持一致胖烛,不能自定義參數(shù)。
監(jiān)聽(tīng)器綁定
監(jiān)聽(tīng)器綁定是在事件發(fā)生時(shí)運(yùn)行的綁定表達(dá)式诅迷。它們類似于方法引用佩番,但允許您運(yùn)行任意數(shù)據(jù)綁定表達(dá)式。此功能適用于 Gradle 2.0 版及更高版本的 Android Gradle 插件罢杉。
在方法引用中趟畏,方法的參數(shù)必須與事件監(jiān)聽(tīng)器的參數(shù)匹配。在監(jiān)聽(tīng)器綁定中滩租,只有您的返回值必須與監(jiān)聽(tīng)器的預(yù)期返回值相匹配(預(yù)期返回值無(wú)效除外)赋秀。
class Presenter {
fun onSaveClick(task: Task){}
}
然后利朵,您可以將點(diǎn)擊事件綁定到 onSaveClick() 方法
<?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)的事件返回類型不是void的值,那么表達(dá)式也必須返回相同類型的值(比如長(zhǎng)按事件返回值是boolean)猎莲。
fun onSaveClick(task: Task): Boolean{}
從以上說(shuō)明可以看出绍弟,方法引用更適合簡(jiǎn)單的事件處理,并且方法不需要自定義參數(shù)著洼。監(jiān)聽(tīng)器則更適合復(fù)雜的事件處理樟遣,方法可以自定義參數(shù)。
避免使用復(fù)雜的監(jiān)聽(tīng)器
監(jiān)聽(tīng)器表達(dá)式功能非常強(qiáng)大身笤,可以使您的代碼非常易于閱讀豹悬。另一方面,包含復(fù)雜表達(dá)式的監(jiān)聽(tīng)器會(huì)使您的布局難以閱讀和維護(hù)液荸。這些表達(dá)式應(yīng)該像將可用數(shù)據(jù)從界面?zhèn)鬟f到回調(diào)方法一樣簡(jiǎn)單瞻佛。您應(yīng)該在從監(jiān)聽(tīng)器表達(dá)式調(diào)用的回調(diào)方法中實(shí)現(xiàn)任何業(yè)務(wù)邏輯。
導(dǎo)入娇钱、變量和包含
導(dǎo)入
通過(guò)導(dǎo)入功能伤柄,您可以輕松地在布局文件中引用類,就像在托管代碼中一樣忍弛。您可以在 data
元素使用多個(gè) import
元素响迂,也可以不使用。以下代碼示例將View
類導(dǎo)入到布局文件中:
<data>
<import type="android.view.View"/>
</data>
上述方法中導(dǎo)入了View
類细疚,可以引用View
的屬性蔗彤,比如設(shè)置顯示:
android:visibility="@{user.man? View.VISIBLE : View.GONE}"
類型別名
當(dāng)類名有沖突時(shí),其中一個(gè)類可使用別名重命名疯兼。以下示例將com.example.real.estate
軟件包中的View
類重命名為Vista
:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
導(dǎo)入其他類
<data>
<import type="com.example.User"/>
<import type="java.util.List"/>
<variable name="user" type="User"/>
<variable name="userList" type="List<User>"/>
</data>
還可以通過(guò)使用導(dǎo)入的類型來(lái)對(duì)表達(dá)式的一部分進(jìn)行類型轉(zhuǎn)換然遏。以下示例將connection
屬性強(qiáng)制轉(zhuǎn)換為類型User
:
<TextView
android:text="@{((User)(user.connection)).lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
在表達(dá)式中引用靜態(tài)字段和方法時(shí),也可以使用導(dǎo)入的類型吧彪。以下代碼會(huì)導(dǎo)入MyStringUtils
類待侵,并引用其 capitalize
方法:
<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"/>
包含
通過(guò)使用應(yīng)用命名空間和特性中的變量名稱,變量可以從包含的布局傳遞到被包含布局的綁定姨裸。以下示例展示了來(lái)自 name.xml 和 contact.xml 布局文件的被包含 user 變量:
<?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>
數(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><!-- Doesn't work -->
<include layout="@layout/name"
bind:user="@{user}"/>
<include layout="@layout/contact"
bind:user="@{user}"/>
</merge>
</layout>