1. 程式人生 > >來自官方的Android資料繫結(Data Binding)框架②

來自官方的Android資料繫結(Data Binding)框架②

資料物件
任何的POJO 物件都可以用作資料繫結,但是修改一個 POJO 物件不會更新 UI。 資料繫結的威力在於,賦予資料物件在資料改變的時候通知其他元件的能力。有三種資料改變通知機制:Observable 物件、ObservableFields 和 observable 集合。

如果這三種類型中的任意一種型別的資料繫結到 UI 中,當資料改變的時候, UI 的資料也會自動更新。

Observable 物件

實現了 android.databinding.Observable 介面的物件,可以設定一個監聽器來監聽所有值域變化的事件。

為了方便開發者使用,BaseObservable 類包含了新增和刪除監聽物件的介面,但是通知資料變化需要開發者自己來做。 和 ListView 的 Adapter 類似。

幫助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 private static class User extends BaseObservable { private String firstName; private String lastName; @Bindable public String getFirstName() { return this.firstName; } @Bindable public String getFirstName() { 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); } }

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

ObservableFields

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

幫助
1 2 3 4 5 6 7 private static class User extends BaseObservable { public final ObservableField<String> firstName = new ObservableField<>(); public final ObservableField<String> lastName = new ObservableField<>(); public final ObservableInt age = new ObservableInt(); }

很簡單,這些變數會自動觸發值改變事件,使用 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.inflate(layoutInflater);
MyLayoutBinding binding = MyLayoutBinding.inflate(LayoutInflater, viewGroup, false);

如果佈局檔案解析的機制有變化,則還可以分開繫結:

MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);

有時候,繫結物件需要執行時建立,則可以通過如下方式:

ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,
    parent, attachToParent);
ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

(帶 ID 的 View)Views With IDs

對於 佈局檔案中的每個帶 ID 的 View 都會生成一個 final 變數。 繫結類只解析一次佈局檔案,並建立每個 View。 這種方式比多次呼叫 findViewById 要高效一些。

例如:

幫助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <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 也可以使用資料繫結,但是為了以後引用這些 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 是不可以的。

兩個資料繫結的示例專案:

相關推薦

來自官方Android資料Data Binding框架

資料物件 任何的POJO 物件都可以用作資料繫結,但是修改一個 POJO 物件不會更新 UI。 資料繫結的威力在於,賦予資料物件在資料改變的時候通知其他元件的能力。有三種資料改變通知機制:Observable 物件、ObservableFields 和 observa

HTML中的資料Data Binding

有沒想過在javascript中使用recordset?原來在客戶端操作資料也可以這樣簡單,定義一個數據源,將資料繫結在各種tag上,實現應用程式般的效果,酷斃了!(首先申明一點,文章的內容全部來自msdn,不過用我自己的話總結而已。)先看看這樣兩個例子:http://msd

WPFS資料要是後臺類物件的屬性值發生改變,通知在“客戶端介面與之的控制元件值”也發生改變需要實現INotitypropertyChanged介面

WPFS資料繫結(要是後臺類物件的屬性值發生改變,通知在“客戶端介面與之繫結的控制元件值”也發生改變需要實現INotitypropertyChanged介面) MainWindow.xaml 1 <Window x:Class="WpfApplication1.MainWindow" 2

第六章 註解式控制器詳解——SpringMVC強大的資料2

