UI更新処理はオブザーバを利用
前節でViewModelにLiveDataオブジェクトが用意できました。次に、このLiveDataオブジェクトに、UIの更新処理を登録していきます。
オブザーバの登録はobserve()メソッド
図3にもあるように、LiveDataがデータの変更を検知してUIの変更処理を自動実行するためには、そのLiveDataにUIの変更処理が登録されている必要があります。このUIの変更処理のことをオブザーバといいます。そして、オブザーバの登録は、アクティビティやフラグメントの役割となります。これは、Javaではリスト3のようなコードとなります。
public class MainActivity extends AppCompatActivity {
:
@Override
protected void onCreate(Bundle savedInstanceState) {
:
LiveData<Cocktailmemo> cocktailmemoLiveData = _mainViewModel.getCocktailmemoLiveData(); // (1)
cocktailmemoLiveData.observe(MainActivity.this, new CocktailmemoObserver()); // (2)
:
}
:
}
リスト3にあるように、オブザーバの登録処理は、原則onCreate()メソッド内で行います。その際、まず、(1)のようにViewModelから内部に保持しているLiveDataオブジェクトを取得します。そして、(2)のように、そのLiveDataオブジェクトのobserve()メソッドを実行してオブザーバの登録を行います。
このobserve()メソッドの引数は2個であり、第1引数は、LifecycleOwnerを渡します。LifecycleOwnerは、Androidのライフサイクルを担うオブジェクトを表します。もしこのオブザーバ登録がアクティビティの場合は、(2)のように、自分自身のオブジェクトを渡せば問題ありません。一方、フラグメントの場合は、getViewLifecycleOwner()メソッドの戻り値を渡します。
第2引数は、まさにオブザーバオブジェクトであり、このクラスは自作します。作り方は次項で紹介します。(2)ではそのように自作したCocktailmemoObserverクラスをnewして渡しています。
Kotlinコードも同様であり、リスト4のようになります。
class MainActivity : AppCompatActivity() {
:
override fun onCreate(savedInstanceState: Bundle?) {
:
_mainViewModel.cocktailmemoLiveData.observe(this@MainActivity, CocktailmemoObserver())
:
}
:
}
リスト3のJavaコードをそのままKotlinコードに置き換えた内容になっています。なお、このコードの記述がフラグメントの場合は、observe()メソッドの第1引数は、JavaのようにgetViewLifecycleOwner()メソッドの戻り値ではなく、viewLifecycleOwnerプロパティとなります。
オブザーバクラスの作り方
では、そのobserver()メソッドの第2引数として渡すオブザーバクラスの作り方を見ていきましょう。リスト3で渡しているJava版CocktailmemoObserverクラスは、MainActivityのメンバクラスとして作成し、リスト5のコードとなります。
public class MainActivity extends AppCompatActivity {
:
private class CocktailmemoObserver implements Observer<Cocktailmemo> { // (1)
// データ変更に応じてUIを変更する処理メソッド。
@Override
public void onChanged(@Nonnull Cocktailmemo cocktailmemo) { // (2)
// カクテル名を表示欄に反映。
TextView tvCocktailName = findViewById(R.id.tvCocktailName); // (3)
tvCocktailName.setText(cocktailmemo.name); // (3)
// 感想を入力欄に反映。
EditText etNote = findViewById(R.id.etNote); // (4)
etNote.setText(cocktailmemo.note); // (4)
// ボタンと入力欄の有効・無効を切り替え。
Button btnSave = findViewById(R.id.btnSave); // (5)
if(cocktailmemo.id == -1) { // (6)
etNote.setEnabled(false);
btnSave.setEnabled(false);
}
else {
etNote.setEnabled(true);
btnSave.setEnabled(true);
}
}
}
}
オブザーバクラスの作り方は以下の通りです。
1. Observerインターフェースを実装する。
リスト5の(1)が該当します。その際、ジェネリクスとして、このクラスをオブザーバとして登録するLiveDataの内部で保持しているデータと同じ型を指定します。前節で紹介したように、cocktailmemoLiveDataの内部に保持しているデータは、Cocktailmemoエンティティです。これに合わせて、(1)でもCocktailmemoをジェネリクスとして型指定しています。
2. onChanged()メソッドをオーバーライドし、その中にUIの変更処理を記述する。
リスト5の(2)が該当します。このonChanged()メソッドの引数は、(1)のObserverインターフェースのジェネリクスとして型指定したデータ型となります。また、nullになることはないため、@Nonnullアノテーションを付与しています。そして、LiveData内に保持しているデータが変更された場合、新しいデータが格納されたものがこの引数として渡されます。そのため、メソッド内には、この引数のデータを利用して、UIの変更処理を記述します。
リスト5に記述したUIの変更処理は、(3)がカクテル名を表示しているTextViewの表示文字列の変更処理、(4)が感想の入力欄であるEditText内の文字列の変更処理、(5)
と(6)以降が感想入力欄と[保存]ボタンの利用可、不可の切り替え処理となっています。
(6)以降について少し補足しておくと、引数として渡ってくるCocktailmemoオブジェクトは、リストからカクテルが選択されていない場合は、そのidが-1となるように次節でコーディングします。それを受けて、idが-1の場合は、データが入力できず、保存ができない状態と判断して、入力欄とボタンを利用不可にしています。一方、-1ではない場合に、利用できるようにしています。
このように、データに応じたUIの変更処理をオブザーバにまとめることにより、アクティビティやフラグメント内に散在していたUIの変更処理を、まとめることができます。
このオブザーバクラスの作り方はKotlinでも同じであり、リスト6のコードとなります。リスト5を、単にKotlinコードに置き換えたコードとなっています。リスト5に対応する形で番号を記載していますので、参考にしてください。
class MainActivity : AppCompatActivity() {
:
private inner class CocktailmemoObserver : Observer<Cocktailmemo> { // (1)
// データ変更に応じてUIを変更する処理メソッド。
override fun onChanged(cocktailmemo: Cocktailmemo) { // (2)
// カクテル名を表示欄に反映。
val tvCocktailName = findViewById<TextView>(R.id.tvCocktailName) // (3)
tvCocktailName.text = cocktailmemo.name // (3)
// 感想を入力欄に反映。
val etNote = findViewById<EditText>(R.id.etNote) // (4)
etNote.setText(cocktailmemo.note) // (4)
// ボタンと入力欄の有効・無効を切り替え。
val btnSave = findViewById<Button>(R.id.btnSave) // (5)
if(cocktailmemo.id == -1) { // (6)
etNote.isEnabled = false
btnSave.isEnabled = false
}
else {
etNote.isEnabled = true
btnSave.isEnabled = true
}
}
}
}
