Android Kotlin の基礎 05.2: LiveData と LiveData のオブザーバー

この Codelab は、Android Kotlin の基礎コースの一部です。このコースを最大限に活用するには、Codelab を順番に進めることをおすすめします。コースのすべての Codelab は、Android Kotlin の基礎の Codelab のランディング ページに一覧表示されています。

はじめに

前の Codelab では、GuessTheWord アプリで ViewModel を使用して、デバイスの構成変更後もアプリのデータを維持できるようにしました。この Codelab では、LiveDataViewModel クラスのデータと統合する方法を学びます。LiveDataAndroid アーキテクチャ コンポーネントの 1 つで、基盤となるデータベースが変更されたときにビューに通知するデータ オブジェクトを構築できます。

LiveData クラスを使用するには、アプリのデータの変更を監視する「オブザーバー」(アクティビティやフラグメントなど)を設定します。LiveData はライフサイクル対応であるため、アクティブなライフサイクルの状態にあるアプリ コンポーネント オブザーバーのみを更新します。

前提となる知識

  • Kotlin で基本的な Android アプリを作成する方法。
  • アプリのデスティネーション間を移動する方法。
  • アクティビティとフラグメントのライフサイクル。
  • アプリで ViewModel オブジェクトを使用する方法。
  • ViewModelProvider.Factory インターフェースを使用して ViewModel オブジェクトを作成する方法。

学習内容

  • LiveData オブジェクトが有用な理由。
  • ViewModel に保存されているデータに LiveData を追加する方法。
  • MutableLiveData を使用するタイミングと方法。
  • LiveData. での変更を監視するオブザーバー メソッドを追加する方法
  • バッキング プロパティを使用して LiveData をカプセル化する方法。
  • UI コントローラと対応する ViewModel の間で通信する方法。

演習内容

  • GuessTheWord アプリで、単語とスコアに LiveData を使用します。
  • 単語やスコアが変更されたときに通知を受け取るオブザーバーを追加します。
  • 変更された値を表示するテキスト ビューを更新します。
  • LiveData オブザーバー パターンを使用して、ゲーム終了イベントを追加します。
  • [Play Again] ボタンを実装します。

レッスン 5 の Codelab では、スターター コードから GuessTheWord アプリを開発します。GuessTheWord は、2 人のプレーヤーが最高スコアを目指して協力する、2 プレーヤー ジェスチャー ゲームです。

最初のプレーヤーはアプリの単語を見て、2 番目のプレーヤーに単語を見せないようにしながら、各単語を順番に演じます。2 人目のプレーヤーが単語を当てようとします。

ゲームをプレイするには、最初のプレーヤーがデバイスでアプリを開き、下のスクリーンショットに示すように、「ギター」などの単語を表示します。

最初のプレーヤーは、単語自体を言わないように注意しながら、単語を演じます。

  • 2 人目のプレーヤーが単語を正しく当てると、1 人目のプレーヤーが [Got It] ボタンを押します。これにより、カウントが 1 つ増え、次の単語が表示されます。
  • 2 人目のプレーヤーが単語を当てられない場合、1 人目のプレーヤーが [スキップ] ボタンを押します。これにより、カウントが 1 減り、次の単語にスキップします。
  • ゲームを終了するには、[End Game] ボタンを押します。(この機能は、シリーズの最初の Codelab のスターター コードには含まれていません)。

この Codelab では、ユーザーがアプリ内のすべての単語を使い切ったときにゲームを終了するイベントを追加して、GuessTheWord アプリを改善します。また、スコア フラグメントに [もう一度プレイ ] ボタンを追加して、ユーザーがゲームをもう一度プレイできるようにします。

タイトル画面

ゲーム画面

スコア画面

このタスクでは、この Codelab のスターター コードを見つけて実行します。前の Codelab で作成した GuessTheWord アプリをスターター コードとして使用することも、スターター アプリをダウンロードすることもできます。

  1. (省略可)前の Codelab のコードを使用しない場合は、この Codelab のスターター コードをダウンロードします。コードの ZIP ファイルを展開し、プロジェクトを Android Studio で開きます。
  2. アプリを実行して、ゲームをプレイします。
  3. [Skip] ボタンをタップすると、次の単語が表示され、スコアが 1 減ります。[Got It] ボタンをタップすると、次の単語が表示され、スコアが 1 増えます。[ゲームを終了] ボタンをクリックすると、ゲームが終了します。

