監視可能なデータ オブジェクトの使用

監視可能とは、オブジェクトがそのデータの変更について他のオブジェクトに通知できることを意味します。データ バインディング ライブラリを使用すると、オブジェクト、フィールド、コレクションを監視できるようになります。

従来のオブジェクトはデータ バインディングに使用できますが、オブジェクトを変更しても UI は自動的に更新されません。データ バインディングを使用すると、データ オブジェクトのデータが変更されたときに他のオブジェクト(リスナー)に通知することができます。監視可能なクラスには、オブジェクトフィールドコレクションの 3 種類があります。

これらの監視可能なデータ オブジェクトのいずれかが UI にバインドされている場合、データ オブジェクトのプロパティが変更されると、UI が自動的に更新されます。

監視可能なフィールド

Observable インターフェースを実装するクラスの作成には一定の労力がかかります。ただし、クラスにプロパティが数個しかない場合は、その労力に見合わないかもしれません。そのような場合、汎用の Observable クラスや以下のプリミティブ固有のクラスを使用して、フィールドの監視を行えるようにできます。

監視可能なフィールドとは、1 つのフィールドを持つ自己完結型の監視可能なオブジェクトです。プリミティブ バージョンを使用すると、アクセス操作中にボックス化とボックス化解除が行われないようにすることができます。このメカニズムを使用するには、次の例に示すように、Java プログラミング言語で public final プロパティを作成するか、Kotlin で読み取り専用のプロパティを作成します。

Kotlin

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

    

Java

    private static class User {
        public final ObservableField<String> firstName = new ObservableField<>();
        public final ObservableField<String> lastName = new ObservableField<>();
        public final ObservableInt age = new ObservableInt();
    }

    

フィールドの値にアクセスするには、アクセサ メソッド set()get() を使用するか、Kotlin プロパティ構文を使用します。

Kotlin

    user.firstName = "Google"
    val age = user.age

    

Java

    user.firstName.set("Google");
    int age = user.age.get();

    

監視可能なコレクション

アプリによっては、データを保持するために動的な構造を使用することがあります。監視可能なコレクションを使用すると、こうした構造にキーを使ってアクセスできます。次の例に示すように、キーが参照型(String など)の場合は、ObservableArrayMap クラスが便利です。

Kotlin

    ObservableArrayMap<String, Any>().apply {
        put("firstName", "Google")
        put("lastName", "Inc.")
        put("age", 17)
    }

    

Java

    ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
    user.put("firstName", "Google");
    user.put("lastName", "Inc.");
    user.put("age", 17);

    

レイアウトでは、次のように文字列キーを使用してマップを検出できます。

<data>
        <import type="android.databinding.ObservableMap"/>
        <variable name="user" type="ObservableMap<String, Object>"/>
    </data>
    …
    <TextView
        android:text="@{user.lastName}"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:text="@{String.valueOf(1 + (Integer)user.age)}"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    

次のように、キーが整数の場合は、ObservableArrayList クラスが便利です。

Kotlin

    ObservableArrayList<Any>().apply {
        add("Google")
        add("Inc.")
        add(17)
    }

    

Java

    ObservableArrayList<Object> user = new ObservableArrayList<>();
    user.add("Google");
    user.add("Inc.");
    user.add(17);

    

レイアウトでは、次の例に示すように、インデックスを介してリストにアクセスできます。

<data>
        <import type="android.databinding.ObservableList"/>
        <import type="com.example.my.app.Fields"/>
        <variable name="user" type="ObservableList<Object>"/>
    </data>
    …
    <TextView
        android:text='@{user[Fields.LAST_NAME]}'
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    

監視可能なオブジェクト

Observable インターフェースを実装するクラスを使用すると、監視可能なオブジェクトでプロパティの変更に関する通知を受け取るリスナーを登録できます。

Observable インターフェースにはリスナーの追加と削除を行うメカニズムがありますが、通知の送信タイミングはデベロッパーが決める必要があります。データ バインディング ライブラリにはリスナーの登録メカニズムを実装する BaseObservable クラスが用意されており、このクラスを利用することで開発を簡素化できます。BaseObservable を実装するデータクラスは、プロパティの変更時に通知を送信します。そのためには、次の例に示すように、Bindable アノテーションをゲッターに付与し、セッターで notifyPropertyChanged() メソッドを呼び出します。

Kotlin

    class User : BaseObservable() {

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

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

Java

    private static class User extends BaseObservable {
        private String firstName;
        private String lastName;

        @Bindable
        public String getFirstName() {
            return this.firstName;
        }

        @Bindable
        public String getLastName() {
            return this.lastName;
        }

        public void setFirstName(String firstName) {
            this.firstName = firstName;
            notifyPropertyChanged(BR.firstName);
        }

        public void setLastName(String lastName) {
            this.lastName = lastName;
            notifyPropertyChanged(BR.lastName);
        }
    }

    

データ バインディングでは、BR という名前のクラスがモジュール パッケージ内に生成されます。このクラスに、データ バインディングで使用するリソースの ID が格納されます。Bindable アノテーションを付与すると、コンパイル時に BR クラスのファイル内にエントリが生成されます。データクラスの基本クラスを変更できない場合でも、PropertyChangeRegistry オブジェクトを使用して Observable インターフェースを実装することで、リスナーの登録と通知を効率的に行うことができます。

参考情報

データ バインディングの詳細については、以下の参考情報をご確認ください。

サンプル

Codelab

ブログ投稿