日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Data Binding 指南

發(fā)布時(shí)間:2023/12/14 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Data Binding 指南 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

翻譯自:Data Binding

本文檔說(shuō)明了如何使用Data Binding Library來(lái)編寫聲明式布局,并將必要的代碼最小化的綁定到應(yīng)用的邏輯和布局中。

Data Binding Library非常的靈活并且兼容性很廣 -- 它是一個(gè)兼容包,因此可以將它用到從Android 2.1(API level 7+)開始的所有的android平臺(tái)版本中。

要使用Data Binding,android 的構(gòu)建插件gradle要求1.5.0-alpha1或者更高的版本。

構(gòu)建環(huán)境

要開始使用Data Binding ,先從Android SDK manager的兼容庫(kù)中下載。

下載完成后需要進(jìn)行配置,添加 dataBinding 節(jié)點(diǎn)到app module的build.gradle 中。 android {....dataBinding {enabled = true} }如果你的app module依賴于另外一個(gè)使用了data binding的庫(kù),你的app module 的 build.gradle中也需要進(jìn)行配置。
另外,確保使用的是兼容版本的Android Studio開發(fā)工具。Android Studio 1.3及之后的版本為data binding提供支持描述,Android Studio Support For Data Binding。

Data Binding 布局文件

寫數(shù)據(jù)綁定式

Data-binding的布局文件和普通的布局文件相比,有一些略微的不同;Data-binding的布局文件的根節(jié)點(diǎn)是 layout ,隨后跟隨的是一個(gè) data 元素,再下面才是普通布局元素。此視圖元素將是一個(gè)非綁定布局文件的根目錄。一個(gè)簡(jiǎn)單的示例: <?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><LinearLayoutandroid: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>
在這個(gè)布局中使用 variable 描述數(shù)據(jù)屬性。 <variable name="user" type="com.example.User"/>在布局文件中的數(shù)據(jù)綁定表達(dá)式是寫入到節(jié)點(diǎn)的屬性中的,語(yǔ)法為:"@{}";這里TextView的text設(shè)置為用戶的firstName。<TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.firstName}"/>

數(shù)據(jù)對(duì)象

假設(shè)現(xiàn)在有一個(gè)普通的java對(duì)象(POJO) User:

public class User {public final String firstName;public final String lastName;public User(String firstName, String lastName) {this.firstName = firstName;this.lastName = lastName;} }這種類型的對(duì)象的數(shù)據(jù)不會(huì)發(fā)生更改。在應(yīng)用程序中,有一次讀取的數(shù)據(jù)是很常見的,此后數(shù)據(jù)不能更改。也可以使用JavaBeans對(duì)象: 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;} }從數(shù)據(jù)綁定的角度看,這兩個(gè)文件是一樣的。表達(dá)式 @{user.firstName} 作用于 TextView的 android:text="" 屬性上, 對(duì)于前一個(gè)文件將使用 firstName 字段,對(duì)于后一個(gè)文件將使用 getFirstName 方法獲取需要綁定的數(shù)據(jù)。或者,將會(huì)解析 firstName() 方法。 通俗的講:我們的數(shù)據(jù)實(shí)體對(duì)象的屬性可以是public類型的,如果是其它類型的則需要配置getter方法,或者直接使用字段名作為方法名的函數(shù)。

綁定數(shù)據(jù)

默認(rèn)的綁定類的名稱將根據(jù)布局文件名稱來(lái)生成,使用PascalCase(帕斯卡寫法)拼接上Binding后綴。上述布局文件是main_activity.xml所以生成類是MainActivityBinding。 這個(gè)類包含所有綁定的布局屬性(例如:user 變量)和知道如何分配值到綁定表達(dá)式。創(chuàng)建綁定的最簡(jiǎn)單方法是inflating:
@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.main_activity);User user = new User("Test", "User");binding.setUser(user); }做好了后,運(yùn)行程序,會(huì)發(fā)現(xiàn)測(cè)試的用戶數(shù)據(jù)顯示到了界面上。或者也可以通過get view的方式創(chuàng)建綁定類。 MainActivityBinding binding = MainActivityBinding.inflate(getLayoutInflater());如果是在ListView或者RecyclerView的item中使用數(shù)據(jù)綁定,則使用下面的方式: ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false); //or ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

