日々是好日

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

MVVM 完全に理解した - 4

前回、次は双方向バインディングと言いましたが、その前に Observable object というものをやらないとつながらないのでそちらを少しだけ。

今回はこちらのページから。

Work with observable data objects  |  Android Developers

Observable fields

前回は不変なデータを持つ data class をバインドしていましたが、普通は入力や処理に応じて表示を変化させることを想像すると思います。
そこで出てくるのが、Data Binding Library の ObservableField<T> や他の Observablexxx クラスです。

class User {
    var firstName = ObservableField<String>()
    var lastName = ObservableField<String>()
    var age = ObservableInt()
    ...
}

ObservableField や類似のクラスは、BaseObservable を継承しており、また Observable インターフェースを実装しています。
上記の User クラスは、すべてのフィールド変数が Observable になっています。
まー実際どう使うのか見ちゃったほうが早いですかね。

ObservableField で View の更新

まずは xmlUser をバインドする。

<data>
    <variable name="user" type="User"/>
</data><TextView
    android:text="@{user.firstName}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<TextView
    android:text="@{user.lastName}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
//バインドするクラス
class User {
    private var firstName = ObservableField<String>()
    private var lastName = ObservableField<String>()
    private var age = ObservableInt()

    //set が呼ばれると、自動で紐付けられた View が更新される
    public changeName() {
        firstName.set("hoge")
        lastName.set("fuga")
    }
}

set メソッド呼ぶだけで View も更新されるのだから便利なものです。
一方で、この View の更新にはもうひとつ方法があり、User : BaseObservable として継承する方法です。

BaseObservable を継承して View と連携

xml は上記と同じ。実装を見てみます。

//BaseObservable を継承する
class User : BaseObservable() {
    //普通のフィールド変数
    private var age = 17

    //Bindable アノテーション!
    @get:Bindable
    var firstName : String = "foo"
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }

    @get:Bindable
    var lastName : String = "bar"
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

setter について、BaseObservable#notifyPropertyChanged をコールすることで、View の更新を行います。
また、急に出てきた BR というやつ。notifyPropertyChanged)には次のように説明されています。

The getter for the property that changes should be marked with Bindable to generate a field in BR to be used as fieldId.

getter に Bindable でマークすると、データバインディングに使用するための BR っつーリソースを生成するよ(意訳)、と。
んで、BR の ID はフィールド変数(と同じ名称?)を使用しているようです。 この ID を使用することで、なんのプロパティが更新されたか通知するようにしているとのこと。

所感

ただでさえ相互にやりとりするから混乱するのに、さらに2通りの方法とか作らないで欲しい件←
Data Binding は自動生成されるものが多くてよく分からなくなりますねー。公式リファレンスはよく読みませい₍₍(ง˘ω˘)ว⁾⁾

今回やっとプロパティに set して更新する方法が出てきたので、次は双方向バインディング行けそうです。