LiveData は、ライフサイクル対応の監視可能なデータホルダー クラスです。たとえば、GuessTheWord アプリの現在のスコアを LiveData でラップできます。この Codelab では、LiveData のいくつかの特性について学びます。

  • LiveData は監視可能です。つまり、LiveData オブジェクトに保持されているデータが変更されるとオブザーバーに通知されます。
  • LiveData にはデータが保持されます。LiveData はあらゆる種類のデータに使用できるラッパーです。
  • LiveData はライフサイクル対応です。つまり、STARTEDRESUMED などのアクティブなライフサイクルの状態にあるオブザーバーのみを更新します。

このタスクでは、GameViewModel にある現在のスコアと現在の単語のデータを LiveData に変換することによって、あらゆるデータ型を LiveData オブジェクトにラップする方法を学びます。後のタスクでは、これらの LiveData オブジェクトにオブザーバーを追加し、LiveData を監視する方法を学びます。

ステップ 1: LiveData を使用するようにスコアと単語を変更する

  1. screens/game パッケージで、GameViewModel ファイルを開きます。
  2. 変数 score と変数 word の型を MutableLiveData に変更します。

    MutableLiveData は、値を変更できる LiveData です。MutableLiveData は汎用のクラスであるため、保持するデータの種類を指定する必要があります。
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
  1. GameViewModelinit ブロック内で、scoreword を初期化します。LiveData 変数の値を変更するには、変数に対して setValue() メソッドを使用します。Kotlin では、value プロパティを使用して setValue() を呼び出すことができます。
init {

   word.value = ""
   score.value = 0
  ...
}

ステップ 2: LiveData オブジェクト参照を更新する

score 変数と word 変数が LiveData 型になりました。このステップでは、value プロパティを使用して、これらの変数への参照を変更します。

  1. GameViewModelonSkip() メソッドで、scorescore.value に変更します。scorenull である可能性があるというエラーが表示されます。このエラーを修正します。
  2. このエラーを解決するには、onSkip()score.valuenull チェックを追加します。次に、scoreminus() 関数を呼び出します。この関数は null-safety で減算を行います。
fun onSkip() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.minus(1)
   }
   nextWord()
}
  1. 同様に onCorrect() メソッドを更新します。score 変数に null チェックを追加し、plus() 関数を使用します。
fun onCorrect() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.plus(1)
   }
   nextWord()
}
  1. GameViewModelnextWord() メソッド内で、word の参照を word.value に変更します。
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       word.value = wordList.removeAt(0)
   }
}
  1. GameFragmentupdateWordText() メソッド内で、viewModel.word への参照を viewModel.word.value. に変更します。
/** Methods for updating the UI **/
private fun updateWordText() {
   binding.wordText.text = viewModel.word.value
}
  1. GameFragmentupdateScoreText() メソッド内で、viewModel.score への参照を viewModel.score.value. に変更します。
private fun updateScoreText() {
   binding.scoreText.text = viewModel.score.value.toString()
}
  1. GameFragmentgameFinished() メソッド内で、viewModel.score への参照を viewModel.score.value に変更します。必要な null 安全確認を追加します。
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score.value?:0
   NavHostFragment.findNavController(this).navigate(action)
}
  1. コードにエラーがないことを確認します。アプリをコンパイルして実行します。アプリの機能は以前と同じであるはずです。

このタスクは、スコアと単語のデータを LiveData オブジェクトに変換した前のタスクと密接に関連しています。このタスクでは、Observer オブジェクトをこれらの LiveData オブジェクトに接続します。

  1. onCreateView() メソッド内の GameFragment, で、現在のスコア viewModel.scoreLiveData オブジェクトに Observer オブジェクトをアタッチします。observe() メソッドを使用し、viewModel の初期化後にコードを配置します。ラムダ式を使用してコードを簡素化します。(ラムダ式は、宣言されない匿名関数であり、式としてその場で渡されます)。
viewModel.score.observe(this, Observer { newScore ->
})

Observer への参照を解決します。これを行うには、Observer をクリックして Alt+Enter(Mac の場合は Option+Enter)を押し、androidx.lifecycle.Observer をインポートします。

  1. 作成したオブザーバーは、監視対象の LiveData オブジェクトに保持されているデータが変更されるとイベントを受け取ります。オブザーバー内で、スコア TextView を新しいスコアで更新します。
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Observer オブジェクトを現在の単語の LiveData オブジェクトにアタッチします。Observer オブジェクトを現在のスコアに関連付けたときと同じ方法で行います。
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})

