官方英文文檔:https://developer.android.google.cn/topic/libraries/data-binding/observability
一、介紹
觀察能力是指一個對象可以通知別人它的數據已經發生改變的能力。DataBinding
庫允許你讓你的對象、字段、集合具備觀察能力。
普通對象可以用於DataBinding
,但是它們不會自動通知UI
它們發生改變了. DataBinding
可以賦予對象在數據改變時通知其他對象的能力 。
二、可觀察字段(Observable Fields
)
當對象只有少量有效字段時,就沒有必要創建一個實現Observable
接口的類了,對於這種情況,可以使用以下專用類來讓字段具備觀察能力。
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
可觀察對象只有單個字段,基本類型版本在訪問時可以避免自動裝箱和自動拆箱,而要使用這一機制,可以如下創建public final
的Java
屬性,或者創建只讀read-only
的Kotlin
屬性
// java
private static class User {
public final ObservableField<String> firstName = new ObservableField<>();
public final ObservableField<String> lastName = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
// kotlin
class User {
val firstName = ObservableField<String>()
val lastName = ObservableField<String>()
val age = ObservableInt()
}
通過set
、get
方法剛問字段值:
// kotlin
user.firstName = "Google"
val age = user.age
// java
user.firstName.set("Google");
int age = user.age.get();
Note: 在Android studio 3.1
及以上版本中可以通過使用LiveData
來替換可觀察字段,並且使用LiveData
可以帶來額外的好處,具體看 這裏.
三、可觀察集合
一些應用需要使用動態數據結構來來存儲數據,可觀察集合允許通過key
來訪問這些數據結構,如 ObservableArrayMap
.
// Kotlin
ObservableArrayMap<String, Any>().apply {
put("firstName", "Google")
put("lastName", "Inc.")
put("age", 17)
}
// Java
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
而在佈局中的使用方法如下示例:
<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
在key
爲整型數據時非常有用,如下:
//Kotlin
ObservableArrayList<Any>().apply {
add("Google")
add("Inc.")
add(17)
}
// java
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"/>
四、可觀察對象
實現了Observable
接口的類允許註冊的監聽器接收到它的字段數據發生改變的通知。
Observable
接口包含註冊和移除監聽器的機制,但是我們需要決定什麼是時候發送通知,爲了方便開發,Databinding
庫提供了BaseObservable
類來實現這個監聽器註冊機制。繼承了BaseObservable
的類負責在對象屬性改變時發送通知,當給get
方法加上@Bindable
註解,且在set
方法中調用notifyPropertyChanged
方法時,這個流程就完成了,如下示例:
// Java
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);
}
}
// Kotlin
class User : BaseObservable() {
@get:Bindable
var firstName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.firstName)
}
@get:Bindable
var lastName: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.lastName)
}
}
Data Binding
會在模塊包生成一個叫做BR
的類,它包含了用於數據綁定的資源ID
,@Bindable
註解則會在編譯時在BR
中生成一個entry
。如果數據類仍不能改變,那麼可以通過PropertyChangeRegistry
對象註冊和高效地通知監聽器。
關於 Bindable
@Bindable
必須作爲get
方法的註解,如下示例,注意到@Bindable({"firstName", "lastName"}}
, 這裏可以綁定多個字段,而在這些字段中,當有一個字段被更新後,與之相關聯的字段(如這裏的name
,它依賴於firstName
和lastName
)都會被視作垃圾數據而被更新。但是這並不意味會onPropertyChanged(Observable, int)
會接收到因爲BR.name
變成垃圾數據而觸發的通知,當且僅當綁定表達式(binding expressions
)中包含name
字段的情況纔會被視作垃圾數據並更新。
@Bindable
public void getFirstName() { return this.firstName; }
@Bindable
public void getLastName() { return this.lastName; }
@Bindable({"firstName", "lastName"}}
public void getName() { return this.firstName + ' ' + this.lastName; }