(三、3)布局和綁定表達式

表達式語言允許您編寫處理視圖調(diào)度的事件的表達式。 數(shù)據(jù)綁定庫自動生成將布局中的視圖與數(shù)據(jù)對象綁定所需的類。

數(shù)據(jù)綁定布局文件略有不同,以布局的根標簽開頭,后跟數(shù)據(jù)元素和視圖根元素。 此視圖元素是您的根在非綁定布局文件中的位置。 以下代碼顯示了一個示例布局文件:

<?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>
   <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}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

數(shù)據(jù)中的user變量描述了可在此布局中使用的屬性。

<variable name="user" type="com.example.User" />

布局中的表達式使用“@ {}”語法寫入屬性屬性中。 這里,TextView文本設(shè)置為用戶變量的firstName屬性:

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

注意:布局表達式應(yīng)保持小而簡單,因為它們不能進行單元測試并且IDE支持有限。 為了簡化布局表達式,您可以使用自定義綁定適配器。

一、數(shù)據(jù)對象

我們現(xiàn)在假設(shè)您有一個普通的對象來描述User實體:


public class User {
  public final String firstName;
  public final String lastName;
  public User(String firstName, String lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  }
}

這種類型的對象具有永不改變的數(shù)據(jù)。 在應(yīng)用程序中,通常會讀取一次并且之后不會更改的數(shù)據(jù)。 也可以使用遵循一組約定的對象,例如Java中的訪問器方法的使用,如以下示例所示:

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ù)綁定的角度來看,這兩個類是等價的。 用于android:text屬性的表達式@ {user.firstName}訪問前一類中的firstName字段和后一類中的getFirstName()方法。 或者,如果該方法存在,它也會解析為firstName()。

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

為每個布局文件生成綁定類。 默認情況下,類的名稱基于布局文件的名稱,將其轉(zhuǎn)換為Pascal大小寫并向其添加Binding后綴。 上面的布局文件名是activity_main.xml,因此相應(yīng)的生成類是ActivityMainBinding。 此類包含布局屬性(例如,用戶變量)到布局視圖的所有綁定,并知道如何為綁定表達式指定值。創(chuàng)建綁定的推薦方法是在擴展布局時執(zhí)行此操作,如 如下例所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
   User user = new User("Test", "User");
   binding.setUser(user);
}

在運行時,應(yīng)用程序在UI中顯示Test用戶。 或者,您可以使用LayoutInflater獲取視圖,如以下示例所示:

ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());

如果在Fragment,ListView或RecyclerView適配器中使用數(shù)據(jù)綁定項,則可能更喜歡使用綁定類或DataBindingUtil類的inflate()方法,如以下代碼示例所示:

ListItemBinding binding = ListItemBinding.inflate(layoutInflater, viewGroup, false);
// or
ListItemBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.list_item, viewGroup, false);

三、表達語言

共同特征

表達式語言看起來很像托管代碼中的表達式。 您可以在表達式語言中使用運算符和關(guān)鍵字:
例子:

android:text="@{String.valueOf(index + 1)}"
android:visibility="@{age < 13 ? View.GONE : View.VISIBLE}"
android:transitionName='@{"image_" + id}'
缺少操作

您可以在托管代碼中使用的表達式語法中缺少以下操作:

  • this
  • super
  • new
  • 顯式通用調(diào)用
空結(jié)合運算符

空合并運算符(??)選擇左操作數(shù)(如果它不為空)或右(如果前者為空)。

android:text="@{user.displayName ?? user.lastName}"

這在功能上等同于:

android:text="@{user.displayName != null ? user.displayName : user.lastName}"
屬性參考

表達式可以使用以下格式引用類中的屬性,這對于fields,getters和ObservableField對象是相同的:

android:text="@{user.lastName}"
避免空指針異常

生成的數(shù)據(jù)綁定代碼會自動檢查空值并避免空指針異常。 例如,在表達式@ {user.name}中,如果user為null,則為user.name分配其默認值null。 如果引用user.age,其中age的類型為int,則數(shù)據(jù)綁定使用默認值0。

集合

為方便起見,可以使用[ ]運算符訪問公共集合,例如數(shù)組,列表,稀疏列表和映射。

<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]}"

注意:您還可以使用object.key表示法引用地圖中的值。 例如,上面示例中的@ {map [key]}可以替換為@ {map.key}。

字符串文字

您可以使用單引號括起屬性值,這允許您在表達式中使用雙引號,如以下示例所示:

android:text='@{map["firstName"]}'

