PS:真の努力は行動の後に起こる。
前の記事では BindAdapter
について紹介しました。Android Jetpack コンポーネントシリーズの記事は以下の通りです:
- Android Jetpack コンポーネントの Lifecycle 編
- Android Jetpack コンポーネントの LiveData 編
- Android Jetpack コンポーネントの ViewModel 編
- Android Jetpack コンポーネントの DataBinding 編
- Android Jetpack コンポーネントの BindingAdapter 編 - jzman
本記事では、可観測なデータオブジェクトの使用方法について説明します。可観測性とは、あるオブジェクトの変化が他のデータに通知される能力を指し、主に 3 種類の可観測タイプがあります:
- フィールド
- オブジェクト
- コレクション
データバインディングを使用すると、データオブジェクトが変更されたときに他のデータに変更を通知する能力を提供できます。可観測データオブジェクトを UI にバインドすると、データオブジェクトの属性が変更されたときに UI が自動的に更新されます。
フィールド#
あるクラスにいくつかの属性しかない場合、これらのオブジェクトにデータの変化を観察する能力を持たせるために、可観測フィールド(Observable fields)を使用できます。データバインディングでは、このような汎用の Observable クラスが提供されており、8 種類の基本データ型と Parcelable 型が含まれています。具体的には以下の通りです:
- ObservableBoolean
- ObservableByte
- ObservableChar
- ObservableShort
- ObservableInt
- ObservableLong
- ObservableFloat
- ObservableDouble
- ObservableParcelable
では、実際に使用してみましょう。ここでは文字列と Int を使って可観測フィールドの使用法を説明します。まず、以下のようにエンティティクラスを作成します:
/**
* Powered by jzman.
* Created on 2018/12/3 0003.
*/
public class Person {
public final ObservableField<String> name = new ObservableField<>();
public final ObservableInt age = new ObservableInt();
}
次に、レイアウトファイルで使用する変数を宣言します。具体的には以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="person"
type="com.manu.databindsample.data.Person"/>
</data>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`name is `+person.name+`,age is `+person.age}"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btnClickObservableField"
android:text="可観測フィールド"/>
</LinearLayout>
</layout>
次に、対応する Activity でデータオブジェクトをバインドします。具体的には以下の通りです:
private Person person;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityObservableObjectSampleBinding binding =
DataBindingUtil.setContentView(this,R.layout.activity_observable_object_sample);
person = new Person();
binding.setPerson(person);
}
最後に、person の値を動的に変更します。Person オブジェクトの属性値が変化すると、その属性値が動的に更新され、UI も動的に更新されます。具体的には以下の通りです:
//動的に属性値を変更
public void btnClickObservableField(View view) {
person.name.set("android");
person.age.set(10);
}
以上が可観測フィールドの使用法です。その鍵はやはりオブザーバーデザインパターンです。文末でテスト効果の画像を見てみましょう。
オブジェクト#
データバインディングを使用する際、データバインディングはインターフェース android.databinding.Observable を提供します。このインターフェースを実装したクラスはリスナーを登録でき、このリスナーは特定のデータオブジェクトの変化を監視し、そのデータオブジェクトの属性の変化を通知します。この Observable インターフェースにはリスナーの追加と削除のメカニズムがありますが、データ更新通知を送信するタイミングは具体的な実装クラスによって決まります。開発を簡素化するために、提供された BaseObservable を使用できます。このクラスはリスナー登録メカニズムを実装しており、具体的に BaseObservable を継承したクラスが属性の変更タイミングを決定します。つまり、対応する getter メソッドに @Bindable アノテーションを使用し、対応する setter メソッドで notifyPropertyChanged メソッドを呼び出します。以下に可観測オブジェクトの具体的な使用方法を示します。まず、データエンティティクラスを以下のように作成します:
/**
* 可観測なデータオブジェクト
* Powered by jzman.
* Created on 2018/12/4 0004.
*/
public class Student extends BaseObservable{
private String name;
private int age;
@Bindable
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
notifyPropertyChanged(BR.name);
}
@Bindable
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
notifyPropertyChanged(BR.age);
}
}
次に、レイアウトファイルで使用する変数を宣言します。具体的には以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="student"
type="com.manu.databindsample.data.Student"/>
</data>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`name is `+student.name+`,age is `+student.age}"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btnClickObservableObject"
android:text="可観測オブジェクト"/>
</LinearLayout>
</layout>
次に、対応する Activity でデータオブジェクトをバインドします。具体的には以下の通りです:
private Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityObservableSampleBinding binding =
DataBindingUtil.setContentView(this,R.layout.activity_observable_sample);
//可観測オブジェクト
student = new Student();
binding.setStudent(student);
}
最後に、student の値を動的に変更します。Student オブジェクトの属性値が変化すると、その属性値が動的に更新され、UI も動的に更新されます。具体的には以下の通りです:
public void btnClickObservableObject(View view) {
student.setName("可観測オブジェクト");
student.setAge(20);
}
以上が可観測オブジェクトの使用法です。文末でテスト効果の画像を見てみましょう。
コレクション#
開発の過程では、コレクションデータが関与することがよくあります。データバインディングでは、観察能力を持つコレクションクラスも提供されています。ここでは最も一般的な Map と List コレクションを使用して ObservableMap と ObservableList の使用法を説明します。まず、レイアウトファイルで使用する変数を宣言します。具体的には以下の通りです:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<!--ArrayMap-->
<import type="android.databinding.ObservableArrayMap"/>
<variable
name="arrayMap"
type="ObservableArrayMap<String,String>"/>
<!--ArrayList-->
<import type="android.databinding.ObservableList"/>
<variable
name="arrayList"
type="ObservableList<String>"/>
</data>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`name is `+arrayMap.name+`,age is `+arrayMap.age}"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btnClickObservableMap"
android:text="可観測コレクションのArrayMap"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{`name is `+arrayList[0]+`,age is `+arrayList[1]}"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btnClickObservableList"
android:text="可観測コレクションのArrayList"/>
</LinearLayout>
</layout>
次に、対応する Activity でデータオブジェクトをバインドします。具体的には以下の通りです:
private ObservableArrayMap<String,String> arrayMap;
private ObservableArrayList<String> arrayList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityObservableSampleBinding binding =
DataBindingUtil.setContentView(this,R.layout.activity_observable_sample);
//可観測コレクションのArrayMap
arrayMap = new ObservableArrayMap<>();
binding.setArrayMap(arrayMap);
//可観測コレクションのArrayList
arrayList = new ObservableArrayList<>();
binding.setArrayList(arrayList);
}
最後に、コレクションデータをバインドした後、コレクションデータを変更して UI の変化を観察できます。具体的には以下の通りです:
//可観測コレクションのArrayMap
public void btnClickObservableMap(View view) {
arrayMap.put("name","可観測コレクションのArrayMap");
arrayMap.put("age","30");
}
//可観測コレクションのArrayList
public void btnClickObservableList(View view) {
arrayList.add("可観測コレクションのArrayList");
arrayList.add("40");
}
以上が可観測コレクションの使用法です。
テスト効果#
上記の小さなケースを通じて、データバインディングが提供する観察能力を持つフィールド、オブジェクト、コレクションを非常に簡単に使用できることがわかると思います。以下は、上記コードのテスト効果の画像です: