這篇博客是Data Binding Guide官網文檔翻譯的下篇,如果沒看過前半部分翻譯的可以先看[Data Binding Guide——google官方文檔翻譯(上)](http://blog.csdn.net/u014486880/article/details/50508133)
### 一,數據對象
任何不含業務邏輯的java簡單對象(POJO)可用于數據綁定,但修改POJO不能使UI更新。而通過數據綁定可以使數據對象感知到數據的變化。有三種不同的感知數據改變的機制,可見對象,可見字段,和可見集合。
當一個可見數據對象綁定到用戶界面和數據對象變化的屬性時,用戶界面將自動更新。
### 可見對象
一個實現可見接口的類,允許把監聽器和對象綁定,以便監聽該對象的所有屬性的變化。
可見接口有添加和刪除偵聽器的機制,但數據改變的通知機制取決于開發者。為了使開發更簡潔,創建一個基類(BaseObservable)來實現監聽器注冊機制。當屬性改變時,數據實現類仍負責告知機制,其中getter方法加@Bindable注釋,并在setter方法中告知屬性的變化。
~~~
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);
}
}
~~~
在編譯過程中,綁定注釋在BR類文件中生成一個條目,然后在模塊包中生成BR類文件。如果數據類的基類不可改變,可以使用方便PropertyChangeRegistry有效地存儲和通知偵聽器的方式來實現可見接口。
### ?可見字段
創建可見類的過程中,開發人員想節省時間或有很多屬性時可以使用可見字段,例如一些常用的可見類型字段:[ObservableBoolean](http://developer.android.com/reference/android/databinding/ObservableBoolean.html),?[ObservableByte](http://developer.android.com/reference/android/databinding/ObservableByte.html),?[ObservableChar](http://developer.android.com/reference/android/databinding/ObservableChar.html),[ObservableShort](http://developer.android.com/reference/android/databinding/ObservableShort.html),?[ObservableInt](http://developer.android.com/reference/android/databinding/ObservableInt.html),?[ObservableLong](http://developer.android.com/reference/android/databinding/ObservableLong.html),?[ObservableFloat](http://developer.android.com/reference/android/databinding/ObservableFloat.html),?[ObservableDouble](http://developer.android.com/reference/android/databinding/ObservableDouble.html),?and[ObservableParcelable](http://developer.android.com/reference/android/databinding/ObservableParcelable.html)。
可見字段是有獨立字段的可見對象。原始版本在訪問操作中避免裝箱和拆箱操作。為方便使用,在數據類創建使用public?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();
}
~~~
通過setter和getter方法獲取值。
~~~
user.firstName.set("Google");
int age = user.age.get();
~~~
可見集合
?一些應用程序使用更多的動態結構來保存數據。可見集合支持鍵控存取方式訪問這些數據對象。當鍵是一個像字符串這樣的引用類型時,可使用ObservableArrayMap。
~~~
ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);
~~~
在布局中,可以通過引用String類型的鍵進行映射:
~~~
<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](http://developer.android.com/reference/android/databinding/ObservableArrayList.html)。
~~~
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"/>
~~~
### 生成綁定
任何不含業務邏輯的java簡單對象(POJO)可用于數據綁定,但修改POJO不能使UI更新。而通過數據綁定可以使數據對象感知到數據的變化。有三種不同的感知數據改變的機制,可見對象,可見字段,和可見集合。
當一個可見數據對象綁定到用戶界面和數據對象變化的屬性時,用戶界面將自動更新。
生成綁定類鏈接在布局視圖的布局變量,正如前面所討論的,綁定的名稱和包可以自定義。生成的綁定類都繼承ViewDataBinding。
應該立即創建綁定,以確保布局中的表達式與視圖的綁定不干擾視圖層。有幾種綁定到布局的方法。最常見的是在綁定類調用靜態方法inflate。inflate解析視圖,并完成數據綁定。還有一個更簡單的版本,只需要在inflate方法中引入LayoutInflater或再加上ViewGroup:
~~~
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);
~~~
如果布局inflate機制有變化,也可以使用分開綁定的機制。
~~~
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);
~~~
在無法預先知道是否綁定的情況下,可以使用[DataBindingUtil](http://developer.android.com/reference/android/databinding/DataBindingUtil.html)?類創建綁定:
~~~
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);
~~~
### 有ID的視圖
在布局中每個有ID的視圖將相應生成一個public?final字段的變量,綁定類只調用一次布局,并創建每個視圖。這種機制比調用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>
~~~
會生成帶有如下屬性的綁定類:
~~~
public final TextView firstName;
public final TextView lastName;
~~~
ID可能沒有數據綁定那樣必要,但是仍有一些實例證明,從代碼訪問角度看仍然是必要的。
### 二、變量
每個變量會給出存取方法:
~~~
<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和getter方法:
~~~
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與普通視圖不同。他們開始是是一個不可視并且大小為0的視圖,可以延遲到運行時填充布局資源。設置為Visible或調用inflate()之后,就會填充布局資源,ViewStub便會被填充的視圖替代。
由于ViewStub會在視圖層消失,為了正常的連接,對應的綁定對象也要隨之消失。由于視圖層是final類型的,ViewStubProxy對象替代ViewStub?后,開發者可以訪問?ViewStub,并且當?ViewStub?在視圖層中被加載時,開發者也可以訪問加載的視圖。
當解析其他布局時,新的布局中要建立綁定關系。因此,ViewStubProxy?對象要監聽[ViewStub的OnInflateListener](http://developer.android.com/reference/android/view/ViewStub.OnInflateListener.html)并建立綁定。開發者可以在ViewStubProxy?對象上建立一個OnInflateListener,綁定建立后,便可調用OnInflateListener。
### 高級綁定
### 動態變量
有時具體的綁定類會不被識別。例如,操作任意布局的[RecyclerView.Adapter](http://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html)必須在onBindViewHolder中指定綁定值,才可識別出對應的綁定類。
在這個例子中,RecyclerView綁定的所有布局都有一個item變量。BindingHolder?對象通過引用getBinding()?方法獲取?[ViewDataBinding](http://developer.android.com/reference/android/databinding/ViewDataBinding.html)?基類。
~~~
public void onBindViewHolder(BindingHolder holder, int position) {
final T item = mItems.get(position);
holder.getBinding().setVariable(BR.item, item);
holder.getBinding().executePendingBindings();
}
~~~
### 立即綁定
當變量變化時,綁定在下一幀到來前也要隨之改變。然而,當綁定需要立即執行時,可以調用強制執行方法[executePendingBindings()](#)。
### 后臺線程
可以在后臺線程中改變數據模型,只要它不是集合。數據綁定將每個變量或字段保存到本地,以避免任何并發問題。
### 屬性Setters
只要綁定值有變化,生成的綁定類就會在視圖中調用setter方法。數據綁定框架可以自定義要調用的方法來設置值。
### 自動Setters
對一個屬性來說,數據綁定試圖查找setAttribute方法。這與屬性的命名空間沒有關系,只與屬性本身有關。
例如?TextView?的屬性?android:text?上的表達式,數據綁定將查找setText(String)?方法,如果表達式返回值為?int,則會調用?setText(int)方法。注意表達式要返回正確的類型,有必要則使用cast進行類型轉換。即使沒有給定的屬性,數據綁定也會執行。可以通過數據綁定調用setter方法創建屬性。例如,DrawerLayout沒有任何屬性,但有很多setter方法,可以調用setter方法自動創建屬性。
~~~
<android.support.v4.widget.DrawerLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:scrimColor="@{@color/scrim}"
app:drawerListener="@{fragment.drawerListener}"/>
~~~
### 重命名Setters
對于屬性setter方法與名字不匹配的情況,可以通過[BindingMethods](http://developer.android.com/reference/android/databinding/BindingMethods.html)注釋關聯名字和方法。類中包含[BindingMethod](http://developer.android.com/reference/android/databinding/BindingMethod.html)注釋,其中可以重命名set方法。例如,android:tint?屬性與[setImageTintList(ColorStateList)](#)方法相關,而與setTint不對應。
~~~
@BindingMethods({
@BindingMethod(type = "android.widget.ImageView",
attribute = "android:tint",
method = "setImageTintList"),
})
~~~
開發者不需要重命名setter方法,安卓框架已經做好了這方面的工作。
### 自定義?Setters
一些屬性需要自定義綁定邏輯,android:paddingLeft?屬性并沒有對應的setter方法,但是存在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());
}
~~~
綁定適配器對于其他自定義類型很有幫助。例如可以在其他線程中自定義加載圖片。如果綁定適配器有沖突,則開發者自定義的將會重寫系統默認的綁定適配方法。一個適配器還可以有多個參數:
~~~
@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?命名空間的屬性自定義適配器。
~~~
@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());
}
}
~~~
只能使用一個抽象方法的接口或抽象類調用事件處理程序。
~~~
@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);
}
}
}
~~~
當一個監聽器有多種方法時,必須要分割成多個監聽器。
例如,OnAttachStateChangeListener包含兩個方法,?onViewAttachedToWindow()?和onViewDetachedFromWindow(),必須創建兩個接口用于區分屬性和處理程序。
~~~
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewDetachedFromWindow {
void onViewDetachedFromWindow(View v);
}
@TargetApi(VERSION_CODES.HONEYCOMB_MR1)
public interface OnViewAttachedToWindow {
void onViewAttachedToWindow(View v);
}
~~~
一個監聽器的改變也會影響另外一個,所以必須有三個不同的綁定適配器,每個監聽器分別對應一個,兩個監聽器對應第三個綁定適配器。
~~~
@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);
}
}
}
~~~
上面的例子比正常的要稍微復雜,因為視圖對監聽器用添加和刪除方法替代set方法。
android.databinding.adapters.ListenerUtil類幫助跟蹤之前的監聽器,以便在綁定適配器中及時移除相應的監聽器。
通過用@TargetApi(VERSION_CODES.HONEYCOMB_MR1)注釋接口OnViewDetachedFromWindow?and?OnViewAttachedToWindow?。數據綁定代碼生成器知道只能在產生蜂窩MR1和新設備中運行時才會生成監聽器。
### 轉換器(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"/>
~~~
### 三、Android?Studio?支持數據綁定
Android?Studio支持數據綁定表達式的語法高亮顯示,并在編輯器中顯示任何表達式語言語法錯誤。
預覽窗格顯示數據綁定表達式的默認值。以下從布局XML文件的元素實例摘錄。
~~~
<TextView android:layout_width="wrap_content"
? ?android:layout_height="wrap_content"
? ?android:text="@{user.firstName, default=PLACEHOLDER}"/>
~~~
如果項目的設計階段需要顯示一個默認值,可以使用工具屬性而不是默認的表達式的值。
到此Data Binding官方文檔就翻譯完了。
- 前言
- Android底部tab與標題欄相結合
- Android免費獲取短信驗證碼
- android中Handler的源碼分析
- 詳解Fragment的傳值問題
- 詳談gson解析
- android新控件之toolbar,floatingActionButton,SnackBar,CollapsingToolbarLayout
- android自定義控件
- 淺談android的線程池
- Android的消息處理機制,AsyncTask源碼解析
- IPC——android進程間通信
- CoCos2d_android入門所需知道的一切
- Cocos2d_android你所需要知道的一切(下)
- Activity你需要知道的一切
- Activity啟動過程源碼分析
- Data Binding Guide——google官方文檔翻譯(上)
- Data Binding Guide——google官方文檔翻譯(下)
- android TextView實現跑馬燈效果
- android中生成excel
- Volley源碼解析
- LayoutInflater源碼解析
- android發送郵件
- android測試工具MonkeyRunner--google官網翻譯
- android View繪制源碼分析
- Android中Window添加View的底層原理
- 仿美團商品選購下拉菜單實現