也可以使用雙引號來包圍屬性值。 這樣做時,字符串文字應(yīng)該用后引號`包圍:

android:text="@{map[`firstName`]}"
資源

您可以使用以下語法訪問表達式中的資源:

android:padding="@{large? @dimen/largePadding : @dimen/smallPadding}"

可以通過提供參數(shù)來評估格式字符串和復(fù)數(shù):

android:text="@{@string/nameFormat(firstName, lastName)}"
android:text="@{@plurals/banana(bananaCount)}"

當復(fù)數(shù)采用多個參數(shù)時,應(yīng)傳遞所有參數(shù):


  Have an orange
  Have %d oranges

android:text="@{@plurals/orange(orangeCount, orangeCount)}"

某些資源需要顯式類型評估,如下表所示:
略。

四、事件處理

數(shù)據(jù)綁定允許您編寫從視圖調(diào)度的表達式處理事件(例如,onClick()方法)。 事件屬性名稱由偵聽器方法的名稱確定,但有一些例外。 例如,View.OnClickListener有一個onClick()方法,因此該事件的屬性是android:onClick。
click事件有一些專門的事件處理程序需要除android:onClick以外的屬性以避免沖突。 您可以使用以下屬性來避免這些類型的沖突:

您可以使用以下機制來處理事件:

  • 方法參考:在表達式中,您可以引用符合偵聽器方法簽名的方法。 當表達式求值為方法引用時,Data綁定將方法引用和所有者對象包裝在偵聽器中,并在目標視圖上設(shè)置該偵聽器。 如果表達式求值為null,則數(shù)據(jù)綁定不會創(chuàng)建偵聽器并改為設(shè)置空偵聽器。
  • 偵聽器綁定:這些是在事件發(fā)生時計算的lambda表達式。 數(shù)據(jù)綁定總是創(chuàng)建一個偵聽器,它在視圖上設(shè)置。 調(diào)度事件時,偵聽器會計算lambda表達式。
方法參考

事件可以直接綁定到處理程序方法,類似于android:onClick的方式可以分配給活動中的方法。 與View onClick屬性相比,一個主要優(yōu)點是表達式在編譯時處理,因此如果該方法不存在或其簽名不正確,則會收到編譯時錯誤。
方法引用和偵聽器綁定之間的主要區(qū)別在于,實際的偵聽器實現(xiàn)是在綁定數(shù)據(jù)時創(chuàng)建的,而不是在觸發(fā)事件時創(chuàng)建的。 如果您希望在事件發(fā)生時評估表達式,則應(yīng)使用偵聽器綁定。
要將事件分配給其處理程序,請使用普通綁定表達式,其值為要調(diào)用的方法名稱。 例如,請考慮以下示例布局數(shù)據(jù)對象:

public class MyHandlers {
    public void onClickFriend(View view) { ... }
}

綁定表達式可以將視圖的單擊偵聽器分配給onClickFriend()方法,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="handlers" type="com.example.MyHandlers"/>
       <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:onClick="@{handlers::onClickFriend}"/>
   </LinearLayout>
</layout>

注意:表達式中方法的簽名必須與偵聽器對象中方法的簽名完全匹配。

監(jiān)聽器綁定

偵聽器綁定是在事件發(fā)生時運行的綁定表達式。 它們類似于方法引用,但它們允許您運行任意數(shù)據(jù)綁定表達式。 適用于Gradle版本2.0及更高版本的Android Gradle插件提供此功能。
在方法引用中,方法的參數(shù)必須與事件偵聽器的參數(shù)匹配。 在偵聽器綁定中,只有您的返回值必須與偵聽器的預(yù)期返回值匹配(除非它期望無效)。 例如,考慮以下具有onSaveClick()方法的presenter類:

public class Presenter {
    public void onSaveClick(Task task){}
}

然后,您可以將click事件綁定到onSaveClick()方法,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable name="task" type="com.android.example.Task" />
        <variable name="presenter" type="com.android.example.Presenter" />
    </data>
    <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent">
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:onClick="@{() -> presenter.onSaveClick(task)}" />
    </LinearLayout>
</layout>

在表達式中使用回調(diào)時,數(shù)據(jù)綁定會自動創(chuàng)建必要的偵聽器并為事件注冊它。 當視圖觸發(fā)事件時,數(shù)據(jù)綁定會評估給定的表達式。 與常規(guī)綁定表達式一樣,在評估這些偵聽器表達式時,仍然可以獲得數(shù)據(jù)綁定的null和線程安全性。
上面的示例中,我們尚未定義傳遞給onClick(View)的視圖參數(shù)。 監(jiān)聽器綁定為監(jiān)聽器參數(shù)提供了兩種選擇:您可以忽略方法的所有參數(shù),也可以命名所有參數(shù)。 如果您更喜歡為參數(shù)命名,可以在表達式中使用它們。 例如,上面的表達式可以寫成如下:

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

或者,如果要在表達式中使用該參數(shù),則可以按如下方式工作:

public class Presenter {
    public void onSaveClick(View view, Task task){}
}
android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

你可以使用帶有多個參數(shù)的lambda表達式:

public class Presenter {
    public void onCompletedChanged(Task task, boolean completed){}
}
<CheckBox android:layout_width="wrap_content" 
          android:layout_height="wrap_content"
          android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

如果您正在偵聽的事件返回類型不為void的值,則表達式也必須返回相同類型的值。 例如,如果要監(jiān)聽長按事件,則表達式應(yīng)返回布爾值。

public class Presenter {
    public boolean onLongClick(View view, Task task) { }
}
android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

如果由于null對象而無法計算表達式,則數(shù)據(jù)綁定將返回該類型的默認值。 例如,引用類型為null,int為0,布爾值為false等。
如果需要使用帶謂詞的表達式(例如,三元),則可以使用void作為符號。

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"
避免復(fù)雜的聽眾

監(jiān)聽器表達式非常強大,可以使您的代碼非常容易閱讀。 另一方面,包含復(fù)雜表達式的偵聽器使您的布局難以閱讀和維護。 這些表達式應(yīng)該像將UI中的可用數(shù)據(jù)傳遞給回調(diào)方法一樣簡單。 您應(yīng)該在從偵聽器表達式調(diào)用的回調(diào)方法中實現(xiàn)任何業(yè)務(wù)邏輯。

五、引用,變量和包含

數(shù)據(jù)綁定庫提供導入,變量和包含等功能。 導入使布局文件中的類很容易引用。 變量允許您描述可用于綁定表達式的屬性。 包括讓您在整個應(yīng)用中重復(fù)使用復(fù)雜的布局。

引用

導入允許您輕松引用布局文件中的類,就像在托管代碼中一樣。 可以在數(shù)據(jù)元素內(nèi)使用零個或多個導入元素。 以下代碼示例將View類導入布局文件:

<data>
    <import type="android.view.View"/>
</data>

導入View類允許您從綁定表達式中引用它。 以下示例顯示如何引用View類的VISIBLE和GONE常量:

<TextView
   android:text="@{user.lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>
輸入別名

當存在類名沖突時,可以將其中一個類重命名為別名。 以下示例將com.example.real.estate包中的View類重命名為Vista:

<import type="android.view.View"/>
<import type="com.example.real.estate.View"
        alias="Vista"/>

您可以使用Vista來引用com.example.real.estate.View,View可以用來引用布局文件中的android.view.View。

導入其他類

導入的類型可以用作變量和表達式中的類型引用。 以下示例顯示用作變量類型的User和List:

<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尚未處理導入,因此導入變量的自動完成功能可能無法在IDE中運行。 您的應(yīng)用程序仍在編譯,您可以通過在變量定義中使用完全限定名稱來解決IDE問題。

您還可以使用導入的類型來轉(zhuǎn)換表達式的一部分。 以下示例將連接屬性強制轉(zhuǎn)換為User類型:

<TextView
   android:text="@{((User)(user.connection)).lastName}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

在表達式中引用靜態(tài)字段和方法時,也可以使用導入的類型。 以下代碼導入MyStringUtils類并引用其capitalize方法:

<data>
    <import type="com.example.MyStringUtils"/>
    <variable name="user" type="com.example.User"/>
</data>
…
<TextView
   android:text="@{MyStringUtils.capitalize(user.lastName)}"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

就像托管代碼一樣,java.lang。*會自動導入。

變量

您可以在數(shù)據(jù)元素中使用多個變量元素。 每個變量元素描述可以在布局上設(shè)置的屬性,以在布局文件中的綁定表達式中使用。 以下示例聲明用戶,圖像和注釋變量:

<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>

在編譯時檢查變量類型,因此如果變量實現(xiàn)Observable或是可觀察的集合,那么應(yīng)該反映在類型中。 如果變量是未實現(xiàn)Observable接口的基類或接口,則不會觀察變量。

當存在用于各種配置的不同布局文件(例如,橫向或縱向)時,組合變量。 這些布局文件之間不得存在沖突的變量定義。

生成的綁定類對于每個描述的變量都有一個setter和getter。 變量采用默認的托管代碼值,直到為引用類型調(diào)用setter-null,0表示int,false表示布爾值等。

根據(jù)需要生成名為context的特殊變量以用于綁定表達式。 context的值是來自根View的getContext()方法的Context對象。 上下文變量由具有該名稱的顯式變量聲明覆蓋。

包括

通過使用app命名空間和屬性中的變量名,變量可以從包含的布局傳遞到包含的布局綁定中。 以下示例顯示了name.xml和contact.xml布局文件中包含的用戶變量:

<?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>
   <LinearLayout
       android: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>

數(shù)據(jù)綁定不支持include作為merge元素的直接子元素。 例如,不支持以下布局:

<?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><!-- Doesn't work -->
       <include layout="@layout/name"
           bind:user="@{user}"/>
       <include layout="@layout/contact"
           bind:user="@{user}"/>
   </merge>
</layout>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,569評論 19 139
  • 本來想把DataBindingLibrary理解完滴,但是由于初學這個,有的也不是很清楚,沒有理解的地方,也就是將...
    BrightLight閱讀 902評論 0 1
  • 導航: 搭建環(huán)境 數(shù)據(jù)綁定編譯器V2 數(shù)據(jù)綁定布局文件編寫你的第一套數(shù)據(jù)綁定表達式數(shù)據(jù)對象綁定數(shù)據(jù)事件處理方法參考...
    yyg閱讀 388評論 0 0
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 30,246評論 8 265
  • 淚與彼岸沒有距離 淚與花開不再相聚 花開之時悄然落淚 花落之時默默回歸
    惡魔林路比閱讀 258評論 0 1

友情鏈接更多精彩內(nèi)容