綁定事件

事件可以直接綁定到方法上,類似于通過布局文件直接綁定方法( android:onClick ),事件屬性名是由偵聽器方法的名稱來(lái)管理的,有一些例外。例如:View.onLongClickListener?有一個(gè)方法?onLongClick,因此事件的屬性是?android:onLongClick。 要將事件分配給它的處理程序,使用一個(gè)標(biāo)準(zhǔn)的綁定表達(dá)式,以方法名稱作為值調(diào)用。例如,如果你的數(shù)據(jù)對(duì)象有2個(gè)方法: public class MyHandlers {public void onClickFriend(View view) { ... }public void onClickEnemy(View view) { ... } }綁定表達(dá)式可以指定一個(gè)View的OnClickListener:
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variable name="handlers" type="com.example.Handlers"/><variable name="user" type="com.example.User"/></data><LinearLayoutandroid: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="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.lastName}"android:onClick="@{user.isFriend ? handlers.onClickFriend : handlers.onClickEnemy}"/></LinearLayout> </layout>有一些單獨(dú)的事件,為了避免和 onclick?沖突,有專門的屬性,以下表格做了列舉:
Class(類)Listener Setter(類中設(shè)置監(jiān)聽器的方法)Attribute(屬性)
SearchViewsetOnSearchClickListener(View.OnClickListener)? ? ? ? ??android:onSearchClick ? ??
ZoomControls? ? ? ? ? ?setOnZoomInClickListener(View.OnClickListener)? ? ?android:onZoomIn
ZoomControlssetOnZoomInClickListener(View.OnClickListener)android:onZoomOut

布局明細(xì)

導(dǎo)包

在 data 節(jié)點(diǎn)可以有0個(gè)或者多個(gè)導(dǎo)入節(jié)點(diǎn),這樣你就能像在java中,使用另外一個(gè)類一樣的方便。 <data><import type="android.view.View"/> </data>如上面,通過 import 導(dǎo)入指向的View后,就可以直接在綁定表達(dá)式中使用 View 了。 <TextViewandroid:text="@{user.lastName}"android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>就如果上面,如果在 data 節(jié)點(diǎn)下使用了 import 導(dǎo)入了指向的View,就可以在android:visibility中使用View了。就跟在java代碼中的使用一樣。
如果導(dǎo)入了多個(gè)類,并且出現(xiàn)了命名沖突的情況,可以通過指定別名【alias】的方式來(lái)解決。
<import type="android.view.View"/> <import type="com.example.real.estate.View"alias="Vista"/>如上,導(dǎo)入了兩個(gè)類,結(jié)果兩個(gè)類的名稱都是View,這個(gè)時(shí)候,就為下面的類指定別名為 'Vista',Vista 就指向了 com.example.real.estate.View,而View就指向了 android.view.View類。導(dǎo)入類型可作為變量和表達(dá)式的類型引用:
<data><import type="com.example.User"/><import type="java.util.List"/><variable name="user" type="User"/><variable name="userList" type="List<User>"/> </data>注:Android Studio針對(duì) import 還沒有自動(dòng)補(bǔ)全功能。您的應(yīng)用程序仍然可以編譯通過,你可以在IDE的問題在你的變量定義使用完全限定的名稱。
<TextViewandroid:text="@{((User)(user.connection)).lastName}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>導(dǎo)入的類,也可以在表達(dá)式中,使用靜態(tài)的字段和方法(static):<data><import type="com.example.MyStringUtils"/><variable name="user" type="com.example.User"/> </data> … <TextViewandroid:text="@{MyStringUtils.capitalize(user.lastName)}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>就像在java中,java.lang.*是自動(dòng)導(dǎo)入的。

變量

在 data 節(jié)點(diǎn)下可以有任意數(shù)量的變量(variable),每一個(gè) variable描述了一個(gè)可以在布局文件中使用的屬性。 <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>

變量類型在編譯時(shí)檢查,所以如果一個(gè)變量實(shí)現(xiàn)?Observable?或是?observable collection,則應(yīng)該在該類型中反映。如果變量是一個(gè)基類或接口,沒有實(shí)現(xiàn)Observable*?接口,將不被觀察到的變量!

