雙向資料繫結

使用單向資料繫結,您可以為屬性設定值,並設定回應該屬性變更的事件監聽器:

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@{viewmodel.rememberMe}"
    android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
/>

雙向資料繫結可提供此程序的捷徑:

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@={viewmodel.rememberMe}"
/>

@={} 標記法主要包含「=」符號,會接收屬性的資料變更,並同時監聽使用者更新。

如要回應備份資料的變更,您可以將版面配置變數設為 Observable (通常是 BaseObservable) 的實作,並使用 @Bindable 註解,如以下程式碼片段所示:

Kotlin

class LoginViewModel : BaseObservable {
    // val data = ...

    @Bindable
    fun getRememberMe(): Boolean {
        return data.rememberMe
    }

    fun setRememberMe(value: Boolean) {
        // Avoids infinite loops.
        if (data.rememberMe != value) {
            data.rememberMe = value

            // React to the change.
            saveData()

            // Notify observers of a new value.
            notifyPropertyChanged(BR.remember_me)
        }
    }
}

Java

public class LoginViewModel extends BaseObservable {
    // private Model data = ...

    @Bindable
    public Boolean getRememberMe() {
        return data.rememberMe;
    }

    public void setRememberMe(Boolean value) {
        // Avoids infinite loops.
        if (data.rememberMe != value) {
            data.rememberMe = value;

            // React to the change.
            saveData();

            // Notify observers of a new value.
            notifyPropertyChanged(BR.remember_me);
        }
    }
}

由於繫結屬性的 getter 方法稱為 getRememberMe(),因此屬性的對應 setter 方法會自動使用 setRememberMe() 名稱。

如要進一步瞭解如何使用 BaseObservable@Bindable,請參閱「使用可觀察的資料物件」。

使用自訂屬性雙向資料繫結

平台為最常見的雙向屬性提供雙向資料繫結實作,變更事件監聽器則可做為應用程式的一部分。如要搭配自訂屬性使用雙向資料繫結,您必須使用 @InverseBindingAdapter@InverseBindingMethod 註解。

例如,如果您想在名為 MyView 的自訂檢視區塊的 "time" 屬性中啟用雙向資料繫結,請完成下列步驟:

  1. 為使用 @BindingAdapter 變更初始值和更新值時為方法加上註解:

    Kotlin

    @BindingAdapter("time")
    @JvmStatic fun setTime(view: MyView, newValue: Time) {
        // Important to break potential infinite loops.
        if (view.time != newValue) {
            view.time = newValue
        }
    }

    Java

    @BindingAdapter("time")
    public static void setTime(MyView view, Time newValue) {
        // Important to break potential infinite loops.
        if (view.time != newValue) {
            view.time = newValue;
        }
    }
  2. 使用 @InverseBindingAdapter 為從檢視區塊讀取值的方法為方法加上註解:

    Kotlin

    @InverseBindingAdapter("time")
    @JvmStatic fun getTime(view: MyView) : Time {
        return view.getTime()
    }

    Java

    @InverseBindingAdapter("time")
    public static Time getTime(MyView view) {
        return view.getTime();
    }

此時,資料繫結會知道在資料變更時的處理方式 (呼叫帶有 @BindingAdapter 註解的方法),以及檢視畫面屬性變更時要呼叫的內容 (呼叫 InverseBindingListener)。不過,它不知道屬性何時或如何變更。

為此,您必須在檢視畫面中設定事件監聽器。它可以是與自訂檢視區塊相關聯的自訂事件監聽器,也可以是一般事件,例如失去焦點或文字變更。將 @BindingAdapter 註解新增至可設定屬性變更事件監聽器的方法:

Kotlin

@BindingAdapter("app:timeAttrChanged")
@JvmStatic fun setListeners(
        view: MyView,
        attrChange: InverseBindingListener
) {
    // Set a listener for click, focus, touch, etc.
}

Java

@BindingAdapter("app:timeAttrChanged")
public static void setListeners(
        MyView view, final InverseBindingListener attrChange) {
    // Set a listener for click, focus, touch, etc.
}

事件監聽器會包含 InverseBindingListener 做為參數。您可以使用 InverseBindingListener 向資料繫結系統告知屬性已變更。接著,系統就能開始呼叫使用 @InverseBindingAdapter 註解的方法,以此類推。

實務上,這個事件監聽器含有一些非簡單的邏輯,包括單向資料繫結的事件監聽器。如需範例,請參閱文字屬性變更的轉接程式 TextViewBindingAdapter

轉換人數

如果繫結至 View 物件的變數需要經過格式化、轉譯或變更,才能顯示,您可以使用 Converter 物件。

舉例來說,您可以採用顯示日期的 EditText 物件:

<EditText
    android:id="@+id/birth_date"
    android:text="@={Converter.dateToString(viewmodel.birthDate)}"
/>

viewmodel.birthDate 屬性包含 Long 類型的值,因此必須使用轉換器進行格式化。

由於使用的是雙向運算式,因此也需要反向轉換工具,讓程式庫知道如何將使用者提供的字串轉換回備份資料類型 (在本例中為 Long)。完成此程序,請將 @InverseMethod 註解新增至其中一個轉換器,然後讓此註解參照反向轉換器。以下程式碼片段顯示這項設定的範例:

Kotlin

object Converter {
    @InverseMethod("stringToDate")
    @JvmStatic fun dateToString(
        view: EditText, oldValue: Long,
        value: Long
    ): String {
        // Converts long to String.
    }

    @JvmStatic fun stringToDate(
        view: EditText, oldValue: String,
        value: String
    ): Long {
        // Converts String to long.
    }
}

Java

public class Converter {
    @InverseMethod("stringToDate")
    public static String dateToString(EditText view, long oldValue,
            long value) {
        // Converts long to String.
    }

    public static long stringToDate(EditText view, String oldValue,
            String value) {
        // Converts String to long.
    }
}

使用雙向資料繫結的無限迴圈

使用雙向資料繫結時,請務必小心不要導入無限迴圈。當使用者變更屬性時,系統會呼叫使用 @InverseBindingAdapter 註解的方法,並將值指派給幕後屬性。這樣會接著呼叫使用 @BindingAdapter 註解的方法,觸發對使用 @InverseBindingAdapter 註解的方法再次呼叫,以此類推。

因此,請務必比較使用 @BindingAdapter 註解的方法中新與舊值,以打破可能的無限迴圈。

雙向屬性

使用下表中的屬性時,平台提供雙向資料繫結的內建支援。如要進一步瞭解平台如何提供這項支援,請參閱對應繫結轉接器的實作:

類別 屬性 繫結配接器
AdapterView android:selectedItemPosition
android:selection
AdapterViewBindingAdapter
CalendarView android:date CalendarViewBindingAdapter
CompoundButton android:checked CompoundButtonBindingAdapter
DatePicker android:year
android:month
android:day
DatePickerBindingAdapter
NumberPicker android:value NumberPickerBindingAdapter
RadioButton android:checkedButton RadioGroupBindingAdapter
RatingBar android:rating RatingBarBindingAdapter
SeekBar android:progress SeekBarBindingAdapter
TabHost android:currentTab TabHostBindingAdapter
TextView android:text TextViewBindingAdapter
TimePicker android:hour
android:minute
TimePickerBindingAdapter

其他資源

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

範例

程式碼研究室

網誌文章