score または word の値が変更されると、画面に表示される score または word が自動的に更新されます。

  1. GameFragment で、メソッド updateWordText()updateScoreText()、およびそれらへの参照をすべて削除します。テキスト ビューは LiveData オブザーバー メソッドによって更新されるため、これらは不要になりました。
  2. アプリを実行します。ゲームアプリは、前とまったく同じように動作しますが、LiveDataLiveData オブザーバーを使用するようになります。

カプセル化により、オブジェクトの一部フィールドへの直接アクセスを制限できます。オブジェクトをカプセル化する場合は、非公開の内部フィールドを変更するためのパブリック メソッドを公開します。カプセル化することで、内部フィールドに対する他のクラスからの操作を制御できます。

現在のコードでは、外部クラスは value プロパティ(たとえば viewModel.score.value)を使用して score 変数と word 変数を変更できます。この Codelab で開発するアプリでは問題にならないかもしれませんが、本番環境のアプリでは、ViewModel オブジェクト内のデータを制御する必要があります。

アプリ内のデータを編集できるのは ViewModel のみです。ただし、UI コントローラはデータを読み取る必要があるため、データ フィールドを完全に非公開にすることはできません。アプリのデータをカプセル化するには、MutableLiveData オブジェクトと LiveData オブジェクトの両方を使用します。

MutableLiveDataLiveData:

  • MutableLiveData オブジェクト内のデータは、名前が示すように変更できます。ViewModel 内ではデータを編集できるように、MutableLiveData を使用します。
  • LiveData オブジェクト内のデータは読み取ることができますが、変更することはできません。ViewModel の外部からは、データを読み取り可能にし、変更は不可能にする必要があるため、データを LiveData として公開する必要があります。

この戦略を実行するには、Kotlin のバッキング プロパティを使用します。バッキング プロパティを使用すると、そのオブジェクト自体ではなくゲッターから返すことができます。このタスクでは、GuessTheWord アプリの score オブジェクトと word オブジェクトのバッキング プロパティを実装します。

バッキング プロパティをスコアと単語に追加する

  1. GameViewModel で、現在の score オブジェクトを private にします。
  2. バッキング プロパティで使用されている命名規則に従って、score_score に変更します。_score プロパティは、内部で使用されるゲームスコアの可変バージョンになりました。
  3. score という名前の LiveData 型のパブリック バージョンを作成します。
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
  1. 初期化エラーが表示される。このエラーは、GameFragment 内で scoreLiveData 参照であり、score がセッターにアクセスできなくなったために発生します。Kotlin のゲッターとセッターについて詳しくは、ゲッターとセッターをご覧ください。

    エラーを解決するには、GameViewModelscore オブジェクトの get() メソッドをオーバーライドし、バッキング プロパティ _score を返します。
val score: LiveData<Int>
   get() = _score
  1. GameViewModel で、score の参照を内部の変更可能なバージョンである _score に変更します。
init {
   ...
   _score.value = 0
   ...
}

...
fun onSkip() {
   if (!wordList.isEmpty()) {
       _score.value = (score.value)?.minus(1)
   }
  ...
}

fun onCorrect() {
   if (!wordList.isEmpty()) {
       _score.value = (score.value)?.plus(1)
   }
   ...
}
  1. score オブジェクトの場合と同様に、word オブジェクトの名前を _word に変更し、バッキング プロパティを追加します。
// 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 オブジェクト wordscore をカプセル化しました。

現在のアプリでは、ユーザーが [End Game] ボタンをタップすると、スコア画面に移動します。また、プレーヤーがすべての単語を完了したら、アプリがスコア画面に移動するようにします。プレーヤーが最後の単語を言い終えたら、ユーザーがボタンをタップしなくてもゲームが自動的に終了するようにします。

この機能を実装するには、すべての単語が表示されたときに ViewModel からフラグメントにトリガーされて通知されるイベントが必要です。これを行うには、LiveData オブザーバー パターンを使用してゲーム終了イベントをモデル化します。

オブザーバー パターン

オブザーバー パターンは、ソフトウェア設計パターンです。オブジェクト間の通信(オブザーバブル(観察の「対象」)とオブザーバー)を指定します。オブザーバブルは、状態の変化をオブザーバーに通知するオブジェクトです。

