オブザーバビリティとは、データ内の変更を他のオブジェクトに通知するオブジェクトの機能です。データ バインディング ライブラリを使用すると、オブジェクト、フィールド、コレクションを監視できます。
データ バインディングには任意のオブジェクトを使用できますが、オブジェクトを変更しても UI は自動的に更新されません。データ バインディングを使用すると、データ オブジェクトが変更されたときに、リスナーと呼ばれる他のオブジェクトに通知機能を持たせることができます。監視可能なクラスには、フィールド、コレクション、オブジェクトの 3 種類があります。
これらの監視可能なデータ オブジェクトのいずれかが UI にバインドされ、データ オブジェクトのプロパティが変更されると、UI が自動的に更新されます。
監視可能なフィールド
クラスのプロパティ数が少ない場合は、Observable
インターフェースを実装するクラスを作成しても無駄になる可能性があります。この場合、汎用の Observable
クラスと次のプリミティブ固有のクラスを使用して、フィールドを監視できます。
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable
監視可能なフィールドは、単一のフィールドを持つ自己完結型の監視可能なオブジェクトです。プリミティブ バージョンでは、アクセス オペレーション中のボックス化とボックス化解除を回避します。このメカニズムを使用するには、次の例に示すように、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); } }
データ バインディングは、データ バインディングに使用するリソースの ID を含むモジュール パッケージに BR
という名前のクラスを生成します。Bindable
アノテーションは、コンパイル時に BR
クラスファイルにエントリを生成します。データクラスの基本クラスを変更できない場合は、PropertyChangeRegistry
オブジェクトを使用して Observable
インターフェースを実装し、リスナーを効率的に登録して通知できます。
ライフサイクル対応オブジェクト
アプリのレイアウトは、データの変更について UI に自動的に通知するデータ バインディング ソースにバインドすることもできます。これにより、バインディングがライフサイクルを認識し、UI が画面に表示されている場合にのみトリガーされます。
データ バインディングは、StateFlow
と LiveData
をサポートしています。データ バインディングで LiveData
を使用する方法について詳しくは、LiveData を使用してデータの変更を UI に通知するをご覧ください。
StateFlow を使用する
アプリでコルーチンで Kotlin を使用する場合は、StateFlow
オブジェクトをデータ バインディング ソースとして使用できます。バインディング クラスで StateFlow
オブジェクトを使用するには、ライフサイクル オーナーを指定して StateFlow
オブジェクトのスコープを定義します。次の例では、バインディング クラスをインスタンス化した後、アクティビティをライフサイクル オーナーとして指定しています。
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Inflate view and obtain an instance of the binding class.
val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)
// Specify the current activity as the lifecycle owner.
binding.lifecycleOwner = this
}
}
レイアウト ビューをアーキテクチャ コンポーネントにバインドするで説明されているように、データ バインディングは ViewModel
オブジェクトとシームレスに連携します。次のように、StateFlow
と ViewModel
を併用できます。
class ScheduleViewModel : ViewModel() {
private val _username = MutableStateFlow<String>("")
val username: StateFlow<String> = _username
init {
viewModelScope.launch {
_username.value = Repository.loadUserName()
}
}
}
次の例に示すように、レイアウトでは、バインディング式を使用して ViewModel
オブジェクトのプロパティとメソッドを対応するビューに割り当てます。
<TextView
android:id="@+id/name"
android:text="@{viewmodel.username}" />
UI は、ユーザーの名前の値が変更されると自動的に更新されます。
StateFlow のサポートを無効にする
Kotlin と AndroidX を使用するアプリの場合、データ バインディングに StateFlow
のサポートが自動的に含まれます。つまり、依存関係がまだ使用できない場合、コルーチンの依存関係が自動的にアプリに含まれることになります。
この機能を無効にするには、build.gradle
ファイルに次の行を追加します。
Groovy
android { ... dataBinding { addKtx = false } }
Kotlin
android { ... dataBinding { addKtx = false } }
また、次の行を gradle.properties
ファイルに追加して、プロジェクトで StateFlow
をグローバルに無効にすることもできます。
Groovy
android.defaults.databinding.addKtx = false
Kotlin
android.defaults.databinding.addKtx = false
参考情報
データ バインディングの詳細については、以下の参考情報をご覧ください。
サンプル
Codelab
ブログ投稿
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- ViewModel の保存済み状態のモジュール
- レイアウト ビューをアーキテクチャ コンポーネントにバインドする
- ページング ライブラリの概要