版面配置與繫結的運算式

運算式語言可讓您編寫運算式來處理分派的事件 依觀看次數資料繫結程式庫會自動產生必要的類別 將版面配置中的檢視畫面與資料物件繫結。

資料繫結版面配置檔案稍有不同,且開頭為 layout,後面是 data 元素和 view 根元素。這個檢視畫面 元素是非繫結版面配置檔案中的根。以下程式碼 顯示版面配置檔案範例:

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

data 中的 user 變數描述了可在 這個版面配置:

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

版面配置內的運算式會使用 @{} 語法。在以下範例中, TextView 文���已設為 user 變數的 firstName 屬性:

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

資料物件

假設您有一個用來描述 User 實體的純物件:

Kotlin

data class User(val firstName: String, val lastName: String)

Java

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

這類物件含有從未變更的資料。在應用程式中 只讀取一次,之後就不會變更您也可以使用 而是遵循一組慣例的物件,例如在其中使用存取子方法 Java 程式設計語言,如以下範例所示:

Kotlin

// Not applicable in Kotlin.
data class User(val firstName: String, val lastName: String)

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

從資料繫結的角度來看,這兩個類別相等。 運算式 @{user.firstName} android:text 屬性會存取舊類別的 firstName 欄位, getFirstName() 方法。這個函數也解析為 firstName() (如果已有此方法)。

繫結資料

系統會為每個版面配置檔案產生繫結類別。根據預設 類別是以版面配置檔案的名稱為基礎,並轉換為 Pascal 命名法, Binding 後置字串。舉例來說,上述版面配置檔案名稱是 activity_main.xml,因此對應的產生的繫結類別為 ActivityMainBinding

此類別會保留版面配置屬性的所有繫結,例如, user 變數—傳送至版面配置的檢視畫面,並知道如何指派值 建立繫結運算式建議您在加載時建立繫結 版面配置,如以下範例所示:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main)

    binding.user = User("Test", "User")
}

Java

@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);
}

在執行階段,應用程式會在 UI 中顯示 Test 使用者。此外,也可以 並使用 LayoutInflater,如 範例:

Kotlin

val binding: ActivityMainBinding = ActivityMainBinding.inflate(getLayoutInflater())

Java

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

如果您在 FragmentListView,或 RecyclerView 轉換器,您可能會想使用 inflate() 繫結的方法或 DataBindingUtil 類別, 如以下程式碼範例所示:

Kotlin

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

Java

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

運算式語言

常見功能

運算式語言與代管程式碼中的運算式十分相似。個人中心 以下運算子與關鍵字可用於運算式語言:

  • 數學:+ - / * %
  • 字串串連:+
  • 邏輯:&& ||
  • 二進位檔:& | ^
  • 一元:+ - ! ~
  • 調節:>> >>> <<
  • 比較:== > < >= <= (< 必須逸出為 &lt;)
  • instanceof
  • 分組依據:()
  • 常值,例如字元、字串、數字、null
  • 投放
  • 方法呼叫
  • 欄位存取權
  • 存取陣列:[]
  • 三元運算子:?:

例如:

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

缺少作業

您可使用的運算式語法缺少下列作業 :

  • this
  • super
  • new
  • 明確一般叫用

空值共合運算子

空值共乘運算子 (??) 會選擇不是 null 的左運算元 如果前者是 null,則為右側:

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

這在功能上等同於下列內容:

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

資源參照

運算式可採用下列格式參照類別中的屬性: 這對於欄位、getter 和 ObservableField敬上 物件:

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

避免空值指標例外狀況

產生的資料繫結程式碼會自動檢查 null 值,並避免 空值指標例外狀況。例如,在 @{user.name} 運算式中, user 為 null,user.name 已被指派的預設值 null。如果發生以下情況: 參照 user.age,其中年齡層為 int 類型,然後資料繫結會使用 預設值為 0

查看參考資料

運算式可使用下列指令,根據 ID 參照版面配置中的其他檢視區塊 語法:

android:text="@{exampleText.text}"

在以下範例中,TextView 檢視表參照了 EditText 檢視區塊,位於 相同的版面配置:

<EditText
    android:id="@+id/example_text"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"/>
<TextView
    android:id="@+id/example_output"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{exampleText.text}"/>

焦點頻道

