学ぶこと
- 推奨される Android app architecture を使用方法
- アプリで
Lifecycle
、ViewModel
、およびViewModelFactory
クラスを使用する方法。 - device-configuration の変更を通じて UI データを保持する方法
- factory method デザインパターンとは何か、およびその使用方法。
- インターフェース
ViewModelProvider.Factory
を使用してViewModel
オブジェクトを作成する方法。
すること
ViewModel
をアプリに追加して、アプリのデータを保存し、データが configuration の変更に耐えられるようにします。ViewModelFactory
と factory-method デザインパターンを使用して、コンストラクターパラメーターを使用してViewModel
オブジェクトをインスタンス化します。
アプリの概要
レッスン5のコードラボでは、スターターコードから始めて、GuessTheWord アプリを開発します。GuessTheWord は、二人用の charades-style のゲームで、Player が協力して可能な限り最高のスコアを達成します。
最初のプレーヤーは、実際に単語自体を言わないように注意しながら、単語を実行します。
- 二番目のプレーヤーが単語を正しく推測すると、最初のプレーヤーが Got it ボタンを押します。これにより、カウントが1つ増え、次の単語が表示されます。
- 二番目のプレーヤーが単語を推測できない場合、最初のプレーヤーはスキップボタンを押します。これにより、カウントが1つ減り、次の単語にスキップします。
- ゲームを終了するには、End Game ボタンを押します。(この機能は、シリーズの最初のコードラボのスターターコードには含まれていません)
Starter code を調べる
このタスクでは、Starter アプリをダウンロードして実行し、コードを調べます。
Get started
- Starter code をダウンロードして Android Studio で調べます。
- エミュレータでアプリを実行します。
- ボタンをタップします。Skip ボタンは次の単語を表示してスコアを1つ減らし、Got it ボタンは次の単語を表示してスコアを1つ増やします。End Game ボタンは実装されていないため、タップしても何も起こりません。
Code walkthrough を実行する
- Android Studio でコードを調べて、アプリがどのように機能するかを確認します。
- 特に重要な以下のファイルを必ず確認したください。
MainActivity.kt
このファイルには、デフォルトのテンプレート生成コードのみが含まれています。
res/layout/main_activity.xml
このファイルには、アプリの main layout が含まれています。NavHostFragment
は、ユーザがアプリ内を移動する時に他の Fragment をホストします。
UI Fragment
Starter code には、com.example.android.guesstheword.screens
パッケージの下の3つの異なるパッケージに3つの Fragment があります。
- タイトル画面の
title/TitleFragment
- ゲーム画面の
game/GameFragment
- スコア画面の
score/ScoreFragment
screens/title/TitleFragment.kt
Title Fragment は、アプリの起動時に表示される最初の画面です。クリックハンドラーが Play ボタンに設定され、ゲーム画面に移動します。
screens/game/GameFragment.kt
これは、ゲームのアクションのほとんどが行われる main fragment です。
- 変数は、現在の単語と現在のスコアに対して定義されます。
resetList()
メソッド内で定義されているwordList
は、ゲームで使用される単語のサンプルリストです。onSkip()
メソッドは、Skip ボタンのクリックハンドラーです。スコアを1減らし、nextWord()
メソッドを使用して次の単語を表示します。onCorrect()
メソッドは、GotIt ボタンのクリックハンドラーです。このメソッドは、onSkip()
メソッドと同様に実装されます。
screens/score/ScoreFragment.kt
ScoreFragment
はゲームの最終画面であり、プレーヤーの最終スコアを表示します。このコードラボでは、この画面を使用して最終スコアを表示する実装を追加します。
res/navigation/main_navigation.xml
navigation graph は、fragment が navigation を介してどのように接続されているかを示しています:
- TitleFragment から、ユーザは GameFragment に移動できます。
- GameFragment から、ユーザは ScoreFragment に移動できます。
- ScoreFragmet から、ユーザは GameFragment に戻ることができます。
Starter app で問題を見つける
このタスクでは、GuessTheWord Starter app の問題を見つけます。
Starter code を実行し、各単語の後に Skip または Got it をタップして、いくつかの単語でゲームをプレイします。
ゲーム画面に単語と現在のスコアが表示されます。デバイスまたはエミュレータを回転させて、画面のむきをを変更します。現在のスコアが失われていることに注意してください。
さらにいくつかの単語でゲームを実行します。ゲーム画面にスコアが表示されたら、アプリ閉じてから再度開きます。アプリの状態が保存されてないため、ゲームが最初から再開されることに注意してください。
いくつかの単語でゲームをプレイしてから、End Game ボタンをタップします。何も起こらないことに注意してください。
アプリの問題:
Starter app は、デバイスの向きが変わった時やアプリがシャットダウンして再起動した時など、configuration の変更中にアプリの状態を保存および復元しません。 この問題は、
onSaveInstanceState
コールバックを使用して解決できます。ただし、onSaveInstanceState()
メソッドを使用するには、状態をバンドルに保存するための追加のコードを記述し、その状態を取得するロジックを実装する必要があります。また、保存できるデータの量は最小限です。ユーザが End Game をタップした時に、ゲーム画面はスコア画面に navigate されません。
これらの問題は、このコードラボで学習した app architecture components を使用して解決できます。
App architecture
app architecture は、アプリのクラスとそれらの間の関係を設計する方法であり、コードが整理され、特定のシナリオで適切に機能し、操作が簡単です。この4つのコードラボのセットでは、GuessTheWord アプリに加えた改善は、Android app architecture ガイドラインに従い、Android Architecture Components を使用します。Android app architecture は、MVVM architecture パターンに似ています。
GuessTheWordアプリは、関心の分離 の設計原則に従い、クラスに分割され、各クラスは個別の関心に対処します。レッスンのこの最初のコードラボでは、使用するクラスは UI controller、ViewModel
および ViewModelFactory
です。
UI controller
UI controller は、Activity
や Fragment
などの UI-based のクラスです。UI controller には、View の表示やユーザ入力のキャプチャなど、UI と operation-system の相互作用を処理するロジックのみを含める必要があります。表示するテキストを決定するロジックなどの意思決定ロジックを UI controller に入れないでください。
GuessTheWord starter code では、UI controller は GameFragment
、ScoreFragment
、TitleFragment
の3つの Fragment です。"関心の分離" の設計原則に従って、GameFragment
は、ゲーム要素を画面に描画し、ユーザがボタンをいつタップしたかを知ることだけを担当します。ユーザがボタンをタップすると、この情報が GameViewModel
に渡されます。
ViewModel
ViewModel
は、ViewModel
に関連付けられた Fragment または Activity に表示されるデータを保持します。ViewModel
は、データに対して簡単な計算と変換を実行して、UI controller によって表示されるデータを準備できます。この architecture では、ViewModel
が意思決定を実行します。
GameViewModel
は、スコア値、単語のリスト、現在の単語などのデータを保持します。これは、これらが画面に表示されるデータであるためです。GameViewModel
には、データの現在の状態を判断するための簡単な計算を実行するためのビジネスロジックも含まれています。
ViewModelFactory
ViewModelFactory
は、コンストラクターパラメーターの有無に関わらず、ViewModel
オブジェクトをインスタンス化 します。
後のコードラボでは、UI controller と ViewModel
に関連する他の Android Architecture Components について学習します。
GameViewModel を作成する
ViewModel
クラスは、UI 関連のデータを格納および管理するように設計されています。このアプリでは、各 ViewModel が1つの Fragment に関連付けられています。
このタスクでは、最初の ViewModel
をアプリに追加します。GameFragment
のための GameViewModel
です。また、ViewModel
がライフサイクル対応であることの意味についても学びます。
GameViewModel クラスを追加する
build.gradle(module:app)
ファイルを開きます。dependencies
ブロック内に、ViewModel
の Gradle dependency を追加します。
//ViewModel implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
パッケージ
screens/game/
フォルダーに、GameViewModel
という新しい Kotlin クラスを作成します。GameViewModel
クラスに抽象クラスViewModel
を拡張させます。ViewModel
がどのようにライフサイクルに対応しているかをよりよく理解するために、log
ステートメントを使用してinit
ブロックを追加します。
class GameViewModel : ViewModel() { init { Log.i("GameViewModel", "GameViewModel created!") } }
onCleard() をオーバーライドし、ロギングを追加
ViewModel
は、関連づけられた Fragment が detach されるか、Activity が終了すると破棄されます。ViewModel
が破棄される直前に、onCleared()
コールバックが呼び出されてリソースがクリーンアップされます。
GameViewModel
クラスで、onCleared()
メソッドをオーバーライドします。onCleared()
内にログステートメントを追加して、GameViewModel
のライフサイクルを追跡します。
override fun onCleared() { super.onCleared() Log.i("GameViewModel", "GameViewModel destroyed!") }
GameViewModel をゲームフラグメントに関連付けます
ViewModel
は UI Controller に関連付ける必要があります。2つを関連付けるには、UI Controller 内に ViewModel
への参照を作成します。
このステップでは、対応する UI Controller (GameFragmen
)内に GameViewModel
への参照を作成します。
GameFragment
クラスで、GameViewModel
タイプのフィールドをクラス変数として追加します。
private lateinit var viewModel: GameViewModel
ViewModel を初期化する
画面の回転などの configuration 変更中に、Fragment などの UI Controller が再作成されます。ただし、ViewModel
インスタンスは存続します。ViewModel
クラスを使用して ViewModel
インスタンスを作成すると、Fragmnet が再作成されるたびに新しいオブジェクトが作成されます。代わりに、ViewModelProvider
を使用して ViewModel
インスタンスを作成します。
Important:
ViewModel
のインスタンスを直接インスタンス化するのではなく、常に ViewModelProvider
を使用して ViewModel
オブジェクトを作成してください。
ViewModelProvider
の仕組み:
ViewModelProvider
は、既存のViewModel
が存在する場合はそれを返し、まだ存在しない場合は新しいViewModel
を作成します。ViewModelProvider
は、指定されたスコープ(Activity または Fragment)に関連付けられたViewModel
インスタンスを作成します。- 作成された
ViewModel
は、スコープが有効である限り保持されます。例えば、スコープが Fragment の場合、Fragment がデタッチされるまでViewModel
は保持されます。
ViewModelProvider.get()
) メソッドを使用して ViewModelProvider
を作成し、ViewModel
を初期化します。
GameFragment
クラスで、viewModel
変数を初期化します。binding 変数の定義後、このコードをonCreateView()
内に配置します。ViewModelProvider.get()
メソッドを使用して、関連するGameFragment
コンテキストとGameViewModel
クラスを渡します。ViewModel
オブジェクトの初期化の上に、ViewModelProvider.get()
メソッド呼び出しをログに記録するログステートメントを追加します。
Log.i("GameFragment", "Called ViewModelProvider.get") viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
- アプリを実行します。Android Studio で Logcat ペインを開き、
Game
でフォルタリングします。エミュレータの Play ボタンをタップします。ゲーム画面が開きます。
Logcat に示されているように、GameFragment
の onCreateView()
メソッドは、ViewModelProvider.get()
メソッドを呼び出して GameViewModel
を作成します。GameFragment
と GameViewModel
に追加したロギングステートメントが Logcat に表示されます。
I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel created!
デバイスまたはエミュレータで自動回転設定を有効にし、画面の向きを数回変更します。GameFragment
は毎回破棄されて再作成されるため、ViewModelProvider.get()
が毎回呼び出されます。ただし、GameViewModel
は一回だけ作成され、呼び出しごとに再作成または破棄されることはありません。
I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get
- ゲームを終了するか、game fragment から移動します。
GameFragment
が破棄されます。関連するGameViewModel
も破棄され、コールバックonCleared()
が呼び出されます。
I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get I/GameFragment: Called ViewModelProvider.get I/GameViewModel: GameViewModel destroyed!
GameViewModel にデータを入力します
ViewModel
は configuration の変更に耐えるため、configuration の変更に耐える必要があるデータに適した場所です。
- 画面に表示するデータと、そのデータを処理するコードを
ViewModel
に配置します。 Activity
・Fragment
・View
は構成の変更後も存続しないため、ViewModel
に Fragment、Activity、View への参照を含めることはできません。
比較のために、ViewModel
を追加する前と ViewModel
を追加した後、スターターアプリで GameFragment
UI データがどのように処理されるかを次に示します:
ViewModel
追加前:- アプリが画面の回転などの configuration 変更を行うと、GameFragment が破棄され、再作成されます。データは失われます。
ViewModel
を追加し、UI データをViewModel
に移動した後:- Fragment が表示する必要のある全てのデータは、
ViewModel
になりました。アプリで構成が変更されても、ViewModel
は存続し、データは保持されます。
- Fragment が表示する必要のある全てのデータは、
このタスクでは、データを処理するためのメソッドとともに、アプリの UI データを GameViewModel
クラスに移動します。これを行うと、configuration の変更中にデータが保持されます。
データフィールドとデータ処理を ViewModel に移動します
次のデータフィールドとメソッドを GameFragment
から GameViewModel
に移動します:
word
、score
、wordList
のデータフィールドを移動します。word
とscore
がprivate
でないことを確認してください。
View への参照が含まれているため、binding 変数の GameFragmentBinding
を移動しないでください。この変数は、レイアウトを Inflate し、クリックリスナーを設定し、画面にデータを表示するために使用されます。これは Fragment の責任です。
resetList()
メソッドとnextWord()
メソッドを移動します。これらのメソッドは、画面に表示する単語を決定します。onCreateView()
メソッド内から、resetList()
およびnextWord()
へのメソッド呼び出しをGameViewModel
のinit
ブロックに移動します。
Fragment が作成されるたびにではなく、ViewModel
の作成時に単語リストをリセットする必要があるため、これらのメソッドは init
ブロックに含まれている必要があります。GameFragment
の init
ブロックにあるログステートメントを削除できます。
GameFragmnet
の onSkip()
および onCorrect()
クリックハンドラーには、データを処理して UI を更新するためのコードが含まれています。UI を更新するコードは Fragment 内にとどまる必要がありますが、データを処理するためのコードを ViewModel
に移動する必要があります。
今は、両方の場所に同じメソッドを配置します:
onSkip()
メソッドとonCorrect()
メソッドをGameFragment
からGameViewModel
にコピーします。GameViewModel
で、onSkip
メソッドとonCorrect
メソッドがprivate
ではないことを確認します。これは、これらのメソッドを Fragment から参照するためです。
リファクタリング後の GameViewModel
クラスのコードは次の通りです:
class GameViewModel : ViewModel() { // The current word var word = "" // The current score var score = 0 // The list of words - the front of the list is the next word to guess private lateinit var wordList: MutableList<String> /** * Resets the list of words and randomizes the order */ private fun resetList() { wordList = mutableListOf( "queen", "hospital", "basketball", "cat", "change", "snail", "soup", "calendar", "sad", "desk", "guitar", "home", "railway", "zebra", "jelly", "car", "crow", "trade", "bag", "roll", "bubble" ) wordList.shuffle() } init { resetList() nextWord() Log.i("GameViewModel", "GameViewModel created!") } /** * Moves to the next word in the list */ private fun nextWord() { if (!wordList.isEmpty()) { //Select and remove a word from the list word = wordList.removeAt(0) } updateWordText() updateScoreText() } /** Methods for buttons presses **/ fun onSkip() { score-- nextWord() } fun onCorrect() { score++ nextWord() } override fun onCleared() { super.onCleared() Log.i("GameViewModel", "GameViewModel destroyed!") } }
GameFragment
のコード:
/** * Fragment where the game is played */ class GameFragment : Fragment() { private lateinit var binding: GameFragmentBinding private lateinit var viewModel: GameViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { // Inflate view and obtain an instance of the binding class binding = DataBindingUtil.inflate( inflater, R.layout.game_fragment, container, false ) Log.i("GameFragment", "Called ViewModelProvider.get") viewModel = ViewModelProvider(this).get(GameViewModel::class.java) binding.correctButton.setOnClickListener { onCorrect() } binding.skipButton.setOnClickListener { onSkip() } updateScoreText() updateWordText() return binding.root } /** Methods for button click handlers **/ private fun onSkip() { score-- nextWord() } private fun onCorrect() { score++ nextWord() } /** Methods for updating the UI **/ private fun updateWordText() { binding.wordText.text = word } private fun updateScoreText() { binding.scoreText.text = score.toString() } }
GameFragment のクリックハンドラーとデータフィールドへの参照を更新します
GameFragment
で、onSkip()
メソッドとonCorrect()
メソッドを更新します。コードを削除してスコアを更新し、代わりにviewModel
で対応するonSkip()
メソッドとonCorrect()
メソッドを呼び出します。nextWord()
メソッドをViewModel
に移動したため、GameFragment はそれにアクセスできなくなりました。
GameFragment
の onSkip()
メソッドと onCorrect()
メソッドで、nextWord()
の呼び出しを updateScoreText()
と updateWordText()
置き換えます。これらのメソッドは、画面にデータを表示します。
private fun onSkip() { viewModel.onSkip() updateWordText() updateScoreText() } private fun onCorrect() { viewModel.onCorrect() updateScoreText() updateWordText() }
score
とword
変数は現在GameViewModel
にあるため、GameFragment
にある変数を更新します。
private fun updateWordText() { binding.wordText.text = viewModel.word } private fun updateScoreText() { binding.scoreText.text = viewModel.score.toString() }
Reminder:
Activity、Fragment および View は configuration の変更後も存続しないため、ViewModel
にアプリの Activity、Fragment、View への参照を含めることはできません。
GameViewModel
のnextWord()
メソッド内で、updateWordText()
メソッドとupdateScoreText()
メソッドの呼び出しを削除します。これらのメソッドは現在、GameFragment
から呼び出されています。アプリをビルドし、エラーがないことを確認します。エラーが発生した場合は、プロジェクトをクリーンアップして rebuild します。
アプリを実行し、いくつかの Word でゲームをプレイします。ゲーム画面を表示している時に、デバイスを回転させます。現在のスコアと単語は、向きを変更した後も保持されることに注目してください。
End Game ボタンのクリックリスナーを実装する
このタスクでは、End Game ボタンのクリックリスナーを実装します。
GameFragment
で、onEndGame()
と呼ばれるメソッドを追加します。onEndGame()
メソッドは、ユーザが End Game ボタンをタップした時に呼ばれます。
private fun onEndGame() { }
GameFragment
のonCreateView()
メソッド内で、GotIt ボタンと Skip ボタンのクリックリスナーを設定するコードを見つけます。これら2行のすぐ下に、End Game ボタンのクリックリスナーを設定します。binding
変数を使用します。クリックリスナー内で、onEndGame()
メソッドを呼び出します。
binding.endGameButton.setOnClickListener { onEndGame() }
GameFragment
で、gameFinished()
というメソッドを追加して、アプリをスコア画面に移動します。Safe Args を使用して、引数としてスコアを渡します。
/** * Called when the game is finished */ private fun gameFinished() { Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show() val action = GameFragmentDirections.actionGameToScore() action.score = viewModel.score NavHostFragment.findNavController(this).navigate(action) }
onEndGame()
メソッド内で、gameFinished()
メソッドを呼び出します。
private fun onEndGame() { gameFinished() }
- アプリを実行し、ゲームをプレイします。End Game ボタンをタップします。アプリはスコア画面に移動しますが、最終スコアは表示されないことに注意してください。次のタスクでこれを修正します。
![]() |
![]() |
---|---|
ViewModelFactory を使用する
ユーザがゲームを終了すると、ScoreFragment
はスコアを表示しません。ViewModel
が ScoreFragment
によって表示されるスコアを保持する必要があります。factory medhot pattern を使用して、ViewModel
の初期化中にスコア値を渡します。
factory method pattern は、factory method を使用してオブジェクトを作成する creational design pattern です。factory method は、同じクラスのインスタンスを返すメソッドです。
このタスクでは、スコアフラグメントのパラメーター化されたコンストラクターと ViewModel
をインスタンス化するファクトリメソッドを使用して ViewModel
を作成します。
スコアパッケージの下に、
ScoreViewModel
という新しい Kotlin クラスを作成します。このクラスは、score fragment のViewModel
になります。ViewModel
からScoreViewModel
クラスを拡張します。最終スコアのコンストラクターパラメーターを追加します。ログステートメントを使用してinit
ブロックを追加します。ScoreViewModel
クラスで、score
という変数を追加して、最終スコアを保存します。
class ScoreViewModel(finalScore: Int) : ViewModel() { // The final score var score = finalScore init { Log.i("ScoreViewModel", "Final score is $finalScore") } }
score
パッケージの下に、ScoreViewModelFactory
という別の Kotlin クラスを作成します。このクラスは、ScoreViewModel
オブジェクトのインスタンス化を担当します。ViewModelProvider.Factory
からScoreViewModelFactory
クラスを拡張します。最終スコアのコンストラクターパラメーターを追加します。
class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory { }
ScoreViewModelFactory
で、Android Studio は実装されていない抽象メンバーに関するエラーを表示します。エラーを解決するには、create()
メソッドをオーバーライドします。create()
メソッドで、新しく作成されたScoreViewModel
オブジェクトを返します。
override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) { return ScoreViewModel(finalScore) as T } throw IllegalArgumentException("Unknown ViewModel class") }
ScoreFragment
で、ScoreViewModel
およびScoreViewModelFactory
のクラス変数を作成します。
private lateinit var viewModel: ScoreViewModel private lateinit var viewModelFactory: ScoreViewModelFactory
ScoreFragment
のonCreateView()
内で、binding
変数を初期化した後、viewModelFactory
を初期化します。ScoreViewModelFactory
を使用します。コンストラクターパラメータとして、argument bundle から final score をScoreViewModelFactory()
に渡します。
viewModelFactory = ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(arguments!!).score)
onCreateView()
で、viewModelFactory
を初期化した後、viewModel
オブジェクトを初期化します。ViewModelProvider.get()
メソッドを呼び出し、関連する score fragment conext とviewModelFactory
を渡します。これにより、viewModelFactory
クラスで定義されたファクトリメソッドを使用してScoreViewModel
オブジェクトが作成されます。
viewModel = ViewModelProvider(this, viewModelFactory) .get(ScoreViewModel::class.java)
onCreateView()
メソッドで、viewModel
を初期化した後、scoreText
view のテキストをScoreViewModel
で定義された最終スコアに設定します。
binding.scoreText.text = viewModel.score.toString()
- アプリを実行してゲームをプレイします。スコアフラグメントに最終スコアが表示されていることに注意してください。
- option:
ScoreViewModel
でフィルタリングして、Logcat のScoreViewModel
ログを確認します。スコア値が表示されます。
2019-02-07 10:50:18.328 com.example.android.guesstheword I/ScoreViewModel: Final score is 15
Note:
このアプリでは、スコアを viewModel.score
変数に直接割り当てることができるため、ScoreViewModel
に ViewModelFactory
を追加する必要はありません。ただし、viewModel
の初期化時にデータが必要になる場合があります。
このタスクでは、ViewModel
を使用するように ScoreFragment
を実装しました。また、ViewModelFactory
インターフェースを使用して ViewModel
のパラメーター化されたコンストラクターを作成する方法も学習しました。
まとめ
- Android app architecture ガイドラインでは、異なる責任を持つクラスを分離することを推奨しています。
UI Controller は
Activity
やFragment
などの UI のベースとなるクラスです。UI controllers には、UI とオペレーティングシステムの相互作用を処理するロジックのみを含める必要があります。UI に表示するデータを含めるべきではありません。そのようなデータはViewModel
に配置します。ViewModel
クラスは、UI 関連のデータを格納および管理します。ViewModel
クラスを使用すると、データは画面の回転などの configuration の変更に耐えることができます。ViewModel
は、推奨される Android Architecture Components の1つです。ViewModelProvider.Factory
は、ViewModel
オブジェクトを作成するために使用できるインターフェースです。
次の表は、UI controllers とそれらのデータを保持する ViewModel
インスタンスを比較しています:
UI controller | ViewModel |
---|---|
UI controller の例は、このコードラボで作成した ScoreFragment です。 |
ViewModel の例は、このコードラボで作成した ScoreViewModel です。 |
UI に表示されるデータは含まれていません。 | UI controller が UI に表示するデータが含まれます。 |
データを表示するためのコードと、クリックリスナーなどのユーザーイベントコードが含まれています。 | データ処理用のコードが含まれています。 |
configuration が変更されるたびに破棄され、再作成されます。 | 関連づけられた UI controller が完全に消えた場合にのみ破棄されます。Activity の場合、Activity が終了した場合、または Fragment の場合、Fragment が切り離された場合 |
View が含まれています。 | Activity、Fragment、または View への参照を含めることはできません。これらは configuration の変更後も存続しないためですが、ViewModel には存続します。 |
関連する ViewModel への参照が含まれています。 |
関連する UI controller への参照は含まれていません。 |