一方向データ バインディングを使用すると、属性の値を設定し、その属性の変更に反応するリスナーを設定できます。
<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); } } }
バインド可能なプロパティのゲッター メソッドは getRememberMe()
と呼ばれるため、プロパティの対応するセッター メソッドで自動的に setRememberMe()
という名前が使用されます。
BaseObservable
と @Bindable
の使用方法については、オブザーバブルなデータ オブジェクトを操作するをご覧ください。
カスタム属性を使用した双方向データ バインディング
プラットフォームには、アプリの一部として使用できる最も一般的な双方向属性と変更リスナーの双方向データ バインディングの実装が用意されています。カスタム属性を持つ双方向データ バインディングを使用する場合は、@InverseBindingAdapter
アノテーションと @InverseBindingMethod
アノテーションを操作する必要があります。
たとえば、MyView
というカスタムビューの "time"
属性で双方向データ バインディングを有効にする場合は、次の手順を完了します。
@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; } }
ビューから値を読み取るメソッドに、
@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
|
参考情報
データ バインディングの詳細については、次の参考情報をご覧ください。
サンプル
Codelab
ブログ投稿
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- 監視可能なデータ オブジェクトを操作する
- レイアウトとバインディング式
- レイアウト ビューをアーキテクチャ コンポーネントにバインドする