このアプリの LiveData の場合、オブザーバブル(サブジェクト)は LiveData オブジェクトであり、オブザーバーはフラグメントなどの UI コントローラのメソッドです。LiveData 内のデータが変更されるたびに、状態が変化します。LiveData クラスは、ViewModel からフラグメントへの通信において重要です。

ステップ 1: LiveData を使用してゲーム終了イベントを検出する

このタスクでは、LiveData オブザーバー パターンを使用してゲーム終了イベントをモデル化します。

  1. GameViewModel で、_eventGameFinish という Boolean MutableLiveData オブジェクトを作成します。このオブジェクトはゲーム終了イベントを保持します。
  2. _eventGameFinish オブジェクトを初期化した後、eventGameFinish という名前のバッキング プロパティを作成して初期化します。
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
   get() = _eventGameFinish
  1. GameViewModelonGameFinish() メソッドを追加します。このメソッドで、ゲーム終了イベント eventGameFinishtrue に設定します。
/** Method for the game completed event **/
fun onGameFinish() {
   _eventGameFinish.value = true
}
  1. GameViewModelnextWord() メソッド内で、単語リストが空の場合にゲームを終了します。
private fun nextWord() {
   if (wordList.isEmpty()) {
       onGameFinish()
   } else {
       //Select and remove a _word from the list
       _word.value = wordList.removeAt(0)
   }
}
  1. GameFragmentonCreateView() 内で、viewModel を初期化した後、eventGameFinish にオブザーバーをアタッチします。observe() メソッドを使用します。ラムダ関数内で、gameFinished() メソッドを呼び出します。
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
   if (hasFinished) gameFinished()
})
  1. アプリを実行し、ゲームをプレイして、すべての単語をプレイします。[ゲームを終了] をタップするまでゲーム フラグメントにとどまるのではなく、スコア画面に自動的に移動します。

    単語リストが空になると、eventGameFinish が設定され、ゲーム フラグメントの関連するオブザーバー メソッドが呼び出され、アプリは画面フラグメントに移動します。
  2. 追加したコードによってライフサイクルの問題が発生しています。問題を把握するため、GameFragment クラスの gameFinished() メソッドでナビゲーション コードをコメントアウトします。メソッドに Toast メッセージを保持するようにしてください。
private fun gameFinished() {
       Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
//        val action = GameFragmentDirections.actionGameToScore()
//        action.score = viewModel.score.value?:0
//        NavHostFragment.findNavController(this).navigate(action)
   }
  1. アプリを実行し、ゲームをプレイして、すべての単語をプレイします。ゲーム画面の下部に「ゲームが終了しました」というトースト メッセージが短時間表示されます。これは想定される動作です。

デバイスまたはエミュレータを回転させます。トーストが再び表示されます。デバイスを数回回転させると、おそらく毎回トーストが表示されます。これはバグです。トーストはゲームが終了したときに 1 回だけ表示されるはずです。フラグメントが再作成されるたびにトーストが表示されるべきではありません。この問題は次のタスクで解決します。

ステップ 2: ゲーム終了イベントをリセットする

通常、LiveData はデータが変更された場合にのみオブザーバーに更新を配信します。オブザーバーは、非アクティブな状態からアクティブな状態に変わったときにも最新データを受け取りますが、これは例外的な動作です。

そのため、アプリでゲーム終了のトーストが繰り返しトリガーされます。画面の回転後にゲーム フラグメントが再作成されると、非アクティブ状態からアクティブ状態に移行します。フラグメント内のオブザーバーが既存の ViewModel に再接続され、現在のデータを受信します。gameFinished() メソッドが再度トリガーされ、トーストが表示されます。

このタスクでは、GameViewModeleventGameFinish フラグをリセットして、この問題を修正し、トーストを 1 回だけ表示します。

  1. GameViewModel で、ゲーム終了イベント _eventGameFinish をリセットする onGameFinishComplete() メソッドを追加します。
/** Method for the game completed event **/

fun onGameFinishComplete() {
   _eventGameFinish.value = false
}
  1. GameFragmentgameFinished() の最後で、viewModel オブジェクトの onGameFinishComplete() を呼び出します。(ナビゲーション コードは gameFinished() でコメントアウトしたままにしておきます)。