當(dāng)有不同的配置文件(例如,橫向或縱向),變量將被合并。在這些布局文件之間不存在沖突的變量定義。

生成綁定類將有一個(gè)為每個(gè)描述變量的setter和getter。這些變量在調(diào)用setter之前,將采用默認(rèn)值 - 引用類型為null,int為0,boolean為false等。

一個(gè)特殊的變量命名為的context。context的值是從根目錄的getcontext()語(yǔ)境。context變量將具有該名稱的顯式變量聲明重寫。


自定義綁定類名


默認(rèn)的,綁定的類名是根據(jù)布局文件的名稱來(lái)生成的,大寫字母開頭,去除 '_',然后拼接上 '_' 后的單詞,然后跟上 ‘Binding’ 。這個(gè)類將被放置在一個(gè) module 包的 databinding 包下。例如:如果一個(gè)布局文件的名稱是,contact_item.xml生成的綁定文件名稱是 ContactItemBinding。如果模塊包為com.example.my.app然后生成的綁定類將被放到?com.example.my.app.databinding 。
綁定類的名稱,也能通過 data 節(jié)點(diǎn)的 class 屬性重命名或者放到不同的包下面。 <data class="ContactItem">... </data>這個(gè)例子生成的綁定類名是ContactItem,并且放到module包的databinding下面。如果想要放到不同的包下面,可以通過使用 '.' 前綴: <data class=".ContactItem">... </data>在這個(gè)例子中,生成的綁定類 ContactItem將被放到module的包目錄下,如果指定了全類名,可以放到任意包下: <data class="com.example.ContactItem">... </data>

Includes

變量可以通過應(yīng)用程序的命名空間和屬性變量的方式,將變量傳遞給通過 include 引入的布局文件中:
<?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><LinearLayoutandroid: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>在這個(gè)例子中,在 name.xml contact.xml 布局文件中都能使用 user 變量。
Data Binding 不支持 include 作為 merge 節(jié)點(diǎn)的直接子類的方式,例如,以下示例是不支持的
<?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><include layout="@layout/name"bind:user="@{user}"/><include layout="@layout/contact"bind:user="@{user}"/></merge> </layout>

Expression Language(表達(dá)式·)

Common Features(通用的特點(diǎn))

綁定表達(dá)式跟java表達(dá)式一樣,有一些通用的符合:
  • 數(shù)學(xué)符合(Mathematical) : + ?- ? * ? / ? %
  • 字符串拼接符合(String Concatenation):+
  • 邏輯運(yùn)算符(Logical):&&【并且】 ? ||【或者】
  • 位操作符(Binary):&【與】 ?|【或】 ?^【異或】
  • 單目運(yùn)算符(Unary):+【取正,正號(hào)】 ?-【取負(fù),負(fù)號(hào)】 ?!【非】 ?~【取反】
  • 位運(yùn)算符(Shift):>>【帶符合右移】 ?>>>【無(wú)符號(hào)右移】 ?<<【帶符號(hào)左移】
  • 比較運(yùn)算符(Comparison):==【等于】 ?>【大于】 <【小于】 >=【大于等于】 <=【小于等于】
  • instanceof運(yùn)算符
  • 分租(Grouping):()【小括弧】
  • 常量(Literals):character(字母)、String(字符串)、numeric(數(shù)字)、null
  • 數(shù)據(jù)類型轉(zhuǎn)換(Cast)
  • 方法調(diào)用(Method Calls)
  • 字段調(diào)用(Field Access)
  • 數(shù)組調(diào)用(Array access):[]
  • 三目運(yùn)算符:? :【類似于if-else】
例如: android:text="@{String.valueOf(index + 1)}" android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}" android:transitionName='@{"image_" + id}'

缺少的操作

有一些操作在表達(dá)式語(yǔ)法中是沒有的,你可以在java代碼中使用。
  • this
  • super
  • new
  • 顯示泛型調(diào)用

Null合并運(yùn)算符

Null合并運(yùn)算符(??)類似于三目運(yùn)算符(? : )【if-else】 android:text="@{user.displayName ?? user.lastName}"上面的代碼等價(jià)于: android:text="@{user.displayName != null ? user.displayName : user.lastName}"

屬性引用(Property Reference)

