http://newdocx.appcan.cn/data-docking-interaction/MVVM
本章將講解在 Data Binding 中的布局及布局中如何使用表達式柬甥。
在布局中支持很多表達式和關鍵字:
數學運算: + - / * %
字符串連接: +
邏輯運算符: && ||
二進制運算符: & | ^
一元運算符: + - ! ~
轉變運算符(位移): >> >>> <<
比較運算符: == > < >= <=
instanceof: 類型判斷
Grouping ()
Literals - character,?String, numeric, null
Cast
Method calls
Field access
Array access []
三元運算符: ?:
看上去支持大部分表達式称开,但是在寫起來往往會遇到問題徘郭,會編譯都不過,這是因為在布局中有很多符號是不能直接使用的婉刀,需要轉義一下巢钓,比如小于號<要寫成<唉窃,轉義知識請自行學習窟蓝。例如:
<!--android:visibility="@{age < 10 ? View.GONE : View.VISIBLE}"此句編譯不過-->android:visibility="@{age < 10 ? View.GONE : View.VISIBLE}"
注意:如果這段代碼你直接拷貝到你項目中,可能依然編譯不過旭等,這是因為你沒有導入 View 包酌呆,在布局中寫表達式的時候特別要注意這一點,因為?java?文件寫慣了搔耕,導包都是自動的隙袁,而在布局中則需要手動導入,需要在布局中的 data 標簽中這樣寫:<import type="android.view.View" />弃榨,import?后面也會專門講解菩收。
上面的運算符和表達式在 java 代碼中應該都用過,但空合并運算符可能比較陌生鲸睛,空合并運算符是通過兩個問號來表達???娜饵,如果左操作數不為 null,則選擇左操作數官辈,如果為 null箱舞,則選擇右操作數。
android:text="@{user.remark ?? user.name}"
這在功能上等同于:
android:text="@{user.remark != null ? user.remark : user.name}"
為了方便使用拳亿,可以使用[]運算符訪問常用集合晴股,如數組、List肺魁、和Map电湘。下面在 UserInfo 中增加了幾個集合數據:
布局中的使用:
代碼中設置索引:
Map 的使用也可以直接引用 key 值,寫成這樣:
<TextView...android:text="@{@string/task(user.task.monday), default=今天任務}".../>
在布局中避免不了直接使用字符串,你可以使用單引號來包圍屬性值寂呛,這允許你在表達式中使用雙引號怎诫,如下面的例子所示:
android:text='@{@string/task(user.task["monday"]), default=今天任務}'
還可以使用雙引號來包圍屬性值, 這個時候字符串文本就需要被反引號包圍(反引號就是鍵盤的第二排第一個這個鍵值):
android:text="@{@string/task(user.task[`monday`]), default=今天任務}"
使用正常的表達式來訪問 Resources 也是可行的:
這里引用了 dimen 和 string贷痪,string 還可以帶格式化參數幻妓,當然也可以引用復數,但是一般情況下我們用不到劫拢,因為中文沒有這個需求涌哲。比如:
android:text="@{@plurals/banana(bananaCount)}"
除了這些,還支持其他的資源引用尚镰,但有些資源的引用需要明確指明類型,如下表所示:
Data Binding 允許你編寫表達式來處理 View 分派的事件哪廓。事件屬性名字取決于監(jiān)聽器方法名字狗唉。例如 View.OnClickListener 有 onClick() 方法,View.OnLongClickListener 有 onLongClick() 的方法涡真,因此事件的屬性是?android:onLongClick分俯,android:onClick。
對于 click 事件哆料,為了避免多種 click 事件的沖突缸剪,Google也定義了一些專門的事件處理,比如:
除了它們东亦,Google 還定義了其他一些常用的綁定事件的屬性杏节,這些可以閱讀 Data Binding 源碼(android.databinding.adapters 包下)或者?Google 官方 Data Binding 的 API。
事件綁定有兩種使用方式:引用方法和綁定監(jiān)聽器典阵。接下來將具體介紹這兩種方式的使用奋渔。
可以引用綁定對象中已經定義好的特定規(guī)則的 click 方法:
布局中的使用:
注意:被綁定的方法有一個 View 參數,這個參數是必須的壮啊,因為 Data Binding 在引用方法時嫉鲸,需要方法的參數和返回值必須與事件監(jiān)聽器的參數和返回值相匹配,如果參數或者返回值不匹配則會在編譯時報錯歹啼。
當表達式計算為引用方法的方式時玄渗,Data Binding 在監(jiān)聽器中包裝引用方法和所有者對象,并在目標 View 上設置該監(jiān)聽器狸眼,但是監(jiān)聽器對象是在數據綁定的時候創(chuàng)建的藤树,如果綁定的對象為空,這個監(jiān)聽器則不會創(chuàng)建份企。引用方法方式的優(yōu)點是找不到符合規(guī)定的方法則編譯報錯也榄。
綁定監(jiān)聽器是在布局中寫一個 lambda 表達式,表達式是在事件發(fā)生時被求值。它類似于引用方法甜紫,但允許你運行任意的數據綁定表達式降宅。這個特性是在 Android Gradle Plugin for Gradle version 2.0 或更高版本中才支持。以下示例為一個頁面跳轉的 click 事件綁定:
聲明了一個 startList() 方法囚霸,接著把按鈕點擊事件綁定到 startList() 方法上:
在引用方法的方式中腰根,方法的參數和返回值必須與事件監(jiān)聽器的參數和返回值相匹配。 在綁定監(jiān)聽器的方式中拓型,則只要返回值與監(jiān)聽器的預期返回值匹配即可额嘿。 例如:
一個長按事件的處理需要返回一個 Boolean 類型的值:
綁定監(jiān)聽器在編譯時會自動創(chuàng)建必要的監(jiān)聽器并為它注冊事件(監(jiān)聽器是一開始就創(chuàng)建好了,等到觸發(fā)時才會判斷被綁定的對象是否為空劣挫,為空則不執(zhí)行任何操作)册养。 當 View 觸發(fā)事件時,Data Binding 才會計算給定的表達式压固,在計算這些表達式時可以獲得 Data Binding 的 null 安全和線程安全性球拦。
有些時候 Click 方法可能需要帶一些必要的參數,比如:
在上面例子中帐我,showSign 方法需要一個 View 和 UserInfo 對象坎炼,在布局中這樣使用?@{(view)->activity.showSign(view, user)},view 是 lambada 表達式中獲取的拦键,user 是上面聲明需綁定的變量谣光。如果綁定方法是多個參數,或者監(jiān)聽器事件是帶返回值的都是以此類推芬为,保證參數和返回值匹配即可萄金。
如果需要使用帶有謂詞的表達式(例如,三元表達式) 碳柱,可以使用監(jiān)聽器相匹配的返回值類型作為表達式捡絮,比如 onCLick 屬性使用 void,onLongClick 屬性使用 Boolean莲镣。
android:onClick="@{(view)->view.isEnabled()?activity.showSign(view, user):void}"
android:onLongClick="@{(v)->v.isEnabled()?activity.showSign(user):false}"
注意:監(jiān)聽器表達式非常強大福稳,可以使您的代碼簡化,容易閱讀瑞侮。另一方面的圆,如果包含復雜表達式的監(jiān)聽器也會使你的布局難以理解和維護。布局中表達式應該盡量的簡單半火,你應該在監(jiān)聽器表達式調用的回調方法中實現(xiàn)相對復雜的業(yè)務邏輯越妈。
Data Binding 庫提供了?import,?variable?以及?include?標簽,import?使得可以在布局文件中引用類钮糖。variable?允許你聲明可在綁定表達式中使用的變量梅掠。include?可以讓你重用布局酌住。
import?可以讓你在布局中使用類,比如導入 View 類阎抒,導入 View 類允許你在綁定表達式中引用它的常量 VISIBLE 和 GONE酪我。
當存在類名沖突時,還可以將其中一個類重命名為別名且叁。 下面的示例將?com.example.databindingdemo.bean 包中的 View 類重命名為 Vista都哭,這樣可以使用 Vista 引用 com.example.databindingdemo.bean.View 類,使用 View 來引用系統(tǒng)中的 android.View.View逞带。
導入的類型可以用作變量和表達式中的類型引用欺矫。下面的示例顯示了用作變量類型的 UserInfo:
<importtype="com.example.databindingdemo.bean.UserInfo"/><variablename="user"type="UserInfo"/>
它等同于:
<variablename="user"type="com.example.databindingdemo.bean.UserInfo"/>
也可以導入類來做類型轉換,或者導入工具類來使用它的靜態(tài)方法:
但不是所有類都需要自己導包展氓,基本數據類型穆趴,String,以及 Data Binding 自己本身提供的 Observable 相關的類編譯器會自動導入遇汞。
可以在?data?標簽內部使用多個?variable毡代。 每個 variable 描述一個變量,該變量可以在布局文件中的綁定表達式中使用勺疼。下面的示例聲明了 UserInfo、Drawable 和 String 變量:
<data>
<variablename="user"type="com.example.User"/><variablename="image"type="android.graphics.drawable.Drawable"/><variablename="note"type="String"/>
</data>
在自動生成的綁定類中具有每個變量的 setter 和 getter 方法捏鱼,這些變量在調用 setter 方法賦值之前都有默認值执庐,對象為 null,int 為 0导梆,boolean 為 false 等等轨淌。同時也會生成一個名為 context 的特殊變量,以便根據需要在綁定表達式中使用看尼。context 的值是根 View 的 getContext()方法中的 Context 對象递鹉。以下為直接通過 context 變量獲取程序包名:
<TextView...android:text="@{context.packageName}".../>
但 context 變量可以被具有該名稱的顯式變量聲明所覆蓋,比如聲明了一個 String 類型的 context 變量:
<variablename="context"type="String"/>
這個時候就不能直接使用?@{context.packageName}?了藏斩,因為 context 已經被覆蓋為 String 類型躏结。
注意:當設備針對橫豎屏有不同的布局文件時,這些布局文件之間不能有沖突的變量定義狰域,必須保證不同配置的布局文件中的變量是一致的媳拴。
include?標簽和普通布局中使用的?include?是一樣的功能,都是導入一個已經存在的布局文件兆览,來實現(xiàn)布局的重用屈溉。只不過在 Data Binding 中它多了綁定數據的功能。下面展示了來自?layout_avatar.xml?布局文件:
這個布局很簡單抬探,里面只有一個 ImageView子巾,里面聲明了一個表達式?app:image="@{resId}"(這是一個自定義的適配器,自定義適配器后面會講到,這里不深究)线梗,它需要一個?resId?的變量椰于,接下來展示在?activity_user.xml?布局中的使用:
<data>
<import type="com.example.databindingdemo.bean.UserInfo"/><variable name="user"type="UserInfo"/>...
</data>..
.<include layout="@layout/layout_avatar"bind:resId="@{user.avatarId}"/>...
在布局中?include?了?layout_avatar.xml?文件,并聲明了一個屬性且綁定了表達式bind:resId="@{user.avatarId}"缠导,這個屬性就是?layout_avatar.xml?文件中的?resId?變量廉羔,它的規(guī)則就是:被?include?的布局里面的變量名就是這里綁定的屬性名。遵循這個規(guī)則僻造,就可以為?layout_avatar.xml?布局中的?resId?變量賦值憋他。
注意:Data Binding 不支持在 merge 標簽中直接 include 布局。