一方向データ バインディングを使用すると、属性の値を設定し、その属性の変更に反応するリスナーを設定できます。
<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
)をご覧ください。
Converter
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
|
参考情報
データ バインディングの詳細については、以下の参考情報をご確認ください。