日々是好日

プログラミングについてのあれこれ、ムダ知識など

MVVM 完全に理解した - 5

お待ちかね Two-way data binding(双方向バインディング) です。

Two-way data binding  |  Android Developers

ホントは一つ前の Bind layout views to Architecture Components についても書いてみたいところですが、そろそろ双方向バインディングも書かないとなので。

双方向バインディング

まず単方向バインディングの場合。

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

checked という値と onCheckedChanged という値変更のイベントを別々に記述しなければなりませんでした。

一方、双方向バインディングの場合、

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

@={} という表記法( notation )により、プロパティのデータ変更の受信と View の更新処理を1行で記述することができます。
動作的には、値の更新と同時に、View の更新をおこなってくれます。 (語彙力がない。)

実装

In order to react to changes in the backing data, you can make your layout variable an implementation of Observable, usually BaseObservable, and use a @Bindable annotation, as shown in the following code snippet:

実装するには、

  1. Data binding layout file を作成
  2. ObservableBaseObservable と、@Bindable アノテーションを使いこなす

とのこと。Observable インターフェースは無いですが、BaseObservable は前回の MVVM 完全に理解した - 4 - 日々是好日 にて議論しているので、実装例を見てみます。
ちなみに Observable インターフェースは、Observer パターンの Subject クラスとほぼ同じ機能を持ちます。

Observable  |  Android Developers

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

ログイン情報が端末に残っているかどうかのクラスみたいですね。
データを保存して、notifyPropertyChanged メソッドにより値の更新を通知しています。
個人的には「通知」というよりも「発報」という感覚ですが。

双方向を行う上での注意点

PEAKS(ピークス)|Android アプリ設計パターン入門より、双方向バインディングを使う上での注意点を引用します。

ViewModel は Fragment(≒ View)がどのような状態かは関知しません。この単方向の原則は MVVM パターンでは大変重要です。お互いの状態を気にすると相互依存してコードが複雑になるためです(特に View の状態が複雑になります。)。

「双方向」といっているので View と ViewModel が相互に参照し合う形を想像するかもしれません。これはギルティ。

  1. View から ViewModel を参照、メソッドコール → ○
  2. ViewModel から View を参照、値更新 → ギルティ
  3. ViewModel#setxxx メソッドで notifyPropertyChanged をコールしてバインドした View に通知 → ○

蛇足

Bind layout views to Architecture Components  |  Android Developersより引用。

In Android Studio version 3.1 and higher, you can replace observable fields with LiveData objects in your data binding code.

とゆーわけで、ObservableFieldLiveData にリプレースできまぁす!!なぜ 3.1 以上かの理由はご存じないです(諦)!!←

所感

Data Binding は学習コスト高いという指摘を見かけますが、たしかにしんどい。
とりあえずこれで準備は整ったので、アプリの概要をこれから考えます。←