Android Kotlin基礎講座 05.2: LiveDataとLiveData observers

タスク:LiveDataをカプセル化する

カプセル化とはオブジェクトのフィールドに対する直接アクセスを制限する方法の一つです。オブジェクトをカプセル化する際には、privateな内部フィールドを修正するためのpublicなメソッドを用意します。カプセル化することで、他のクラスがこれらの内部フィールドをどのように操作するかを制御できます。

現在のコードでは、どの外部クラスからでもvalueプロパティを用いてscore変数とword変数の値を変更できます。この記事で開発しているアプリにおいてはさほど問題ではありませんが、本格的な製品としてのアプリでは、ViewModelオブジェクトのデータはしっかりと制御しなければなりません。

ViewModelのみがアプリのデータを編集するべきです。しかしUI controllerはそれらのデータを読み込む必要があるので、データフィールドは完全にprivateであってはいけません。アプリのデータをカプセル化するためには、MutableLiveDataとLiveDataオブジェクト両方を使います。

MutableLiveDataとLiveData:

  • MutableLiveDataオブジェクト内のデータはその名前が示唆するように変更可能です。ViewModel内のデータは変更可能であるべきなので、MutableLiveDataを使います。
  • LiveDataオブジェクト内のデータは読み込み可能ですが変更不可です。ViewModelの外部からはデータは読み込み可能かつ編集不可であるべきなので、LiveDataであるべきです。

これを実現するためにはKotlinバッキングプロパティというものを使います。バッキングプロパティを使うとオブジェクトそのものでなくゲッターから何かを返すことができます。このタスクではGuessTheWordアプリのscore変数とword変数用にバッキングプロパティを実装します。

scoreとwordにバッキングプロパティを追加する

  1. GameViewModel中のscoreオブジェクトをprivateにしてください。
  2. バッキングプロパティに用いられている命名慣例に従って、scoreを_scoreに変更してください。これで_scoreプロパティは内部でのみゲーム得点の変更が可能なバージョンになりました。
  3. scoreというLiveData型のpublicバージョンを作成してください。
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
  1. 初期化エラーが表示されます。このエラーはGameFragment内でscoreがLiveData参照であり、scoreがセッターにアクセスできないために起こります。Kotlinにおけるゲッターとセッターについてよく詳しく知りたい方はGetters and Settersをご覧ください。

    このエラーを解消するために、GameViewModel内のscoreオブジェクト用のget()メソッドをオーバーライドし、バッキングプロパティである_scoreを返すようにしてください。
val score: LiveData<Int>
   get() = _score
  1. GameViewModel中のscoreについての参照部分を内部可変バージョンの_scoreに変更してください。
init {
   ...
   _score.value = 0
   ...
}

...
fun onSkip() {
   _score.value = (score.value)?.minus(1)
  ...
}

fun onCorrect() {
   _score.value = (score.value)?.plus(1)
   ...
}
  1. wordオブジェクトも_wordに変更し、scoreオブジェクトの時と同様にバッキングプロパティを追加してください。
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
   get() = _word
...
init {
   _word.value = ""
   ...
}
...
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       _word.value = wordList.removeAt(0)
   }
}

これでLiveDataオブジェクトであるwordとscoreをカプセル化することができました。