來自官方的Android數據綁定(Data Binding)框架② Read more: http://blog.chengyunfeng.com/?p=735#ixzz4HkFGb2dc

數據對象
任何的POJO 對象都可以用作數據綁定,但是修改一個 POJO 對象不會更新 UI。 數據綁定的威力在於,賦予數據對象在數據改變的時候通知其他組件的能力。有三種數據改變通知機制:Observable 對象、ObservableFields 和 observable 集合。

如果這三種類型中的任意一種類型的數據綁定到 UI 中,當數據改變的時候, UI 的數據也會自動更新。

Observable 對象

實現了 android.databinding.Observable 接口的對象,可以設置一個監聽器來監聽所有值域變化的事件。

爲了方便開發者使用,BaseObservable 類包含了添加和刪除監聽對象的接口,但是通知數據變化需要開發者自己來做。 和 ListView 的 Adapter 類似。

Bindable 註解在編譯的時候會生成一個 BR 類中的實體,BR 位於模塊的包中。如果您的數據類無法修改,則可以使用 PropertyChangeRegistry 來保存和通知改變事件。

ObservableFields

繼承 Observable 可能有點麻煩,如果你像簡單一點或者只有少量幾個綁定的屬性,則可以使用 ObservableFields。 ObservableFields 爲字包含的 observable 對象。 ObservableFields 包含了所有基本類型和一個引用類型。 使用方式如下:

很簡單,這些變量會自動觸發值改變事件,使用 get 和 set 來訪問:

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

Observable 集合
如果引用的 key 爲對象,則可以使用 ObservableArrayMap :

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

在佈局文件中,可以通過 String key 來引用map 裏面的對象:

<data>
<import type=”android.databinding.ObservableMap”/>
<variable name=”user” type=”ObservableMap&lt;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 爲整數,則使用 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&lt;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”/>

生成的綁定類(Generated Binding)

生成的綁定類自動處理的 佈局文件中的 View 和 變量的值,並把他們關聯起來。 所有生成的綁定類都繼承自 android.databinding.ViewDataBinding。

創建綁定類

綁定類應該在解析完佈局後立刻創建,這樣可以避免其他數據干擾佈局文件中表達式的解析。獲取綁定類最常用的方式是通過生成類的靜態函數 inflate 。inflate 函數同時解析 View 和完成數據綁定。

如果佈局文件解析的機制有變化,則還可以分開綁定:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有時候,綁定對象需要運行時創建,則可以通過如下方式:

(帶 ID 的 View)Views With IDs

對於 佈局文件中的每個帶 ID 的 View 都會生成一個 final 變量。 綁定類只解析一次佈局文件,並創建每個 View。 這種方式比多次調用 findViewById 要高效一些。

例如:

生成的 綁定類會包含如下變量:
public final TextView firstName;
public final TextView lastName;

沒有 ID 也可以使用數據綁定,但是爲了以後引用這些 View, 添加個 id 會更加方便。

變量(Variables)

每個變量都會生成 get 和 set 函數:
<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>

會生成如下代碼:
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);

ViewStubs

ViewStubs 和普通的 view 不太一樣。 一開始這些 view 是不可見的, 並且沒有解析到 界面中,當顯示 ViewStub 或者顯示的解析他們的時候纔會加載到界面中,替代之前的 View。

由於 ViewStub 最終會從 View 層級中消失, 所以對應的綁定對象也應該消失以便回收資源。由於 View 是 final 的,這裏會使用一個 ViewStubProxy 對象來替代 ViewStub, 這樣開發者就可以訪問 ViewStub 了,並且當 ViewStub 被加載到 View 層級中的時候,開發者也可以訪問加載的 View。

當解析另外一個佈局文件的時候, 綁定對象也應該和新的佈局關聯起來。因此,ViewStubProxy 需要監聽 ViewStub 的 OnInflateListener 回調接口來建立綁定關係。開發者可以在 ViewStubProxy 上設置一個 OnInflateListener ,當綁定建立的時候,開發者可以收到回調 函數。

高級綁定

動態變量
有時候具體綁定的類還不知道是哪個。例如,一個 RecyclerView Adapter 使用一些佈局文件,只有在 onBindViewHolder 中才知道 layout 使用的是哪個變量。
下面的例子中,RecyclerView 的每個 View 都包含一個 item 變量, 通過 BindingHolder 的 getBinding 函數來訪問 ViewDataBinding 。然後把 item 變量設置進去。
public void onBindViewHolder(BindingHolder holder, int position) {
final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}

立即綁定
當變量的值更新的時候,binding 對象將在下個更新週期中更新。這樣就會有一點時間間隔,如果你像立刻更新,則可以使用 executePendingBindings 函數。

