這種表達式語言(expression language
)使咱們可使用表達式處理 view 的事件。Data Binding
庫會自動生成綁定類(binding class
)用來處理 view 和 data 的綁定關係。php
使用 Data Binding
的佈局文件和傳統的佈局文件稍有不一樣,它的根標籤是 layout
,裏面會有一個 data
子標籤和一個根 view 子標籤。這個根 view 子標籤和傳統的佈局文件是同樣的。具體以下所示:java
<?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
變量,將會在綁定表達式中用到。android
<variable name="user" type="com.example.User" />
複製代碼
綁定表達式用於爲屬性賦值,它使用的語法是 @{}
。在下面的例子中,TextView
控件的屬性 text
,被賦值爲 user
變量的 firstName
屬性值:express
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{user.firstName}" />
複製代碼
假設如今有一個描述 User
實體的數據對象:數組
public class User {
public final String firstName;
public final String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
複製代碼
這個數據類的成員屬性都是不可變的、是 public 的。它還有另一種寫法,成員屬性是 private 的,而且提供訪問它們的方法:ide
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;
}
}
複製代碼
從數據綁定的角度來看,上面兩個實體類是等價的。用於給 android:text
屬性賦值的表達式 @{user.firstName}
,會自動讀取前一個類的 firstName
屬性,或者調用後一個類的 getFirstName()
方法。並且,若是 firstName()
方法存在,也會調用這個方法。佈局
Binding data
)每一個佈局文件都會生成一個對應的綁定類。默認的綁定類的名字是文件名轉爲駝峯寫法並加上後綴 Binding。好比文件名是 activity_main.xml
,對應的綁定類名爲 ActivityMainBinding
。這個綁定類保存了數據變量和 view 屬性的綁定關係,而且知道如何爲 view 屬性賦值。推薦的方式是在加載佈局的時候建立綁定類,以下所示:post
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.user = User("Test", "User")
}
複製代碼
另外,還可使用 inflate()
方法建立綁定類:this
val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())
複製代碼
若是是在 Fragment
、 ListView
、 RecyclerView
中使用 Data Binding
,你可能更傾向於使用 inflate()
方法或 DataBindingUtil
類,以下所示:spa
val listItemBinding = ListItemBinding.inflate(layoutInflater, viewGroup, false)
// or
val listItemBinding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false)
複製代碼
expression language
)1 經常使用特性
表達式語言看起來很像代碼裏面的表達式。你能夠在表達式中使用以下的操做符和關鍵字:
+ - / * %
+
&& ||
& | ^
+ - ! ~
>> >>> <<
== > < >= <=
(注:<
運算符須要寫成 <
)instance of
()
[]
?:
舉例:
android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age > 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
複製代碼
2 不可用操做
下面操做只能在代碼裏使用,不能用於表達式語法:
this
super
new
3 空結合運算符(??
)
空結合運算符左邊表達式若是不爲空,使用左邊表達式結果,不然使用右邊表達式結果
android:text="@{user.displayName ?? user.lastName}"
複製代碼
它等價於:
android:text="@{user.displayName != null ? user.displayName : user.lastName}"
複製代碼
4 屬性引用
使用綁定表達式,能夠引用一個類的成員變量、get 方法、ObservableField
:
android:text="@{user.lastName}"
複製代碼
5 避免空指針異常
生成的綁定類會自動判空從而避免空指針異常。好比,表達式 @{user.name}
,若是 user
是空,user.name
會被賦予默認值 null
。若是引用的是 user.age
,age
的類型是 int
,那麼會使用 0
做爲默認值。
6 集合
經常使用的集合,如數組、list、sparse list、map 等,能夠方便地使用 []
操做符訪問它們的元素。
<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]}"
複製代碼
注:爲了能正確解析 XML,須要將
<
替換爲<
。如List<String>
應該寫成List<String>
。
訪問 map 中的元素,除了可使用 @{map[key]}
,也可使用 @{map.key}
。
7 字符串的寫法
可使用單引號包裹屬性,在表達式中使用雙引號,以下所示:
android:text='@{map["firstName"]}'
複製代碼
也可使用雙引號包裹屬性,在表達式中使用單引號,以下所示:
android:text="@{map[`firstName`]}"
複製代碼
8 訪問資源
能夠在表達式中使用以下語法訪問資源:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"
複製代碼
9 事件處理
Data Binding
可讓咱們在表達式中處理 view 分發的事件,好比 onClick()
。屬性的名字取決於監聽器方法的名字,好比,View.OnClickListener
有一個 onClick()
方法,因此對應的屬性名就是 android:onClick
。
處理 view 事件有兩種方法:
null
,對應的 view 不會設置監聽器。listener binding
):這種方式老是會給對應的 view 設置監聽器,當 view 收到事件時會調用 lambda 表達式。
- 方法引用
事件能夠和方法直接綁定,這種方式與
android:onClick
能夠和 activity 中一個方法綁定很相似。這種方式的優點是,綁定表達式是在編譯期間處理的,若是方法不存在或者簽名不匹配,會直接報錯。和監聽器綁定不一樣的是,方法引用的方式會在數據綁定的時候建立監聽器,監聽器綁定則是在收到事件的時候建立監聽器。
示例以下:
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>
複製代碼
注: 表達式中的方法簽名必須與監聽器中的方法簽名一致。
- 監聽器綁定(
listener binding
)監聽器綁定的方式,只有在收到事件的時候纔會執行。它和方法引用很像,可是它可使用任意的表達式。這個特性在 Gradle 2.0 及之後可使用。
方法引用的方式,要求方法的簽名必須和監聽器的方法簽名一致。可是監聽器綁定的方式,只要求返回值一致便可。例如,假以下面的 Presenter 類有一個
onSaveClick()
方法:
class Presenter {
fun onSaveClick(task: Task){}
}
複製代碼
onSaveClick()
能夠與android:onClick
綁定,以下所示:
<?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>
複製代碼
在上面這個例子中,咱們沒有定義 view 參數。監聽器綁定提供了兩種選擇:要麼忽略全部參數,要麼顯式寫出全部參數。若是你喜歡寫出參數,以下所示:
android:onClick="@{(view) -> presenter.onSaveClick(task)}"
複製代碼
若是你須要使用這些參數,以下所示:
class Presenter {
fun onSaveClick(view: View, task: Task){}
}
複製代碼
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"
複製代碼
同時,也能夠有多個參數,以下所示:
class Presenter {
fun onCompletedChanged(task: Task, completed: Boolean){}
}
複製代碼
<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"
android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />
複製代碼
此外,若是你監聽的事件返回值不是
void
,那麼你的表達式也須要返回相同的返回值。以下所示:
class Presenter {
fun onLongClick(view: View, task: Task): Boolean { }
}
複製代碼
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"
複製代碼
若是表達式不能正常執行,那麼會返回默認值,引用類型返回 null,int 類型返回 0,布爾類型返回 false。
Data Binding
提供了諸如 imports
、 variables
、 includes
等特性。imports
用於導入所須要的類,方便引用;variable
用於定義一個變量,方便在綁定表達式中使用。includes
使咱們能夠複用佈局。
下面這個例子展現了導入 View
這個類到佈局文件中:
<data>
<import type="android.view.View"/>
</data>
複製代碼
導入的目的就是爲了方便在綁定表達式中使用。下面的例子展現了在表達式中引用 View
類的兩個常量 VISIBLE
和 GOEN
:
<TextView android:text="@{user.lastName}" android:layout_width="wrap_content" android:layout_height="wrap_content" android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
複製代碼
若是導入的兩個類名字相同,能夠爲其中一個或兩個起別名以區分:
<import type="android.view.View"/>
<import type="com.example.real.estate.View"
alias="Vista"/>
複製代碼
注:
java.lang.*
下面的類會自動導入。
變量的聲明用於在綁定表達式中使用,以下所示,聲明瞭 user
、 image
、 note
三個變量:
<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>
複製代碼
自動生成的綁定類,包含了這些變量的 get 和 set 方法。這些變量都會有默認值,引用類型默認值是 null
,int 類型默認值是 0,布爾類型默認值是 false。
變量能夠傳遞給 include
佈局,以下所示,user
變量傳遞給了 name.xml
和 contact.xml
兩個佈局:
<?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>
複製代碼
Data binding
不支持 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>
複製代碼