Dieses Codelab ist Teil des Kurses „Grundlagen von Android und Kotlin“. Sie können diesen Kurs am besten nutzen, wenn Sie die Codelabs der Reihe nach durcharbeiten. Alle Codelabs des Kurses sind auf der Landingpage für Codelabs zu den Grundlagen von Android und Kotlin aufgeführt.
Einführung
Im vorherigen Codelab haben Sie in der App „GuessTheWord“ ein ViewModel
verwendet, damit die Daten der App Änderungen an der Gerätekonfiguration überstehen. In diesem Codelab erfahren Sie, wie Sie LiveData
in die Daten der ViewModel
-Klassen einbinden. Mit LiveData
, einer der Android Architecture Components, können Sie Datenobjekte erstellen, die Ansichten benachrichtigen, wenn sich die zugrunde liegende Datenbank ändert.
Um die Klasse LiveData
zu verwenden, richten Sie „Beobachter“ (z. B. Aktivitäten oder Fragmente) ein, die Änderungen an den Daten der App beobachten. LiveData
ist lebenszyklusbezogen. Daher werden nur App-Komponenten-Beobachter aktualisiert, die sich in einem aktiven Lebenszyklusstatus befinden.
Was Sie bereits wissen sollten
- So erstellen Sie grundlegende Android-Apps in Kotlin.
- So navigieren Sie zwischen den Zielen Ihrer App.
- Aktivitäts- und Fragmentlebenszyklus.
ViewModel
-Objekte in Ihrer App verwendenViewModel
-Objekte mit derViewModelProvider.Factory
-Schnittstelle erstellen
Lerninhalte
- Was
LiveData
-Objekte nützlich macht. - So fügen Sie
LiveData
zu den in einerViewModel
gespeicherten Daten hinzu. - Wann und wie Sie
MutableLiveData
verwenden. - Beobachtermethoden hinzufügen, um Änderungen in
LiveData.
zu beobachten LiveData
mit einer Backing Property kapseln- So kommunizieren Sie zwischen einem UI-Controller und dem entsprechenden
ViewModel
.
Aufgaben
- Verwende
LiveData
für das Wort und die Punktzahl in der GuessTheWord App. - Fügen Sie Beobachter hinzu, die bemerken, wenn sich das Wort oder die Punktzahl ändert.
- Aktualisieren Sie die Textansichten, in denen geänderte Werte angezeigt werden.
- Verwende das
LiveData
-Beobachtermuster, um ein Ereignis für das Ende eines Spiels hinzuzufügen. - Implementieren Sie die Schaltfläche Nochmal spielen.
In den Codelabs zu Lektion 5 entwickeln Sie die App „GuessTheWord“ auf Grundlage von Startcode. GuessTheWord ist ein Schattenspiel für zwei Spieler, bei dem die Spieler zusammenarbeiten, um die höchstmögliche Punktzahl zu erreichen.
Der erste Spieler sieht sich die Wörter in der App an und stellt sie nacheinander dar, ohne dem zweiten Spieler das Wort zu zeigen. Der zweite Spieler versucht, das Wort zu erraten.
Um das Spiel zu starten, öffnet der erste Spieler die App auf dem Gerät und sieht ein Wort, z. B. „Gitarre“, wie im Screenshot unten zu sehen.
Der erste Spieler stellt das Wort dar, ohne es auszusprechen.
- Wenn der zweite Spieler das Wort richtig errät, drückt der erste Spieler auf die Schaltfläche Got It (Erraten). Dadurch wird die Anzahl um eins erhöht und das nächste Wort angezeigt.
- Wenn der zweite Spieler das Wort nicht erraten kann, drückt der erste Spieler die Schaltfläche Überspringen. Dadurch wird die Anzahl um eins verringert und zum nächsten Wort gesprungen.
- Drücken Sie die Schaltfläche Spiel beenden, um das Spiel zu beenden. (Diese Funktion ist nicht im Startercode für das erste Codelab der Reihe enthalten.)
In diesem Codelab verbessern Sie die App „GuessTheWord“, indem Sie ein Ereignis hinzufügen, um das Spiel zu beenden, wenn der Nutzer alle Wörter in der App durchlaufen hat. Außerdem fügen Sie dem Score-Fragment die Schaltfläche Play Again (Nochmal spielen) hinzu, damit der Nutzer das Spiel noch einmal spielen kann.
Titelbildschirm | Spielbildschirm | Bewertungsbildschirm |
In dieser Aufgabe suchen Sie den Startcode für dieses Codelab und führen ihn aus. Sie können die GuessTheWord-App, die Sie im vorherigen Codelab erstellt haben, als Startcode verwenden oder eine Starter-App herunterladen.
- (Optional) Wenn Sie den Code aus dem vorherigen Codelab nicht verwenden, laden Sie den Startcode für dieses Codelab herunter. Entpacken Sie den Code und öffnen Sie das Projekt in Android Studio.
- Führen Sie die App aus und spielen Sie das Spiel.
- Die Schaltfläche Überspringen zeigt das nächste Wort an und verringert die Punktzahl um eins. Die Schaltfläche Verstanden zeigt das nächste Wort an und erhöht die Punktzahl um eins. Mit der Schaltfläche Spiel beenden wird das Spiel beendet.
LiveData
ist eine beobachtbare Datenhalterklasse, die den Lebenszyklus berücksichtigt. Sie können beispielsweise ein LiveData
um die aktuelle Punktzahl in der App „GuessTheWord“ legen. In diesem Codelab erfahren Sie mehr über verschiedene Eigenschaften von LiveData
:
LiveData
ist beobachtbar. Das bedeutet, dass ein Beobachter benachrichtigt wird, wenn sich die Daten desLiveData
-Objekts ändern.LiveData
enthält Daten;LiveData
ist ein Wrapper, der mit beliebigen Daten verwendet werden kann.LiveData
ist lebenszyklusbezogen. Das bedeutet, dass nur Beobachter aktualisiert werden, die sich in einem aktiven Lebenszyklusstatus wieSTARTED
oderRESUMED
befinden.
In dieser Aufgabe erfahren Sie, wie Sie einen beliebigen Datentyp in LiveData
-Objekte einfügen, indem Sie die aktuellen Punkt- und Wortdaten in GameViewModel
in LiveData
konvertieren. In einer späteren Aufgabe fügen Sie diesen LiveData
-Objekten einen Observer hinzu und erfahren, wie Sie die LiveData
beobachten.
Schritt 1: Den Score und das Wort ändern, um LiveData zu verwenden
- Öffnen Sie im Paket
screens/game
die DateiGameViewModel
. - Ändern Sie den Typ der Variablen
score
undword
inMutableLiveData
.MutableLiveData
ist eineLiveData
, deren Wert geändert werden kann.MutableLiveData
ist eine generische Klasse. Sie müssen also den Datentyp angeben, den sie enthält.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
- Initialisieren Sie in
GameViewModel
im Blockinit
die Variablenscore
undword
. Wenn Sie den Wert einerLiveData
-Variablen ändern möchten, verwenden Sie die MethodesetValue()
für die Variable. In Kotlin können SiesetValue()
mit der Propertyvalue
aufrufen.
init {
word.value = ""
score.value = 0
...
}
Schritt 2: LiveData-Objektreferenz aktualisieren
Die Variablen score
und word
sind jetzt vom Typ LiveData
. In diesem Schritt ändern Sie die Verweise auf diese Variablen mithilfe der Eigenschaft value
.
- Ändern Sie in
GameViewModel
in der MethodeonSkip()
score
inscore.value
. Beachten Sie den Fehler, dassscore
möglicherweisenull
ist. Sie beheben diesen Fehler als Nächstes. - Fügen Sie in
onSkip()
einennull
-Check zuscore.value
hinzu, um den Fehler zu beheben. Rufen Sie dann die Funktionminus()
fürscore
auf, um die Subtraktion mitnull
-Sicherheit auszuführen.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}
- Aktualisieren Sie die Methode
onCorrect()
auf dieselbe Weise: Fügen Sie der Variablenscore
einenull
-Prüfung hinzu und verwenden Sie die Funktionplus()
.
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}
- Ändern Sie in
GameViewModel
in der MethodenextWord()
den Verweisword
inword
.
value
.
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}
- Ändern Sie in
GameFragment
in der MethodeupdateWordText()
den Verweis aufviewModel
.word
inviewModel
.
word
.
value.
.
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}
- Ändern Sie in
GameFragment
in der MethodeupdateScoreText()
den Verweis aufviewModel
.score
inviewModel
.
score
.
value.
.
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}
- Ändern Sie in
GameFragment
in der MethodegameFinished()
den Verweis aufviewModel
.score
inviewModel
.
score
.
value
. Fügen Sie den erforderlichennull
-Sicherheitscheck hinzu.
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)
}
- Achten Sie darauf, dass Ihr Code keine Fehler enthält. Kompilieren und führen Sie Ihre App aus. Die Funktionalität der App sollte dieselbe sein wie zuvor.
Diese Aufgabe hängt eng mit der vorherigen Aufgabe zusammen, in der Sie die Punktzahl- und Wortdaten in LiveData
-Objekte konvertiert haben. In dieser Aufgabe hängen Sie Observer
-Objekte an diese LiveData
-Objekte an.
- Hängen Sie in
GameFragment,
in der MethodeonCreateView()
einObserver
-Objekt an dasLiveData
-Objekt für die aktuelle Punktzahl,viewModel.score
, an. Verwenden Sie die Methodeobserve()
und fügen Sie den Code nach der Initialisierung vonviewModel
ein. Verwenden Sie einen Lambda-Ausdruck, um den Code zu vereinfachen. Ein Lambda-Ausdruck ist eine anonyme Funktion, die nicht deklariert, sondern sofort als Ausdruck übergeben wird.
viewModel.score.observe(this, Observer { newScore ->
})
Lösen Sie den Verweis auf Observer
auf. Klicken Sie dazu auf Observer
, drücken Sie Alt+Enter
(Option+Enter
auf einem Mac) und importieren Sie androidx.lifecycle.Observer
.
- Der gerade erstellte Observer empfängt ein Ereignis, wenn sich die Daten des beobachteten
LiveData
-Objekts ändern. Aktualisieren Sie im Observer den Wert fürTextView
mit dem neuen Wert.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Hängen Sie ein
Observer
-Objekt an das aktuelleLiveData
-Objekt an. Gehen Sie dabei genauso vor wie beim Anhängen einesObserver
-Objekts an den aktuellen Score.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
Wenn sich der Wert von score
oder word
ändert, wird der auf dem Bildschirm angezeigte Wert von score
oder word
jetzt automatisch aktualisiert.
- Löschen Sie in
GameFragment
die MethodenupdateWordText()
undupdateScoreText()
sowie alle Verweise darauf. Sie werden nicht mehr benötigt, da die Textansichten durch dieLiveData
-Beobachtermethoden aktualisiert werden. - Führen Sie Ihre App aus. Ihre Spiele-App sollte genau wie zuvor funktionieren, verwendet jetzt aber
LiveData
undLiveData
-Beobachter.
Kapselung ist eine Möglichkeit, den direkten Zugriff auf einige Felder eines Objekts einzuschränken. Wenn Sie ein Objekt kapseln, stellen Sie eine Reihe öffentlicher Methoden bereit, mit denen die privaten internen Felder geändert werden können. Durch die Kapselung steuern Sie, wie andere Klassen diese internen Felder bearbeiten.
In Ihrem aktuellen Code kann jede externe Klasse die Variablen score
und word
mit der Eigenschaft value
ändern, z. B. mit viewModel.score.value
. In der App, die Sie in diesem Codelab entwickeln, spielt das möglicherweise keine Rolle. In einer Produktions-App sollten Sie jedoch die Daten in den ViewModel
-Objekten kontrollieren.
Nur die ViewModel
sollte die Daten in Ihrer App bearbeiten. UI-Controller müssen die Daten jedoch lesen können, daher können die Datenfelder nicht vollständig privat sein. Zum Kapseln der Daten Ihrer App verwenden Sie sowohl MutableLiveData
- als auch LiveData
-Objekte.
MutableLiveData
im Vergleich zu LiveData
:
- Daten in einem
MutableLiveData
-Objekt können geändert werden, wie der Name schon sagt. Innerhalb vonViewModel
sollten die Daten bearbeitbar sein. Daher wirdMutableLiveData
verwendet. - Daten in einem
LiveData
-Objekt können gelesen, aber nicht geändert werden. Außerhalb vonViewModel
sollten Daten lesbar, aber nicht bearbeitbar sein. Daher sollten die Daten alsLiveData
verfügbar gemacht werden.
Für diese Strategie verwenden Sie eine Backing Property in Kotlin. Mit einer Backing Property können Sie etwas anderes als das genaue Objekt aus einem Getter zurückgeben. In dieser Aufgabe implementieren Sie eine Sicherungseigenschaft für die Objekte score
und word
in der App „GuessTheWord“.
Dem Ergebnis und dem Wort eine unterstützende Property hinzufügen
- Machen Sie in
GameViewModel
das aktuellescore
-Objekt zuprivate
. - Wenn Sie die in den zugrunde liegenden Attributen verwendete Namenskonvention verwenden möchten, ändern Sie
score
in_score
. Die Property_score
ist jetzt die veränderbare Version der Spielpunktzahl, die intern verwendet werden soll. - Erstellen Sie eine öffentliche Version des Typs
LiveData
mit dem Namenscore
.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
- Es wird ein Initialisierungsfehler angezeigt. Dieser Fehler tritt auf, weil
score
inGameFragment
einLiveData
-Verweis ist undscore
nicht mehr auf seinen Setter zugreifen kann. Weitere Informationen zu Gettern und Settern in Kotlin finden Sie unter Getter und Setter.
Um den Fehler zu beheben, überschreiben Sie die Methodeget()
für dasscore
-Objekt inGameViewModel
und geben Sie die Sicherungseigenschaft_score
zurück.
val score: LiveData<Int>
get() = _score
- Ändern Sie im
GameViewModel
die Verweise vonscore
in die interne veränderliche Version_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)
}
...
}
- Benennen Sie das
word
-Objekt in_word
um und fügen Sie eine Backing-Property dafür hinzu, wie Sie es für dasscore
-Objekt getan haben.
// 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)
}
}
Gut gemacht! Sie haben die LiveData
-Objekte word
und score
gekapselt.
In Ihrer aktuellen App wird der Nutzer zum Bildschirm mit dem Ergebnis weitergeleitet, wenn er auf die Schaltfläche Spiel beenden tippt. Außerdem soll die App zum Bildschirm mit der Punktzahl wechseln, wenn die Spieler alle Wörter durchlaufen haben. Nachdem die Spieler das letzte Wort eingegeben haben, soll das Spiel automatisch beendet werden, damit der Nutzer nicht auf die Schaltfläche tippen muss.
Um diese Funktion zu implementieren, muss ein Ereignis ausgelöst und an das Fragment von ViewModel
gesendet werden, wenn alle Wörter angezeigt wurden. Dazu verwenden Sie das LiveData
-Beobachtermuster, um ein Ereignis für das Ende des Spiels zu modellieren.
Das Observer-Muster
Das Beobachtermuster ist ein Software-Designmuster. Es wird die Kommunikation zwischen Objekten festgelegt: einem Observable (dem „Subjekt“ der Beobachtung) und Observers. Ein Observable ist ein Objekt, das Beobachter über Änderungen in seinem Status informiert.
Im Fall von LiveData
in dieser App ist das Observable (Subjekt) das LiveData
-Objekt und die Beobachter sind die Methoden in den UI-Controllern, z. B. Fragmente. Eine Statusänderung erfolgt immer dann, wenn sich die in LiveData
enthaltenen Daten ändern. Die LiveData
-Klassen sind entscheidend für die Kommunikation vom ViewModel
zum Fragment.
Schritt 1: LiveData verwenden, um ein Ereignis für das Ende eines Spiels zu erkennen
In dieser Aufgabe verwenden Sie das LiveData
-Beobachtermuster, um ein Ereignis zu modellieren, das ausgelöst wird, wenn ein Spiel beendet ist.
- Erstellen Sie in
GameViewModel
einBoolean
-MutableLiveData
-Objekt mit dem Namen_eventGameFinish
. Dieses Objekt enthält das Ereignis „Spiel beendet“. - Erstellen und initialisieren Sie nach der Initialisierung des
_eventGameFinish
-Objekts eine Sicherungsproperty namenseventGameFinish
.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish
- Fügen Sie in
GameViewModel
eineonGameFinish()
-Methode hinzu. Legen Sie in der Methode das Ereignis „Spiel beendet“ (eventGameFinish
) auftrue
fest.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}
- Beende das Spiel in
GameViewModel
in der MethodenextWord()
, wenn die Wortliste leer ist.
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}
- Hängen Sie in
GameFragment
innerhalb vononCreateView()
nach der Initialisierung vonviewModel
einen Observer aneventGameFinish
an. Verwenden Sie die Methodeobserve()
. Rufen Sie in der Lambda-Funktion die MethodegameFinished()
auf.
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
if (hasFinished) gameFinished()
})
- Führen Sie Ihre App aus, spielen Sie das Spiel und gehen Sie alle Wörter durch. Die App wechselt automatisch zum Bildschirm mit dem Ergebnis, anstatt im Spiel-Fragment zu bleiben, bis Sie auf Spiel beenden tippen.
Wenn die Wortliste leer ist, wirdeventGameFinish
festgelegt, die zugehörige Beobachtermethode im Game-Fragment wird aufgerufen und die App wechselt zum Bildschirm-Fragment. - Der von Ihnen hinzugefügte Code hat ein Problem mit dem Lebenszyklus verursacht. Um das Problem zu verstehen, kommentieren Sie in der Klasse
GameFragment
den Navigationscode in der MethodegameFinished()
aus. Achten Sie darauf, dieToast
-Nachricht in der Methode beizubehalten.
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)
}
- Führen Sie Ihre App aus, spielen Sie das Spiel und gehen Sie alle Wörter durch. Unten auf dem Spielbildschirm wird kurz die Toast-Meldung „Das Spiel ist gerade beendet“ angezeigt. Das ist das erwartete Verhalten.
Drehen Sie das Gerät oder den Emulator. Der Hinweis wird wieder angezeigt. Drehen Sie das Gerät noch einige Male. Wahrscheinlich wird der Toast jedes Mal angezeigt. Das ist ein Fehler, da der Hinweis nur einmal angezeigt werden sollte, wenn das Spiel beendet ist. Der Toast sollte nicht jedes Mal angezeigt werden, wenn das Fragment neu erstellt wird. Sie beheben dieses Problem in der nächsten Aufgabe.
Schritt 2: Ereignis „Spiel beendet“ zurücksetzen
Normalerweise werden Aktualisierungen nur dann an die Beobachter gesendet, wenn sich Daten ändern.LiveData
Eine Ausnahme von diesem Verhalten besteht darin, dass Beobachter auch dann Updates erhalten, wenn sich der Beobachter von einem inaktiven in einen aktiven Status ändert.
Aus diesem Grund wird der Toast „Spiel beendet“ in Ihrer App wiederholt ausgelöst. Wenn das Game-Fragment nach einer Bildschirmdrehung neu erstellt wird, wechselt es von einem inaktiven in einen aktiven Zustand. Der Observer im Fragment wird wieder mit dem vorhandenen ViewModel
verbunden und empfängt die aktuellen Daten. Die Methode gameFinished()
wird noch einmal ausgelöst und der Toast wird angezeigt.
In dieser Aufgabe beheben Sie dieses Problem und lassen den Toast nur einmal anzeigen, indem Sie das Flag eventGameFinish
in GameViewModel
zurücksetzen.
- Fügen Sie in
GameViewModel
eineonGameFinishComplete()
-Methode zum Zurücksetzen des Ereignisses „Spiel beendet“ (_eventGameFinish
) hinzu.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- Rufen Sie in
GameFragment
am Ende vongameFinished()
onGameFinishComplete()
für dasviewModel
-Objekt auf. Lassen Sie den Navigationscode ingameFinished()
vorerst auskommentiert.
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
- Führen Sie die App aus und spielen Sie das Spiel. Gehen Sie alle Wörter durch und ändern Sie dann die Bildschirmausrichtung des Geräts. Der Hinweis wird nur einmal angezeigt.
- Entfernen Sie in
GameFragment
innerhalb der MethodegameFinished()
die Auskommentierung des Navigationscodes.
Wählen Sie in Android Studio die auskommentierten Zeilen aus und drücken SieControl+/
(Command+/
auf einem Mac), um die Auskommentierung zu entfernen.
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()
}
Wenn Sie von Android Studio dazu aufgefordert werden, importieren Sie androidx.navigation.fragment.NavHostFragment.findNavController
.
- Führen Sie die App aus und spielen Sie das Spiel. Die App muss nach dem Durchgehen aller Wörter automatisch zum Bildschirm mit dem Endergebnis wechseln.
Gut gemacht! Ihre App verwendet LiveData
, um ein Ereignis zum Spielende auszulösen und dem Spiel-Fragment mitzuteilen, dass die Wortliste leer ist.GameViewModel
Das Spielfragment wechselt dann zum Ergebnisfragment.
In dieser Aufgabe ändern Sie den Wert in ein LiveData
-Objekt in ScoreViewModel
und hängen einen Observer daran an. Diese Aufgabe ähnelt der, die Sie beim Hinzufügen von LiveData
zur GameViewModel
ausgeführt haben.
Sie nehmen diese Änderungen an ScoreViewModel
vor, damit alle Daten in Ihrer App LiveData
verwenden.
- Ändern Sie in
ScoreViewModel
den Variablentypscore
inMutableLiveData
. Benennen Sie sie gemäß der Konvention in_score
um und fügen Sie ein Backing-Attribut hinzu.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
- Initialisieren Sie
_score
inScoreViewModel
innerhalb desinit
-Blocks. Sie können das Log nach Belieben aus deminit
-Block entfernen oder dort belassen.
init {
_score.value = finalScore
}
- Hängen Sie in
ScoreFragment
innerhalb vononCreateView()
nach der Initialisierung vonviewModel
einen Observer für das Score-ObjektLiveData
an. Legen Sie im Lambda-Ausdruck den Punktwert auf die Textansicht für die Punktzahl fest. Entfernen Sie den Code, mit dem der Textansicht der Punktwert ausViewModel
direkt zugewiesen wird.
Code zum Hinzufügen:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
Zu entfernender Code:
binding.scoreText.text = viewModel.score.toString()
Importieren Sie androidx.lifecycle.Observer
, wenn Sie von Android Studio dazu aufgefordert werden.
- Führen Sie die App aus und spielen Sie das Spiel. Die App sollte wie zuvor funktionieren, verwendet jetzt aber
LiveData
und einen Observer, um den Score zu aktualisieren.
In dieser Aufgabe fügen Sie dem Ergebnisbildschirm die Schaltfläche Nochmal spielen hinzu und implementieren den Klick-Listener mit einem LiveData
-Ereignis. Die Schaltfläche löst ein Ereignis aus, um vom Ergebnisbildschirm zum Spielbildschirm zu wechseln.
Der Startcode für die App enthält die Schaltfläche Nochmal spielen, die jedoch ausgeblendet ist.
- Ändern Sie in
res/layout/score_fragment.xml
für die Schaltflächeplay_again_button
den Wert des Attributsvisibility
invisible
.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>
- Fügen Sie in
ScoreViewModel
einLiveData
-Objekt ein, das einenBoolean
namens_eventPlayAgain
enthält. Mit diesem Objekt wird dasLiveData
-Ereignis gespeichert, um vom Ergebnisbildschirm zum Spielbildschirm zu wechseln.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain
- Definieren Sie in
ScoreViewModel
Methoden zum Festlegen und Zurücksetzen des Ereignisses,_eventPlayAgain
.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}
- Fügen Sie in
ScoreFragment
einen Observer füreventPlayAgain
hinzu. Setzen Sie den Code ans Ende vononCreateView()
, vor diereturn
-Anweisung. Gehen Sie im Lambda-Ausdruck zurück zum Spielbildschirm und setzen SieeventPlayAgain
zurück.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})
Importieren Sie androidx.navigation.fragment.findNavController
, wenn Sie von Android Studio dazu aufgefordert werden.
- Fügen Sie in
ScoreFragment
innerhalb vononCreateView()
einen Klick-Listener für die Schaltfläche PlayAgain hinzu und rufen SieviewModel
.onPlayAgain()
auf.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Führen Sie die App aus und spielen Sie das Spiel. Wenn das Spiel beendet ist, wird auf dem Bildschirm mit dem Ergebnis die Endpunktzahl und die Schaltfläche Nochmal spielen angezeigt. Tippen Sie auf die Schaltfläche PlayAgain (Nochmal spielen). Die App leitet Sie dann zum Spielbildschirm weiter, damit Sie das Spiel noch einmal spielen können.
Gute Arbeit! Sie haben die Architektur Ihrer App so geändert, dass LiveData
-Objekte im ViewModel
verwendet werden, und Sie haben Beobachter an die LiveData
-Objekte angehängt. LiveData
benachrichtigt Beobachterobjekte, wenn sich der von LiveData
gehaltene Wert ändert.
Android Studio-Projekt: GuessTheWord
LiveData
LiveData
ist eine beobachtbare Datenhalterklasse, die den Lebenszyklus berücksichtigt und zu den Android-Architekturkomponenten gehört.- Mit
LiveData
können Sie dafür sorgen, dass die Benutzeroberfläche automatisch aktualisiert wird, wenn sich die Daten ändern. LiveData
ist beobachtbar. Das bedeutet, dass ein Beobachter wie eine Aktivität oder ein Fragment benachrichtigt werden kann, wenn sich die Daten imLiveData
-Objekt ändern.LiveData
enthält Daten. Es ist ein Wrapper, der mit beliebigen Daten verwendet werden kann.LiveData
ist lebenszyklusbezogen. Das bedeutet, dass nur Beobachter aktualisiert werden, die sich in einem aktiven Lebenszyklusstatus wieSTARTED
oderRESUMED
befinden.
LiveData hinzufügen
- Ändern Sie den Typ der Datenvariablen in
ViewModel
inLiveData
oderMutableLiveData
.
MutableLiveData
ist ein LiveData
-Objekt, dessen Wert geändert werden kann. MutableLiveData
ist eine generische Klasse. Sie müssen also den Datentyp angeben, den sie enthält.
- Wenn Sie den Wert der Daten ändern möchten, die von
LiveData
enthalten sind, verwenden Sie die MethodesetValue()
für die VariableLiveData
.
LiveData kapseln
- Die
LiveData
inViewModel
sollte bearbeitbar sein. Außerhalb vonViewModel
sollteLiveData
lesbar sein. Dies kann mit einem Backing Property in Kotlin implementiert werden. - Mit einer Kotlin-Backing-Property können Sie in einem Getter etwas anderes als das genaue Objekt zurückgeben.
- Um
LiveData
zu kapseln, verwenden Sieprivate
MutableLiveData
innerhalb vonViewModel
und geben Sie außerhalb vonViewModel
eineLiveData
-Backing-Property zurück.
Observable LiveData
LiveData
folgt einem Observer-Muster. Das „beobachtbare“ Objekt ist dasLiveData
-Objekt und die Beobachter sind die Methoden in den UI-Controllern, z. B. Fragmente. Immer wenn sich die inLiveData
enthaltenen Daten ändern, werden die Observer-Methoden in den UI-Controllern benachrichtigt.- Damit das
LiveData
beobachtbar ist, hängen Sie mit der Methodeobserve()
ein Beobachterobjekt an dieLiveData
-Referenz in den Beobachtern (z. B. Aktivitäten und Fragmenten) an. - Dieses
LiveData
-Beobachtermuster kann für die Kommunikation vomViewModel
zu den UI-Controllern verwendet werden.
Udacity-Kurs:
Android-Entwicklerdokumentation:
Sonstiges:
- Backing Property in Kotlin
In diesem Abschnitt werden mögliche Hausaufgaben für Schüler und Studenten aufgeführt, die dieses Codelab im Rahmen eines von einem Kursleiter geleiteten Kurses durcharbeiten. Es liegt in der Verantwortung des Kursleiters, Folgendes zu tun:
- Weisen Sie bei Bedarf Aufgaben zu.
- Teilen Sie den Schülern/Studenten mit, wie sie Hausaufgaben abgeben können.
- Benoten Sie die Hausaufgaben.
Lehrkräfte können diese Vorschläge nach Belieben nutzen und auch andere Hausaufgaben zuweisen, die sie für angemessen halten.
Wenn Sie dieses Codelab selbst durcharbeiten, können Sie mit diesen Hausaufgaben Ihr Wissen testen.
Beantworten Sie diese Fragen
Frage 1
Wie kapseln Sie die LiveData
, die in einem ViewModel
gespeichert sind, sodass externe Objekte Daten lesen können, ohne sie aktualisieren zu können?
- Ändern Sie im
ViewModel
-Objekt den Datentyp der Daten inprivate
LiveData
. Verwenden Sie eine Backing Property, um schreibgeschützte Daten vom TypMutableLiveData
verfügbar zu machen. - Ändern Sie im
ViewModel
-Objekt den Datentyp der Daten inprivate
MutableLiveData
. Verwenden Sie eine Backing Property, um schreibgeschützte Daten vom TypLiveData
verfügbar zu machen. - Ändern Sie im UI-Controller den Datentyp der Daten in
private
MutableLiveData
. Verwenden Sie eine Backing Property, um schreibgeschützte Daten vom TypLiveData
verfügbar zu machen. - Ändern Sie im
ViewModel
-Objekt den Datentyp der Daten inLiveData
. Verwenden Sie eine Backing Property, um schreibgeschützte Daten vom TypLiveData
verfügbar zu machen.
Frage 2
LiveData
aktualisiert einen UI-Controller (z. B. ein Fragment), wenn sich der UI-Controller in einem der folgenden Status befindet:
- Fortgesetzt
- Im Hintergrund
- Pausiert
- Angehalten
Frage 3
Was ist im LiveData
-Beobachtermuster das beobachtbare Element (was wird beobachtet)?
- Die Beobachtermethode
- Die Daten in einem
LiveData
-Objekt - Der UI-Controller
- Das
ViewModel
-Objekt
Links zu anderen Codelabs in diesem Kurs finden Sie auf der Landingpage für Android Kotlin Fundamentals-Codelabs.