後臺線程

只要不是集合變量,則可以在後臺線程中更新數據。數據綁定將會保存每個變量的值到本地以避免多線程問題。

Attribute Setters

當綁定的值改變的時候,生成的綁定對象會調用一個 setter 函數來更新 View 的值。綁定框架可以自定義調用哪個函數來設置值。

自動查找 setter

對於一個屬性,綁定框架會自動查找 setAttribute 函數。例如 TextView 的屬性 android:text 上的表達式,綁定框架將會調用 TextView 的 setText(String) 函數,如果表達式返回值爲 int, 則會調用 setText(int) 函數。所以,要小心表達式的返回值,如果必要可以使用 cast 來轉換爲需要的類型。

需要注意的是, 數據綁定框架查找的是一個 set 函數,而不是該屬性是否存在。 例如 support 庫中的 DrawerLayout 沒有任何屬性,但是有很多 set 函數,所以可以把這些函數當做屬性來在 綁定佈局文件中使用,只需要把函數名字的 set 去掉,並把後面的單詞首字符修改爲小寫即可。例如:

<android.support.v4.widget.DrawerLayout
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
app:scrimColor=”@{@color/scrim}”
app:drawerListener=”@{fragment.drawerListener}”/>

DrawerLayout 有個 setScrimColor 函數,但是沒有 scrimColor 這個變量。

重命名 setter

還可以通過 BindingMethods 來重命名對應的 set 函數。 例如 android:tint 屬性的 set 函數被重命名爲 setImageTintList 而不是 setTint.

@BindingMethods({
@BindingMethod(type = “android.widget.ImageView”,
attribute = “android:tint”,
method = “setImageTintList”),
})

開發者一般不需要重命名 setter, android 框架已經重命名了對應的實現。

自定義 Setters
有些屬性需要自定義綁定邏輯。例如, android:paddingLeft 屬性並沒有對應的函數, View 只有一個 setPadding(left, top, right, bottom)。通過 BindingAdapter 註解來創建一個靜態的自定義 setter 函數。 android 系統已經創建了這些 BindingAdapter 函數了,例如下面是 paddingLeft 屬性對應的函數:

@BindingAdapter(“android:paddingLeft”)
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}

綁定適配器(Binding adapter)對於其他類型的定製是非常有用的。例如一個自定義的 loader 可以在其他線程中加載圖片。

如果綁定適配器有衝突,則開發者自定義的將會替代系統自定義的。

一個 適配器還可以有多個參數:

@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}”/>

如果用於 ImageView 的 imageUrl和 error 參數都存在並且 imageUrl 是 string 類型、error 是 drawable 類型 則就會調用上面定義的適配器。

在匹配適配器的時候, 會忽略自定義的命名空間
你也可以爲 android 命名空間的屬性自定義適配器

轉換器(Converters)

對象轉換

當綁定表達式返回一個對象時候,將會自動調用 set 函數、重命名的函數、或者自定義的 setter 中的一個。表達式返回的對象將會轉換爲該函數的參數類型。

使用 ObservableMaps 來保存數據會比較簡單。例如:
<TextView
android:text=’@{userMap[“lastName”]}’
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”/>

這裏的 userMap 返回的對象將制動轉換爲 setText(CharSequence) 的參數。 如果參數不明確,則開發者需要強制轉換爲需要的類型。

自定義轉換規則

有時候參數應該可以自動轉換,例如
<View
android:background=”@{isError ? @color/red : @color/white}”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”/>

上面的背景需要一個 Drawable 對象,但是表達式的返回值爲整數對象的顏色值。這種情況下,顏色值需要轉換爲 ColorDrawable。 這種轉換通過一個靜態函數完成,該函數帶有一個 BindingConversion 註解。

@BindingConversion
public static ColorDrawable convertColorToDrawable(int color) {
return new ColorDrawable(color);
}
需要注意的是,轉換是在 setter 層面上完成的, 所以不能混合使用不同的類型:

<View
android:background=”@{isError ? @drawable/error : @color/white}”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”/>

上面混合使用 drawable 和 int 是不可以的。

由於數據綁定框架還處於beta 測試階段, 上面所介紹的方法可能隨時出現更改,最新的信息,請參考這裏:https://developer.android.com/tools/data-binding/guide.html

兩個數據綁定的示例項目:

本文出自 雲在千峯,轉載時請註明出處及相應鏈接。

本文永久鏈接: http://blog.chengyunfeng.com/?p=735



Read more: http://blog.chengyunfeng.com/?p=735#ixzz4HkF7UqRh
發佈了11 篇原創文章 · 獲贊 1 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章