存取常用集合,例如陣列、清單、稀疏清單 為方便起見,請使用 [] 運算子。

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String>"/>
    <variable name="sparse" type="SparseArray&lt;String>"/>
    <variable name="map" type="Map&lt;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"]}'

你也可以使用雙引號括住屬性值。這樣做時 字串常值必須以倒引號 ` 括住,如上所示 這裡:

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

資源

運算式可以使用下列語法參照應用程式資源:

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

您可以提供參數來評估格式字串和複數:

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

您可以傳遞資源參照資料檢視 參照做為資源參數:

android:text="@{@string/example_resource(user.lastName, exampleText.text)}"

複數使用多個參數時,請傳遞所有的參數:


  Have an orange
  Have %d oranges

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

某些資源需要明確類型評估,如下所示 資料表:

類型 一般參考資料 運算式參照
String[] @array @stringArray
int[] @array @intArray
TypedArray @array @typedArray
Animator @animator @animator
StateListAnimator @animator @stateListAnimator
color int @color @color
ColorStateList @color @colorStateList

事件處理

資料繫結可讓您編寫運算式處理, 資料檢視 (例如 onClick()敬上 方法。事件屬性名稱取決於事件監聽器方法的名稱。 只有少數例外例如: View.OnClickListener有 方法為 onClick(),因此此事件的屬性為 android:onClick

點擊事件有一些特殊的事件處理常式, 屬性,以免衝突。android:onClick您可以使用 下列屬性,避免發生這類衝突:

類別 事件監聽器設定者 屬性
SearchView setOnSearchClickListener(View.OnClickListener) android:onSearchClick
ZoomControls setOnZoomInClickListener(View.OnClickListener) android:onZoomIn
ZoomControls setOnZoomOutClickListener(View.OnClickListener) android:onZoomOut

您可以使用這兩種機制,詳情請參閱 之後來處理事件:

  • 方法參照:您可以在運算式中使用 符合事件監聽器方法簽名的參照方法。時間 運算式會評估為方法參照,資料繫結會包裝方法 參考和擁有者物件,並設定 目標檢視。如果運算式評估為 null,則資料繫結不會 請建立事件監聽器,並改為設定 null 事件監聽器。
  • 事件監聽器繫結:這些是 lambda 運算式, 才會受到評估。資料繫結一律會建立 並會在檢視區塊上設定該接聽程式。傳送事件時, 事件監聽器會評估 lambda 運算式。

方法參考資料

您可以直接將事件繫結至處理常式方法,方法與 指派 將 android:onClick 轉換為 方法。相較於 View onClick 屬性是 運算式都會在編譯期間處理因此,如果這個方法不存在 ,您會收到編譯時間錯誤。

方法參照和事件監聽器繫結之間的主要差異在於 系統會在資料繫結時建立實際的事件監聽器實作項目,而非 事件。發生事件時 請使用事件監聽器繫結

如要將事件指派給處理常式,請使用一般繫結運算式,並加上 值就是要呼叫的方法名稱。例如,請看以下範例 版面配置資料物件:

Kotlin

class MyHandlers {
    fun onClickFriend(view: View) { ... }
}

Java

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>

事件監聽器繫結

事件監聽器繫結是一種繫結運算式,會在事件發生時執行。他們 類似於方法的參照,但可讓您執行任意的資料繫結 運算式。這項功能適用於 Gradle 適用的 Android Gradle 外掛程式 2.0 以上版本。

在方法參照中,方法的參數必須與 事件接聽程式。在事件監聽器繫結中,只有回傳值必須與 事件監聽器的預期傳回值 (除非預期值是 void)。適用對象 例如,假設下列簡報者類別具有 onSaveClick() 方法:

Kotlin

class Presenter {
    fun onSaveClick(task: Task){}
}

Java

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

您可以將點擊事件繫結至 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>

在運算式中使用回呼時,資料繫結會自動建立 ,並註冊該事件給 事件。當檢視區塊觸發 事件,資料繫結會評估給定運算式。如同一般繫結 運算式後,您會取得資料繫結的空值和執行緒安全性,而 系統會評估事件監聽器運算式。

在上述範例中,傳遞至 onClick(View)view 參數 沒有定義事件監聽器繫結提供兩個事件監聽器參數的選擇: 您可以忽略方法中的所有參數,也可以為所有參數命名。偏好 為參數命名,即可在運算式中使用。舉例來說, 可以按照以下方式撰寫前面的運算式:

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

如果您想在運算式中使用參數,請按照下列步驟進行:

Kotlin

class Presenter {
    fun onSaveClick(view: View, task: Task){}
}

Java

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

此外,您可以使用含有多個參數的 lambda 運算式:

Kotlin

class Presenter {
    fun onCompletedChanged(task: Task, completed: Boolean){}
}

Java

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 的值,那麼 運算式也必須傳回相同類型的值。舉例來說 瞭解輕觸與保留 (長按) 事件,運算式就必須傳回 布林值。

Kotlin

class Presenter {
    fun onLongClick(view: View, task: Task): Boolean { }
}

Java

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

如果因 null 物件而無法評估運算式,資料繫結會傳回 該類型的預設值,例如 null 代表參照類型,0 代表 int,或為 booleanfalse

如果您需要使用帶有述詞的運算式,例如: 三元組,您可以使用 void 做為符號:

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

避免使用複雜的事件監聽器

事件監聽器運算式的功能很強大,可讓您的程式碼更容易閱讀。每月中的特定幾天 另一方面,包含複雜運算式的事件監聽器會使版面配置更加困難 讀取及維護就像傳送可用的資料一樣,運算式保持簡單 直接從使用者介面傳送至回呼方法。在該頁面內實作任何商業邏輯 您從事件監聽器運算式叫用的回呼方法。

匯入、變數,以及包含

資料繫結程式庫提供匯入、變數和 包括匯入功能可讓您在版面配置檔案中輕鬆參照類別。 變數可讓您描述可在繫結運算式中使用的屬性。 可讓您在應用程式內重複使用複雜的版面配置。

匯入

匯入功能可讓您在版面配置檔案中參照類別,就像在代管程式碼中一樣。 您可以在 data 元素中使用零個或多個 import 元素。 以下程式碼範例會將 View 類別匯入版面配置檔案:

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

匯入 View 類別後,您就能從繫結運算式中參照該類別。 以下範例說明如何參照 VISIBLEView 類別的 GONE 常數:

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

類型別名

如果課程名稱發生衝突,您可以將其中一個類別重新命名為 別名。以下範例將物件中的 View類別重新命名 將 com.example.real.estate 套件到 Vista

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

接著,您可以使用 Vista 參照 com.example.real.estate.ViewView 在版面配置檔案中參照 android.view.View

匯入其他課程

您可以在變數和運算式中使用匯入的類型做為類型參照。 以下範例顯示用來做為變數類型的 UserList

<data>
    <import type="com.example.User"/>
    <import type="java.util.List"/>
    <variable name="user" type="User"/>
    <variable name="userList" type="List&lt;User>"/>
</data>

您可以使用匯入的類型來轉換運算式的部分流程。下列 範例會將 connection 屬性轉換為 User 類型:

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

您也可以在參照靜態欄位和方法時,使用匯入的類型 運算式。下列程式碼會匯入 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.*

變數

您可以在 data 元素中使用多個 variable 元素。每項 variable 元素說明可在版面配置上設定的屬性 版面配置。以下範例宣告 userimagenote 變數:

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

系統會在編譯期間檢查變數類型,因此,如果變數類型 Observable可觀測的集合 必須反映在類型中如果變數是基礎類別或介面 未實作 Observable 介面,那麼這些變數「不會」

當不同設定具有不同的版面配置檔案時 (例如, 橫向或直向),這些變數會合併計算。不得使用 造成這些版面配置檔案之間的變數定義發生衝突。

產生的繫結類別會針對每個上述內容,使用 setter 和 getter 變數。變數會採用預設的代管程式碼值,直到 setter 為止 稱為- null 代表參照類型,0 代表 intfalse 代表 boolean

系統會產生名為 context 的特殊變數,用於繫結運算式 將物件的使用中版本還原為舊版 或依需要永久刪除封存版本context 的值是 根層級檢視畫面的 Context 物件 getContext() 方法。 context 變數已由明確變數宣告覆寫 名稱。

包含

您可以將變數從含有容器的 使用應用程式命名空間和屬性中的變數名稱來設定版面配置。 以下範例顯示了 name.xmluser 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>

資料繫結不支援做為合併元素的直接子項使用。 舉例來說,系統不支援下列版面配置:

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

其他資源

如要進一步瞭解資料繫結,請參閱以下其他資源。

範例

程式碼研究室

網誌文章