在上面一開始講的時(shí)候,就講過通過表達(dá)式對(duì)于實(shí)體對(duì)象數(shù)據(jù)的引用,對(duì)于字段(fields)、getters、ObservableFields的引用和普通對(duì)象數(shù)據(jù)的引用是一樣的。 android:text="@{user.lastName}"

避免空指針異常

Data Binding會(huì)自動(dòng)檢查null,避免出現(xiàn)空指針異常;例如:如果表達(dá)式為?@{user.name},如果user為空,則user.name則會(huì)賦予默認(rèn)值(null),如果 @{user.age},age字段是int型的,則默認(rèn)值是0。

集合

常用的集合:arrays, lists, sparse lists, 和 maps都可以通過 [] 訪問。 <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]}"

字符串

當(dāng)使用單引號(hào)作為屬性引用的時(shí)候,其中的字符串可以使用雙引號(hào): android:text='@{map["firstName"]}'屬性引用使用雙引號(hào),也是可以的,這個(gè)時(shí)候,字符串的引用則通過?&quot;或者?`【鍵盤上1前面的那個(gè)符合(不按shift)】來(lái)引用。 android:text="@{map[`firstName`}" android:text="@{map[&quot;firstName&quot;]}"

Resources(資源)

使用正規(guī)的表達(dá)式來(lái)訪問資源文件也是可以的:
android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"格式字符串和復(fù)數(shù)可提供參數(shù): android:text="@{@string/nameFormat(firstName, lastName)}" android:text="@{@plurals/banana(bananaCount)}"當(dāng)一個(gè)復(fù)數(shù)需要多個(gè)參數(shù)時(shí),所有參數(shù)都應(yīng)該通過:
Have an orangeHave %d orangesandroid:text="@{@plurals/orange(orangeCount, orangeCount)}"一些資源需要明確類型調(diào)用。
Type【類型】 Normal Reference【正常引用】 Expression Reference【表達(dá)式引用】
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color?int @color @color
ColorStateList @color @colorStateList

數(shù)據(jù)對(duì)象(Data Objects)

任何普通的java對(duì)象(POJO)都可用于數(shù)據(jù)綁定,但修改一個(gè)POJO不會(huì)造成UI更新。數(shù)據(jù)綁定的真正核心,可以用在給你的數(shù)據(jù)發(fā)生變化時(shí),通知數(shù)據(jù)對(duì)象。有三種不同的數(shù)據(jù)改變通知機(jī)制,可觀察的對(duì)象,可觀察的領(lǐng)域,和可觀察的集合。Observable objects,?observable fields, 和?observable collections.
當(dāng)這些observable數(shù)據(jù)對(duì)象被綁定到UI界面時(shí),數(shù)據(jù)對(duì)象的屬性發(fā)生更改時(shí),UI也將自動(dòng)更新。

Observable 對(duì)象(Observable Objects)

一個(gè)類實(shí)現(xiàn)?Observable?接口時(shí),允許添加一個(gè)監(jiān)聽器到綁定的對(duì)象上,監(jiān)聽數(shù)據(jù)的變化。 Observable?接口提供了一個(gè)添加和刪除監(jiān)聽器的機(jī)制,但通知是由開發(fā)人員決定的。為了使開發(fā)更容易,Data Binding提供了一個(gè)基類 --?BaseObservable?是為了實(shí)現(xiàn)偵聽器注冊(cè)機(jī)制而創(chuàng)建的。當(dāng)數(shù)據(jù)屬性發(fā)生變化時(shí)數(shù)據(jù)實(shí)現(xiàn)類依然負(fù)責(zé)進(jìn)行通知,這是通過給getter方法指定一個(gè)?Bindable?注解,然后在setter中進(jìn)行通知來(lái)完成的。
private static class User extends BaseObservable {private String firstName;private String lastName;@Bindablepublic String getFirstName() {return this.firstName;}@Bindablepublic 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);} }在編譯期間,Bindable?將在BR文件中生成一條記錄。BR文件將在模塊包中(module package)生成。如果數(shù)據(jù)類不能被更改,?Observable?接口通過方便的PropertyChangeRegistry來(lái)實(shí)現(xiàn)用于儲(chǔ)存和有效地通知監(jiān)聽者。

Observable字段

一些小工作會(huì)涉及到創(chuàng)建Observable類,因此一些開發(fā)者想節(jié)省時(shí)間或者有少量的字段的可以使用?ObservableField?和??ObservableBoolean,?ObservableByte,?ObservableChar,?ObservableShort,?ObservableInt,?ObservableLong,?ObservableFloat,ObservableDouble, and?ObservableParcelable.ObservableFields是自包含具有單個(gè)字段的 ObservableField 對(duì)象。原始版本避免裝箱和拆箱過程中訪問操作。要使用ObservableField,需要在類中創(chuàng)建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(); }就是這樣,要使用值,需要使用set和get方法: user.firstName.set("Google"); int age = user.age.get();

Observable 集合

一些應(yīng)用程序使用更多的動(dòng)態(tài)結(jié)構(gòu)來(lái)保存數(shù)據(jù)。Observable允許鍵訪問這些數(shù)據(jù)對(duì)象,也就是說(shuō)Observable是通過key-value的方式存儲(chǔ)數(shù)據(jù)的。類似于java中的Map和List。ObservableArrayMap?使用key是引用類型的,例如:String,類似于java中的Map。

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>(); user.put("firstName", "Google"); user.put("lastName", "Inc."); user.put("age", 17);在layout布局文件中,可以通過String鍵訪問map:

<data><import type="android.databinding.ObservableMap"/><variable name="user" type="ObservableMap<String, Object>"/> </data> … <TextViewandroid:text='@{user["lastName"]}'android:layout_width="wrap_content"android:layout_height="wrap_content"/> <TextViewandroid:text='@{String.valueOf(1 + (Integer)user["age"])}'android:layout_width="wrap_content"android:layout_height="wrap_content"/>ObservableArrayList?用于key是整數(shù)的情況,類似于java中的List:

ObservableArrayList<Object> user = new ObservableArrayList<>(); user.add("Google"); user.add("Inc."); user.add(17);在layout布局文件中,通過索引訪問list:
<data><import type="android.databinding.ObservableList"/><import type="com.example.my.app.Fields"/><variable name="user" type="ObservableList&lt;Object>"/> </data> … <TextViewandroid:text='@{user[Fields.LAST_NAME]}'android:layout_width="wrap_content"android:layout_height="wrap_content"/> <TextViewandroid:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'android:layout_width="wrap_content"android:layout_height="wrap_content"/>

生成Binding類

生成的綁定文件指向了布局中的變量與視圖。就如前面所講的一樣,生成的綁定類可以自定義名稱和包名。所有生成的綁定類都擴(kuò)展自ViewDataBinding

創(chuàng)建(Creating)

應(yīng)該在inflation之后創(chuàng)建,以確保在布局中與表達(dá)式的視圖結(jié)合之前不干擾視圖層次結(jié)構(gòu)。有幾個(gè)方法可以綁定到布局。最常用的是使用綁定類的靜態(tài)方法。從加載View層次結(jié)構(gòu)到綁定只需要一步。可以通過只需要一個(gè)LayoutInflater的方式綁定,也可以再傳遞ViewGroup的方式:

MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater); MyLayoutBinding binding = MyLayoutBinding.inflate(layoutInflater, viewGroup, false);如果布局是使用不同的結(jié)構(gòu),它可能是單獨(dú)的:
MyLayoutBinding binding = MyLayoutBinding.bind(viewRoot);有時(shí)候不能提前知道綁定類,這種情況下,可以通過?DataBindingUtil?類來(lái)進(jìn)行獲取綁定:
ViewDataBinding binding = DataBindingUtil.inflate(LayoutInflater, layoutId,parent, attachToParent); ViewDataBinding binding = DataBindingUtil.bindTo(viewRoot, layoutId);

帶id的View

Data Binding對(duì)于每一個(gè)布局文件中帶id的View都會(huì)生成一個(gè)public final 的 View字段。Binding只是在頁(yè)面層次結(jié)構(gòu)上做簡(jiǎn)單的傳遞,提取帶id的View。這種機(jī)制在某些情況下比傳統(tǒng)的findViewById要快,例如:

<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variable name="user" type="com.example.User"/></data><LinearLayoutandroid: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}"<span style="white-space:pre"> </span> android:id="@+id/firstName"/><TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.lastName}"<span style="white-space:pre"> </span> android:id="@+id/lastName"/></LinearLayout> </layout>生成的綁定類如下:

public final TextView firstName; public final TextView lastName;對(duì)于Data Binding并不是完全沒有必要再去獲取一個(gè)View實(shí)例,再某些時(shí)候也需要去訪問View。

Variables

對(duì)于每一個(gè)Variable(變量)都有一個(gè)訪問方法:

<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>在綁定類中都將生成getter和setter方法:

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

ViewStub?跟普通的視圖有一些不同。他們開始時(shí)并不是可見的,當(dāng)明確要顯示或被載入時(shí),通過加載另一個(gè)布局文件來(lái)替換自己。

因?yàn)閂iewStub基本是隱藏的,所以在Data Binding中,ViewStub也是隱藏的。在Data Binding中,當(dāng)ViewStub被加載完成后,一個(gè)ViewStub將被轉(zhuǎn)換為一個(gè)ViewStubProxy,供開發(fā)者訪問ViewStub。

當(dāng)加載另一個(gè)文件的時(shí)候,必須建立新的布局。因此,ViewStubProxy 必須監(jiān)聽 ViewStub的?ViewStub.OnInflateListener?事件。由于只有一個(gè)視圖存在,這ViewStubProxy允許開發(fā)人員建立一個(gè)onInflateListener監(jiān)聽它。

<layout xmlns:android="http://schemas.android.com/apk/res/android"><LinearLayout...><ViewStubandroid:id="@+id/view_stub"android:layout="@layout/view_stub"... /></LinearLayout> </layout>布局文件中添加一個(gè)i ViewStub 并且添加id屬性。

binding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub); binding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {@Overridepublic void onInflate(ViewStub stub, View inflated) {ViewStubBinding binding = DataBindingUtil.bind(inflated);User user = new User("fee", "lang");binding.setUser(user);} });在 Java 代碼中獲取?binding?實(shí)例?ViewStubProy?注冊(cè)?ViewStub.OnInflateListener?事件。

高級(jí)Binding(Binding進(jìn)階)(Advanced Binding)

動(dòng)態(tài)變量(Dynamic Variables)

有時(shí)候,并不知道明確的綁定類。例如:RecyclerView.Adapter對(duì)于layout布局的操作并不知道具體的綁定類,它仍然需要在onBindViewHolder(VH, int)中綁定值。

在下面這個(gè)例子中,RecyclerView綁定的所有的布局文件中,都有一個(gè) 'item' 的變量。BindingHolder有一個(gè)getBinding方法返回ViewDataBinding實(shí)例.

public void onBindViewHolder(BindingHolder holder, int position) {final T item = mItems.get(position);holder.getBinding().setVariable(BR.item, item);holder.getBinding().executePendingBindings(); }

立即綁定(Immediate Binding)

當(dāng)一個(gè)變量或Observable變化時(shí),該綁定將在下一幀之前更改。然而有時(shí)候,當(dāng)想要立即綁定更改的時(shí)候,可以調(diào)用?executePendingBindings()強(qiáng)制執(zhí)行。

后臺(tái)線程(Background Thread)

你可以在后臺(tái)線程中改變你的數(shù)據(jù)模型,只要它不是集合。數(shù)據(jù)綁定將本地化每個(gè)變量(Variable)/字段(Field),同時(shí)進(jìn)行評(píng)估,以避免任何并發(fā)問題。

屬性設(shè)置(Attribute Setters)

每當(dāng)一個(gè)綁定值的變化,Data Binding生成的綁定類一定會(huì)調(diào)用配置了綁定表達(dá)式的View相關(guān)屬性的setter方法。Data Binding框架有自定義設(shè)置值的方法。

Automatic Setters

對(duì)于一個(gè)屬性,Data Binding框架會(huì)嘗試去尋找相應(yīng)的setter方法。與該屬性的namespace(命名空間)并不什么關(guān)系,僅僅與屬性本身名稱有關(guān)。例如:在TextView的?android:text配置了綁定表達(dá)式過后,框架會(huì)去尋找setText(String)方法。如果綁定表達(dá)式返回的值是int類型的,框架會(huì)尋找 setText(int) 方法。確保表達(dá)式返回正確的數(shù)據(jù)類型,如果需要的話可以使用數(shù)據(jù)類型轉(zhuǎn)換。注意:即使沒有屬性存在于給定的名稱,Data Binding也會(huì)正常運(yùn)行。您可以很輕松的使用Data Binding框架為一些 setter 方法創(chuàng)建屬性。例如:對(duì)于 support v4包中的?DrawerLayout 并沒有任何的屬性,但是有很多的setter方法。您可以自動(dòng)的去使用這些setter方法: <android.support.v4.widget.DrawerLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:scrimColor="@{@color/scrim}"app:drawerListener="@{fragment.drawerListener}"/>

重命名Setter(Renamed Setters)

有一些屬性的setter方法并不能通過名稱來(lái)匹配。對(duì)于這些方法,屬性可以通過?BindingMethods注解來(lái)進(jìn)行關(guān)聯(lián)。每一個(gè)用于重命名的方法都必須與一個(gè)包含 BindingMethods 注解的類相關(guān)聯(lián)。例如:對(duì)于android:tint屬性,與之相關(guān)聯(lián)的setter方法是setImageTintList(ColorStateList)并不是setTint。

@BindingMethods({@BindingMethod(type = "android.widget.ImageView",attribute = "android:tint",method = "setImageTintList"), })實(shí)際開發(fā)過程中,開發(fā)人員想要重命名這些屬性是不可能的,因?yàn)閍ndroid底層的屬性都已經(jīng)被實(shí)現(xiàn)了。

自定義Setter方法(Custome Setters)

有一些屬性需要自定義綁定邏輯。例如:對(duì)于?android:paddingLeft?屬性,Data Binding并沒有配置setter方法。相反,setPadding(left, top, right, bottom)方法是存在的。一個(gè)靜態(tài)的綁定了BindingAdapter注解的方法允許開發(fā)者為一個(gè)屬性自定義一個(gè)調(diào)用的setter方法。

android的屬性已經(jīng)創(chuàng)建了BindingAdapter,例如:對(duì)于paddingLeft屬性:

@BindingAdapter("android:paddingLeft") public static void setPaddingLeft(View view, int padding) {view.setPadding(padding,view.getPaddingTop(),view.getPaddingRight(),view.getPaddingBottom()); }Data Binding適配器可以用于定制其它類型。例如,可以自定義loader方法用來(lái)在線程中加載圖片:

當(dāng)有沖突時(shí),開發(fā)人員創(chuàng)建的綁定適配器將重寫Data Binding的默認(rèn)適配器。

您還可以創(chuàng)建接收多個(gè)參數(shù)的適配器。

@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}” <span style="white-space:pre"> </span>app:error=“@{@drawable/venueError}”/>對(duì)于一個(gè)ImageView,如果同時(shí)存在imageUrl和error并且imageUrl為String類型,error是drawable類型適配器將被調(diào)用:

  • 自定義的命名空間(namespace)將忽略匹配
  • 同時(shí)也可以為命名空間(namespace)編寫適配器
綁定適配器方法可以選擇在其處理程序中選擇舊值。一個(gè)操作舊值和新值的方法,首先應(yīng)該有屬性的所有舊值,其次要有新值:

@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());} }事件處理程序可以只使用一個(gè)接口(interface)或抽象類的抽象方法,例如:

@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);}} }當(dāng)一個(gè)偵聽器有多種方法時(shí),它必須被分割成多個(gè)偵聽器。例如:?View.OnAttachStateChangeListener?有兩個(gè)方法:onViewAttachedToWindow()?和?onViewDetachedFromWindow().然后,我們必須創(chuàng)建一個(gè)接口來(lái)區(qū)分它們的屬性和處理程序。@TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewDetachedFromWindow {void onViewDetachedFromWindow(View v); }@TargetApi(VERSION_CODES.HONEYCOMB_MR1) public interface OnViewAttachedToWindow {void onViewAttachedToWindow(View v); }因?yàn)楦淖円粋€(gè)偵聽器也會(huì)影響另一個(gè),所以我們必須有三個(gè)不同的綁定適配器,一個(gè)用于每個(gè)屬性,一個(gè)是兩個(gè),它們都應(yīng)該被設(shè)置。
@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() {@Overridepublic void onViewAttachedToWindow(View v) {if (attach != null) {attach.onViewAttachedToWindow(v);}}@Overridepublic 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);}} }上面的例子是比正常的稍微復(fù)雜,視圖使用的是添加和刪除監(jiān)聽器的方式來(lái)替換View.OnAttachStateChangeListener的設(shè)置方法。android.databinding.adapters.ListenerUtil類幫助追蹤以前的監(jiān)聽器,因此,他們可能會(huì)在綁定適配器中刪除。
通過在OnViewDetachedFromWindow和OnViewAttachedToWindow接口上使用@TargetApi(VERSION_CODES.HONEYCOMB_MR1),Data Binding生成的代碼知道監(jiān)聽器只有運(yùn)行在Honeycomb MR1和更新的設(shè)備上才生成。相同的版本支持addOnAttachStateChangeListener(View.OnAttachStateChangeListener).

轉(zhuǎn)換(Converts)

對(duì)象轉(zhuǎn)換(Object Conversions)

當(dāng)從數(shù)據(jù)表達(dá)式返回?cái)?shù)據(jù)對(duì)象時(shí),將從自動(dòng),重命名以及自定義的setter方法中進(jìn)行選擇。數(shù)據(jù)對(duì)象將轉(zhuǎn)換為setter方法的參數(shù)類型。

這是為了方便使用 ObservableMap 來(lái)保存數(shù)據(jù),例如:

<TextViewandroid:text='@{userMap["lastName"]}'android:layout_width="wrap_content"android:layout_height="wrap_content"/>userMap?返回的對(duì)象將被自動(dòng)轉(zhuǎn)換為找到的text的setter方法setText(CharSequence)的參數(shù)的數(shù)據(jù)類型。當(dāng)有關(guān)的參數(shù)可能產(chǎn)生類型混亂時(shí),就需要開發(fā)人員在表達(dá)式中進(jìn)行轉(zhuǎn)換。

自定義轉(zhuǎn)換(Custom Conversions)

有時(shí)候轉(zhuǎn)換為自動(dòng)的在特定的類型之間。例如:當(dāng)設(shè)置背景顏色時(shí): <Viewandroid:background="@{isError ? @color/red : @color/white}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>在這里設(shè)置背景需要的是Drawable,但是顏色color是一個(gè)integer(整型)的。每當(dāng)一個(gè)Drawable返回的是整形(int)的時(shí)候,int應(yīng)該被轉(zhuǎn)換為ColorDrawable類型。這個(gè)轉(zhuǎn)換是通過使用帶有BindingConversion注解的靜態(tài)方法完成的
@BindingConversion public static ColorDrawable convertColorToDrawable(int color) {return new ColorDrawable(color); }注意,轉(zhuǎn)換只發(fā)生在setter級(jí),所以它是不允許這樣的組合類型:
<Viewandroid:background="@{isError ? @drawable/error : @color/white}"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

Android Studio 支持 Data Binding

Android Studio支持Data Binding的多種代碼編輯。例如,它支持以下功能的數(shù)據(jù)綁定表達(dá)式:

  • 語(yǔ)法高亮
  • 標(biāo)記表達(dá)式語(yǔ)言的語(yǔ)法錯(cuò)誤

  • XML代碼實(shí)現(xiàn)

  • 引用,包括導(dǎo)航(如導(dǎo)航到聲明)和快速文檔

  • 注意:數(shù)組(集合)、泛型以及 Observable 類,在沒有錯(cuò)誤的時(shí)候,也可能顯示錯(cuò)誤。

預(yù)覽窗格顯示提供的數(shù)據(jù)綁定表達(dá)式的默認(rèn)值。從以下布局的XML文件的元素實(shí)例摘錄,預(yù)覽窗格中顯示的默認(rèn)文本值的占位符文本。

<TextView android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{user.firstName, default=PLACEHOLDER}"/>

如果你需要你的項(xiàng)目的設(shè)計(jì)階段中顯示一個(gè)默認(rèn)值,你也可以使用工具屬性來(lái)替換默認(rèn)的表達(dá)式的值,詳細(xì)描述請(qǐng)看:?Designtime Layout Attributes。







總結(jié)

以上是生活随笔為你收集整理的Data Binding 指南的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。