6.6.2、@RequestParam繫結單個請求引數值 @RequestParam用於將請求引數區資料對映到功能處理方法的引數上。 public String requestparam1(@RequestParam String username) 請求中包含username引數(如/

第六章 註解式控制器詳解—SpringMVC強大的資料1

     到目前為止,請求已經能交給我們的處理器進行處理了,接下來的事情是要進行收集資料啦,接下來我們看看我們能從請求中收集到哪些資料,如圖6-11:  圖6-11 1、@RequestParam繫結單個請求引數值; 2、@PathVariabl

一步一步教你實現安卓mvvm架構,雙向資料

上文講了普通介面的雙向資料繫結,本文講一下recyclerview的雙向資料繫結,整個工程只要寫一個adapter就夠了,不用像以前一個recyclerview寫一個adapter了,節省了至少80%的程式碼,什麼第三方庫都不需要了。強大的工具! 1.   建立一個通用

查詢SAP UI5官方關於資料的文件:

定義:顧名思義,就是完全按照你的需求給你定製。特點是可以給到原始碼,獨立部署伺服器,客戶方可以要求擁有原始碼的智慧財產權,可以掌控伺服器和資料安全。 優勢:可以按照客戶方需求進行滿足,可以拿到原始碼,可以二次開發,有智慧財產權,可以掌控伺服器和資料安全。 缺點:

chart 控制元件的各種資料

一.資料來源      說到繪製圖表,可能很多人的第一反應除了是圖表呈現的方式外,更關心的便是資料如何新增,記得在很多年前,哪時要繪製一個數據曲線圖,一直找不到好的呈現方式,後來使用了SVG的繪圖模式,不過在新增資料的時候可謂吃盡了苦頭,畢竟,SVG只是一種描述語言,要動態的實現一個圖表的繪製,是非常困難的.

JavaScript實現簡單的雙向資料Ember、Angular、Vue

什麼是雙向資料繫結呢? 簡單的說 就是UI檢視與資料繫結在了一塊 也就是資料和檢視是同步改變的 雙向資料繫結最常見的應用場景就是表單 (應用場景還是很有限的) 現在我們要實現這樣一個簡單的資料繫結 輸入欄中輸入字元 和它繫結的節點內容

WPF資料

前面講了最近本的兩種資料繫結,這次講講資源繫結和Context繫結 1、資源繫結 同樣不需要寫程式碼,僅僅需要配置XAML就可以了。 1 <TabItem Header="ResourceBinding"> 2

Vue.js實現雙向資料表單自動賦值、表單自動取值

1、使用Vue.js實現雙向表單資料繫結,例子 <!--html程式碼--> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta

android資料框架DataBinding使用

在介紹DataBinding之前,肯定要先學會搭建使用它的環境。鑑於是2015年釋出的,現在大家androidstudio的版本估計都在1.5以上了,就在1.5的版本上搭建和介紹吧,1.5之前的大家可以在網上搜索,因為我看網上大多數是介紹的1.3的使用方法,還沒有很多對於1

Android資料框架DataBinding,堪稱解決介面邏輯的黑科技

去年穀歌 I/O大會上介紹了一個非常厲害的新框架DataBinding, 資料繫結框架給我們帶來了很大的方便,以前我們可能需要在每個Activity裡寫很多的findViewById,不僅麻煩,還增加了程式碼的耦合性,如果我們使用DataBinding,就可以拋棄那麼

Android資料框架DataBinding用法

    2)繫結Variable      雖然在佈局檔案中對應上了,但是值是怎麼傳進去的呢?這就是我們要將的Activity中的那兩行程式碼了,它把實體類和佈局檔案進行了繫結。修改MainActivity中的onCreate,用 DatabindingUtil.setContentView() 來替換掉 s

SRPG遊戲開發三十四第八章 遊戲中的資料 - 四 資料編輯器Data Editor

返回總目錄 第八章 遊戲中的資料(Data in Game) 在之前的章節中,我們進行地圖物件的生成,移動等操作。 這一章本來可以進行戰鬥的編寫,不過資料缺失是一個問題。 所以這一章我們先來建立一些資料,以及如何編輯它們,是否需要生成配置檔案等。 文章

機器學習小組知識點27:資料預處理之資料離散化Data Discretization

離散化和概念分層產生 通過將屬性域劃分為區間,離散化技術可以用來減少給定連續屬性值的個數。區間的標號可以替代實際的資料值。如果使用基於判定樹的分類挖掘方法,減少屬性值的數量特別有好處。通常,這種方法是遞迴的,大量的時間花在每一步的資料排序上。因此,待排序的不同

【強烈推薦】:關於系統學習資料探勘Data Mining的一些建議!!

微信公眾號 關鍵字全網搜尋最新排名 【機器學習演算法】:排名第一 【機器學習】:排名第一 【Python】:排名第三 【演算法】:排名第四 關於資料探勘 提到收據挖掘(Data Mining, DM),很多想學習的同學大多數都會問我: 什麼是資料探勘? 怎麼培養資料分析的能力? 如何成為一名資料科學家? (

scala中的上下文context bound

context bound implicitly implicitly 主要是在當前作用域查詢指定型別: def implicitly[T](implicit e : T) : T 例子: implicit val x = 1 val y

原生事件跨瀏覽器,dom0和dom2的區別?

1. DOM0級事件處理程式(屬性繫結,相容性好)通過javascript制定事件處理程式的傳統方式,將一個函式賦值給一個事件處理程式屬性。特點是簡單,跨瀏覽器。        var btn = document.getElementById("btn");btn.oncl

ASP.NET單值和多值c#+VS2010

一、單值繫結 新建一個ASP.NET網站模板->雙擊網站根目錄下的Default.aspx檔案,開啟設計檢視,從工具箱中拖一個label控制元件到設計檢視中->切換到源檢視中,設定label控制元件的屬性如下: <asp:Label ID="Label1