private fun gameFinished() {
   ...
   viewModel.onGameFinishComplete()
}
  1. アプリを実行して、ゲームをプレイします。すべての単語を確認してから、デバイスの画面の向きを変更します。トーストは 1 回だけ表示されます。
  2. GameFragmentgameFinished() メソッド内で、ナビゲーション コードのコメントを解除します。

    Android Studio でコメントを解除するには、コメントアウトされている行を選択して Control+/(Mac の場合は Command+/)を押します。
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score.value?:0
   findNavController(this).navigate(action)
   viewModel.onGameFinishComplete()
}

Android Studio のプロンプトが表示された場合は、androidx.navigation.fragment.NavHostFragment.findNavController をインポートします。

  1. アプリを実行して、ゲームをプレイします。すべての単語を終えた後、アプリが自動的に最終スコア画面に移動することを確認します。

その調子です。アプリは LiveData を使用してゲーム終了イベントをトリガーし、GameViewModel から単語リストが空であることをゲーム フラグメントに伝えます。ゲーム フラグメントはスコア フラグメントに移動します。

このタスクでは、ScoreViewModel のスコアを LiveData オブジェクトに変更し、オブザーバーをアタッチします。このタスクは、GameViewModelLiveData を追加したときに行った作業と似ています。

これらの変更は、アプリ内のすべてのデータが LiveData を使用するように、完全性を確保するために ScoreViewModel に行います。

  1. ScoreViewModel で、score 変数の型を MutableLiveData に変更します。慣例に従って _score に名前を変更し、バッキング プロパティを追加します。
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
   get() = _score
  1. ScoreViewModelinit ブロック内で、_score を初期化します。init ブロックのログは、必要に応じて削除またはそのままにできます。
init {
   _score.value = finalScore
}
  1. ScoreFragmentonCreateView() 内で、viewModel を初期化した後、スコア LiveData オブジェクトのオブザーバーをアタッチします。ラムダ式内で、スコア値をスコア テキストビューに設定します。ViewModel から、スコア値をテキストビューに直接割り当てるコードを削除します。

追加するコード:

// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})

削除するコード:

binding.scoreText.text = viewModel.score.toString()

Android Studio のプロンプトが表示されたら、androidx.lifecycle.Observer をインポートします。

  1. アプリを実行して、ゲームをプレイします。アプリは以前と同じように動作しますが、スコアの更新に LiveData とオブザーバーを使用するようになります。

このタスクでは、スコア画面に [Play Again] ボタンを追加し、LiveData イベントを使用してクリック リスナーを実装します。ボタンは、スコア画面からゲーム画面に移動するイベントをトリガーします。

アプリのスターター コードには [もう一度プレイ] ボタンが含まれていますが、このボタンは非表示になっています。

  1. res/layout/score_fragment.xmlplay_again_button ボタンで、visibility 属性の値を visible に変更します。
<Button
   android:id="@+id/play_again_button"
...
   android:visibility="visible"
 />
  1. ScoreViewModel で、_eventPlayAgain という Boolean を保持する LiveData オブジェクトを追加します。このオブジェクトは、スコア画面からゲーム画面に移動するために LiveData イベントを保存するために使用されます。
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
   get() = _eventPlayAgain
  1. ScoreViewModel で、イベント _eventPlayAgain を設定およびリセットするメソッドを定義します。
fun onPlayAgain() {
   _eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
   _eventPlayAgain.value = false
}
  1. ScoreFragment で、eventPlayAgain のオブザーバーを追加します。コードを onCreateView() の末尾、return ステートメントの前に配置します。ラムダ式内で、ゲーム画面に戻り、eventPlayAgain をリセットします。
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
   if (playAgain) {
      findNavController().navigate(ScoreFragmentDirections.actionRestart())
       viewModel.onPlayAgainComplete()
   }
})

Android Studio のプロンプトが表示されたら、androidx.navigation.fragment.findNavController をインポートします。

  1. ScoreFragmentonCreateView() の中で、[PlayAgain] ボタンにクリック リスナーを追加し、viewModel.onPlayAgain() を呼び出します。
binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. アプリを実行して、ゲームをプレイします。ゲームが終了すると、スコア画面に最終スコアと [Play Again](もう一度プレイ)ボタンが表示されます。[PlayAgain] ボタンをタップすると、アプリがゲーム画面に移動し、ゲームを再度プレイできます。

よかったですね!アプリのアーキテクチャを変更して ViewModelLiveData オブジェクトを使用し、オブザーバーを LiveData オブジェクトにアタッチしました。LiveData は、LiveData が保持する値が変更されるとオブザーバー オブジェクトに通知します。

Android Studio プロジェクト: GuessTheWord

LiveData

  • LiveData は、ライフサイクル対応の監視可能なデータホルダー クラスであり、Android アーキテクチャ コンポーネントの 1 つです。
  • LiveData を使用すると、データが更新されたときに UI が自動的に更新されるようにできます。
  • LiveData は監視可能です。つまり、LiveData オブジェクトに保持されているデータが変更されると、アクティビティやフラグメントなどのオブザーバーに通知されます。
  • LiveData にはデータが保持されます。これは、あらゆる種類のデータに使用できるラッパーです。
  • LiveData はライフサイクル対応です。つまり、STARTEDRESUMED などのアクティブなライフサイクルの状態にあるオブザーバーのみを更新します。

LiveData を追加するには

  • ViewModel のデータ変数の型を LiveData または MutableLiveData に変更します。

MutableLiveData は、値を変更できる LiveData オブジェクトです。MutableLiveData は汎用のクラスであるため、保持するデータの種類を指定する必要があります。

  • LiveData が保持するデータの値を変更するには、LiveData 変数で setValue() メソッドを使用します。

LiveData をカプセル化するには

  • ViewModel 内の LiveData は編集可能である必要があります。ViewModel の外部では、LiveData は読み取り可能である必要があります。これは、Kotlin のバッキング プロパティを使用して実装できます。
  • Kotlin のバッキング プロパティを使用すると、そのオブジェクト自体ではなくゲッターから返すことができます。
  • LiveData をカプセル化するには、ViewModel 内で private MutableLiveData を使用し、ViewModel の外で LiveData バッキング プロパティを返します。

Observable LiveData

  • LiveData はオブザーバー パターンに従います。「オブザーバブル」は LiveData オブジェクトで、オブザーバーはフラグメントなどの UI コントローラのメソッドです。LiveData 内でラップされたデータが変更されるたびに、UI コントローラのオブザーバー メソッドに通知が送信されます。
  • LiveData をオブザーバブルにするには、observe() メソッドを使用して、オブザーバー(アクティビティやフラグメントなど)の LiveData 参照にオブザーバー オブジェクトをアタッチします。
  • この LiveData オブザーバー パターンを使用して、ViewModel から UI コントローラに通信できます。

Udacity コース:

Android デベロッパー ドキュメント:

その他:

このセクションでは、インストラクター主導のコースの一環として、この Codelab に取り組んでいる生徒向けに考えられる宿題をいくつか示します。インストラクターは、以下のようなことを行えます。

  • 必要に応じて宿題を与える
  • 宿題の提出方法を生徒に伝える
  • 宿題を採点する

インストラクターは、これらの提案を必要なだけ使用し、必要に応じて他の宿題も自由に与えることができます。

この Codelab に独力で取り組む場合は、これらの宿題を自由に使用して知識をテストしてください。

以下の質問に回答してください

問題 1

ViewModel に格納された LiveData をカプセル化して、外部オブジェクトがデータを更新せずに読み取れるようにするにはどうすればよいですか。

  • ViewModel オブジェクト内で、データのデータ型を private LiveData に変更する。バッキング プロパティを使用して、MutableLiveData 型の読み取り専用データを公開する。
  • ViewModel オブジェクト内で、データのデータ型を private MutableLiveData に変更する。バッキング プロパティを使用して、LiveData 型の読み取り専用データを公開する。
  • UI コントローラ内で、データのデータ型を private MutableLiveData に変更する。バッキング プロパティを使用して、LiveData 型の読み取り専用データを公開する。
  • ViewModel オブジェクト内で、データのデータ型を LiveData に変更する。バッキング プロパティを使用して、LiveData 型の読み取り専用データを公開する。

問題 2

LiveData が UI コントローラ(フラグメントなど)を更新するのは、UI コントローラが次のうちどの状態にある場合ですか?

  • 再開
  • バックグラウンド
  • 一時停止
  • 停止

問題 3

LiveData オブザーバー パターンで、監視可能なアイテム(監視対象)は何ですか?

  • オブザーバー メソッド
  • LiveData オブジェクト内のデータ
  • UI コントローラ
  • ViewModel オブジェクト

次のレッスンに進む: 5.3: ViewModel と LiveData を使用したデータ バインディング

このコースの他の Codelab へのリンクについては、Android Kotlin の基礎の Codelab のランディング ページをご覧ください。