Questo codelab fa parte del corso Android Kotlin Fundamentals. Otterrai il massimo valore da questo corso se lavori in sequenza nei codelab. Tutti i codelab del corso sono elencati nella pagina di destinazione di Android Kotlin Fundamentals.
Introduzione
Nei codelab precedenti di questa lezione hai migliorato il codice per l'app GuessTheWord. Ora l'app utilizza oggetti ViewModel
, quindi i dati dell'app si adeguano alle modifiche della configurazione del dispositivo, come le rotazioni dello schermo e le modifiche relative alla disponibilità della tastiera. Hai anche aggiunto LiveData
osservabile, in modo che le visualizzazioni vengano notificate automaticamente quando vengono modificati i dati osservati.
In questo codelab, continui a lavorare con l'app GuessTheWord. associare le visualizzazioni ai corsi ViewModel
nell'app in modo che le visualizzazioni nel tuo layout comunichino direttamente con gli oggetti ViewModel
. Finora, nell'app le visualizzazioni hanno comunicato indirettamente con ViewModel
, tramite i frammenti dell'app. Dopo aver integrato l'associazione di dati con gli oggetti ViewModel
, non hai più bisogno dei gestori dei clic nei frammenti dell'app, quindi li rimuovi.
Puoi anche modificare l'app GuessTheWord per utilizzare LiveData
come origine di associazione di dati per informare l'interfaccia utente delle modifiche ai dati, senza utilizzare i metodi Osservatore LiveData
.
Informazioni importanti
- Come creare app Android di base in Kotlin.
- Come funzionano i cicli di vita di attività e frammenti.
- Come utilizzare gli oggetti
ViewModel
nell'app. - Come archiviare dati utilizzando
LiveData
in unViewModel
. - Come aggiungere metodi di osservazione per osservare le modifiche nei dati di
LiveData
.
Obiettivi didattici
- Come utilizzare gli elementi della libreria dei dati.
- Come integrare
ViewModel
con l'associazione di dati. - Come integrare
LiveData
con l'associazione di dati. - Come utilizzare le associazioni dei listener per sostituire i listener di clic in un frammento.
- Come aggiungere la formattazione di stringa alle espressioni di associazione di dati.
In questo lab proverai a:
- Le visualizzazioni nei layout di GuessTheWord comunicano indirettamente con oggetti
ViewModel
, utilizzando i controller (frammenti) dell'interfaccia utente per inoltrare le informazioni. In questo codelab, puoi associare le viste dell'app agli oggettiViewModel
in modo che le viste comunichino direttamente con gli oggettiViewModel
. - Se cambi l'app, utilizzerai
LiveData
come origine di associazione di dati. Dopo questa modifica, gli oggettiLiveData
inviano una notifica all'interfaccia utente riguardo alle modifiche dei dati e i metodi degli osservatoriLiveData
non sono più necessari.
Nei codelab della Lezione 5, sviluppi l'app GuessTheWord, iniziando con il codice di avvio. GuessTheWord è un gioco in stile charade per giocatori, in cui i giocatori collaborano per raggiungere il punteggio più alto possibile.
Il primo giocatore controlla le parole nell'app e le reagisce a turno, assicurandoti di non mostrare la parola al secondo giocatore. Il secondo giocatore prova a indovinare la parola.
Per giocare, il primo giocatore apre l'app sul dispositivo e vede una parola, ad esempio "chitarra", come mostrato nello screenshot di seguito.
Il primo giocatore pronuncia la parola, facendo attenzione a non pronunciare la parola stessa.
- Quando il secondo giocatore indovina la parola correttamente, il primo pulsante preme il pulsante OK, che aumenta il conteggio di uno e mostra la parola successiva.
- Se il secondo giocatore non riesce a indovinare la parola, il primo giocatore preme il pulsante Ignora: in questo modo il numero viene ridotto di uno e passa alla parola successiva.
- Per terminare il gioco, premi il pulsante Termina gioco. Questa funzionalità non è presente nel codice di avvio del primo codelab della serie.
In questo codelab, migliorerai l'app GuessTheWord integrando l'associazione di dati con LiveData
negli oggetti ViewModel
. Questa operazione automatizza la comunicazione tra le viste nel layout e gli oggetti ViewModel
e consente di semplificare il codice utilizzando LiveData
.
Schermata del titolo |
Schermata di gioco |
Schermata punteggio |
In questa attività, troverai ed eseguirai il tuo codice di avvio per questo codelab. Puoi usare come codice di avvio l'app GuessTheWord che hai creato nel codelab precedente oppure puoi scaricare un'app iniziale.
- (Facoltativo) Se non utilizzi il codice del codelab precedente, scarica il codice di avvio per questo codelab. Decomprimi il codice e apri il progetto in Android Studio.
- Esegui l'app e gioca.
- Tieni presente che il pulsante OK mostra la parola successiva e aumenta il punteggio di uno, mentre il pulsante Salta mostra la parola successiva e riduce il punteggio di uno. Il pulsante Termina gioco consente di terminare il gioco.
- Scorri tutte le parole e nota che l'app passa automaticamente alla schermata del punteggio.
In un codelab precedente, hai utilizzato l'associazione di dati come metodo sicuro per il tipo per accedere alle viste nell'app GuessTheWord. Tuttavia, il vero vantaggio dell'associazione di dati è l'associazione del nome suggerito: associazione di dati direttamente agli oggetti di visualizzazione nell'app.
Architettura attuale dell'app
Nell'app, le viste sono definite nel layout XML e i relativi dati vengono conservati in oggetti ViewModel
. Tra ogni vista e il corrispondente ViewModel
è presente un controller dell'interfaccia utente, che funge da inoltro tra di loro.
Ad esempio:
- Il pulsante OK è definito come una visualizzazione
Button
nel file di layoutgame_fragment.xml
. - Quando l'utente tocca il pulsante OK, un listener di clic nel frammento
GameFragment
chiama il listener di clic corrispondente inGameViewModel
. - Il punteggio viene aggiornato in
GameViewModel
.
La vista Button
e la comunicazione GameViewModel
non comunicano direttamente, perciò è necessario che l'ascoltatore dei clic sia in GameFragment
.
ViewModel passato nell'associazione di dati
Sarebbe più semplice se le viste nel layout comunicassero direttamente con i dati negli oggetti ViewModel
, senza fare affidamento sui controller dell'interfaccia utente come intermediari.
Gli oggetti ViewModel
contengono tutti i dati dell'interfaccia utente nell'app GuessTheWord. Passando gli oggetti ViewModel
nell'associazione di dati, puoi automatizzare alcune delle comunicazioni tra le viste e gli oggetti ViewModel
.
In questa attività assocerai le classi GameViewModel
e ScoreViewModel
ai layout XML corrispondenti. Hai anche configurato le associazioni di listener per gestire gli eventi di clic.
Passaggio 1: aggiungi l'associazione di dati per GameViewmodel
In questo passaggio dovrai associare GameViewModel
al file di layout corrispondente, game_fragment.xml
.
- Nel file
game_fragment.xml
, aggiungi una variabile di associazione di dati del tipoGameViewModel
. Se sul tuo dispositivo sono presenti errori in Android Studio, pulisci e ricrea il progetto.
<layout ...>
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout...
- Nel file
GameFragment
, passaGameViewModel
nell'associazione di dati.
A tal fine, assegnaviewModel
alla variabilebinding.gameViewModel
dichiarata nel passaggio precedente. Inserisci questo codice all'interno dionCreateView()
, dopo l'inizializzazione diviewModel
. Se sul tuo dispositivo sono presenti errori in Android Studio, pulisci e ricrea il progetto.
// Set the viewmodel for databinding - this allows the bound layout access
// to all the data in the ViewModel
binding.gameViewModel = viewModel
Passaggio 2: utilizza le associazioni degli ascoltatori per la gestione degli eventi
Le associazioni di listener sono espressioni di binding che vengono eseguite quando vengono attivati eventi come onClick()
, onZoomIn()
o onZoomOut()
. Le associazioni di listener sono scritte come espressioni lambda.
L'associazione di dati crea un listener e imposta il listener nella vista. Quando si verifica l'evento, il listener valuta l'espressione lambda. Le associazioni degli ascoltatori funzionano con il plug-in Android per Gradle versione 2.0 o successive. Per saperne di più, consulta Layout ed espressioni di associazione.
In questo passaggio devi sostituire i listener di clic in GameFragment
con le associazioni di listener nel file game_fragment.xml
.
- In
game_fragment.xml
, aggiungi l'attributoonClick
allaskip_button
. Definisci un'espressione di associazione e chiama il metodoonSkip()
inGameViewModel
. Questa espressione di associazione è chiamata associazione di ascolto.
<Button
android:id="@+id/skip_button"
...
android:onClick="@{() -> gameViewModel.onSkip()}"
... />
- Allo stesso modo, associa l'evento di clic di
correct_button
al metodoonCorrect
()
inGameViewModel
.
<Button
android:id="@+id/correct_button"
...
android:onClick="@{() -> gameViewModel.onCorrect()}"
... />
- Associa l'evento di clic di
end_game_button
al metodoonGameFinish
()
inGameViewModel
.
<Button
android:id="@+id/end_game_button"
...
android:onClick="@{() -> gameViewModel.onGameFinish()}"
... />
- In
GameFragment
, rimuovi le istruzioni che impostano i listener di clic e rimuovi le funzioni che i listener di clic chiamano. Non ti serve più.
Codice da rimuovere:
binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
binding.endGameButton.setOnClickListener { onEndGame() }
/** Methods for buttons presses **/
private fun onSkip() {
viewModel.onSkip()
}
private fun onCorrect() {
viewModel.onCorrect()
}
private fun onEndGame() {
gameFinished()
}
Passaggio 3: aggiungi l'associazione di dati per il ScoreViewmodel
In questo passaggio dovrai associare ScoreViewModel
al file di layout corrispondente, score_fragment.xml
.
- Nel file
score_fragment.xml
, aggiungi una variabile di associazione di tipoScoreViewModel
. Questo passaggio è simile a quello che hai effettuato perGameViewModel
.
<layout ...>
<data>
<variable
name="scoreViewModel"
type="com.example.android.guesstheword.screens.score.ScoreViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
- In
score_fragment.xml
, aggiungi l'attributoonClick
allaplay_again_button
. Definisci un'associazione di listener e chiama il metodoonPlayAgain()
inScoreViewModel
.
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- In
ScoreFragment
, all'interno dionCreateView()
, inizializzaviewModel
. Quindi inizializza la variabile di associazionebinding.scoreViewModel
.
viewModel = ...
binding.scoreViewModel = viewModel
- In
ScoreFragment
, rimuovi il codice che imposta il listener di clic perplayAgainButton
. Se Android Studio mostra un errore, pulisci e ricrea il progetto.
Codice da rimuovere:
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Esegui l'app. L'app dovrebbe funzionare come prima, ma ora le visualizzazioni dei pulsanti comunicano direttamente con gli oggetti
ViewModel
. Le visualizzazioni non comunicano più tramite i gestori dei clic sui pulsanti inScoreFragment
.
Risoluzione dei problemi dovuti a messaggi di errore relativi all'associazione di dati
Quando un'app utilizza l'associazione di dati, la procedura di compilazione genera classi intermedie che vengono utilizzate per l'associazione di dati. Un'app può avere errori che Android Studio non rileva finché non provi a compilare l'app, quindi non puoi visualizzare avvisi o codice rosso mentre scrivi il codice. Tuttavia, al momento della compilazione vengono visualizzati errori crittografici provenienti dalle classi intermedie generate.
Se viene visualizzato un messaggio di errore criptico:
- Osserva attentamente il messaggio nel riquadro Build di Android Studio. Se vedi una sede che termina con
databinding
, si è verificato un errore con l'associazione di dati. - Nel file XML di layout, controlla se ci sono errori negli attributi
onClick
che utilizzano l'associazione di dati. Cerca la funzione chiamata dall'espressione lambda e assicurati che esista. - Nella sezione
<data>
del file XML, controlla l'ortografia della variabile di associazione di dati.
Ad esempio, prendi nota dell'errore ortografico nel nome della funzione onCorrect()
nel seguente valore dell'attributo:
android:onClick="@{() -> gameViewModel.onCorrectx()}"
Tieni presente anche l'errore di ortografia di gameViewModel
nella sezione <data>
del file XML:
<data>
<variable
name="gameViewModelx"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
Android Studio non rileva errori di questo tipo finché non compili l'app, quindi il compilatore mostra un messaggio di errore simile al seguente:
error: cannot find symbol import com.example.android.guesstheword.databinding.GameFragmentBindingImpl" symbol: class GameFragmentBindingImpl location: package com.example.android.guesstheword.databinding
L'associazione di dati funziona bene con LiveData
che vengono utilizzati con gli oggetti ViewModel
. Ora che hai aggiunto un'associazione di dati agli oggetti ViewModel
, puoi incorporare LiveData
.
In questa attività, modificherai l'app GuessTheWord in modo che utilizzi LiveData
come origine di associazione di dati per informare l'interfaccia utente delle modifiche ai dati, senza usare i metodi osservatori LiveData
.
Passaggio 1: aggiungi la parola LiveData al file game_fragment.xml
In questo passaggio devi associare la visualizzazione testuale corrente direttamente all'oggetto LiveData
in ViewModel
.
- In
game_fragment.xml
, aggiungi l'attributoandroid:text
alla visualizzazione di testoword_text
.
Impostalo sull'oggetto LiveData
, word
da GameViewModel
, utilizzando la variabile di associazione gameViewModel
.
<TextView
android:id="@+id/word_text"
...
android:text="@{gameViewModel.word}"
... />
Tieni presente che non devi necessariamente utilizzare word.value
. In alternativa, puoi utilizzare l'oggetto LiveData
effettivo. L'oggetto LiveData
mostra il valore corrente dell'elemento word
. Se il valore di word
è null, l'oggetto LiveData
mostra una stringa vuota.
- In
GameFragment
, inonCreateView()
, dopo l'inizializzazione digameViewModel
, imposta l'attività corrente come proprietario del ciclo di vita della variabilebinding
. Definisce l'ambito dell'oggettoLiveData
sopra riportato, consentendo all'oggetto di aggiornare automaticamente le viste nel layout,game_fragment.xml
.
binding.gameViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
- In
GameFragment
, rimuovi l'osservatore perword
diLiveData
.
Codice da rimuovere:
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
- Esegui l'app e gioca. Ora la parola corrente viene aggiornata senza un metodo osservatore nel controller dell'interfaccia utente.
Passaggio 2: aggiungi il punteggio LiveData al file score_fragment.xml
In questo passaggio devi associare LiveData
score
alla visualizzazione del testo del punteggio nel frammento del punteggio.
- In
score_fragment.xml
, aggiungi l'attributoandroid:text
alla visualizzazione testo del punteggio. AssegnascoreViewModel.score
all'attributotext
. Poichéscore
è un numero intero, convertilo in una stringa utilizzandoString.valueOf()
.
<TextView
android:id="@+id/score_text"
...
android:text="@{String.valueOf(scoreViewModel.score)}"
... />
- In
ScoreFragment
, dopo aver inizializzato l'elementoscoreViewModel
, imposta l'attività corrente come proprietario del ciclo di vita della variabilebinding
.
binding.scoreViewModel = ...
// Specify the current activity as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = this
- In
ScoreFragment
, rimuovi l'osservatore per l'oggettoscore
.
Codice da rimuovere:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Esegui l'app e gioca. Tieni presente che il punteggio nel frammento del punteggio viene visualizzato correttamente, senza un osservatore nel frammento del punteggio.
Passaggio 3: aggiungi la formattazione delle stringhe con l'associazione di dati
Nel layout puoi aggiungere la formattazione di stringa insieme all'associazione di dati. In questa attività, formatti la parola corrente per aggiungere le virgolette. Puoi anche formattare la stringa di punteggio in modo che faccia riferimento al Punteggio corrente, come mostrato nell'immagine seguente.
- In
string.xml
, aggiungi le seguenti stringhe che utilizzerai per formattare le visualizzazioni di testo diword
escore
.%s
e%d
sono i segnaposto per la parola corrente e il punteggio corrente.
<string name="quote_format">\"%s\"</string>
<string name="score_format">Current Score: %d</string>
- In
game_fragment.xml
, aggiorna l'attributotext
della visualizzazione di testoword_text
in modo da utilizzare la risorsa stringaquote_format
. SuperagameViewModel.word
. In questo modo la parola corrente viene trasferita come argomento alla stringa di formattazione.
<TextView
android:id="@+id/word_text"
...
android:text="@{@string/quote_format(gameViewModel.word)}"
... />
- Formatta la visualizzazione di testo
score
in modo simile allaword_text
. Ingame_fragment.xml
, aggiungi l'attributotext
alla visualizzazione di testoscore_text
. Utilizza la risorsa stringascore_format
, che include un argomento numerico, rappresentato dal segnaposto%d
. Passa l'oggettoLiveData
,score
, come argomento a questa stringa di formattazione.
<TextView
android:id="@+id/score_text"
...
android:text="@{@string/score_format(gameViewModel.score)}"
... />
- Nella classe
GameFragment
, all'interno del metodoonCreateView()
, rimuovi il codice osservatorescore
.
Codice da rimuovere:
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Pulisci, ricostruisci ed esegui la tua app, quindi gioca. Tieni presente che la parola corrente e il punteggio sono formattati nella schermata del gioco.
Complimenti! Hai integrato LiveData
e ViewModel
con l'associazione di dati nella tua app. In questo modo le viste nel layout possono comunicare direttamente con ViewModel
, senza utilizzare i gestori dei clic nel frammento. Hai anche utilizzato gli oggetti LiveData
come origine di associazione di dati per avvisare automaticamente l'interfaccia utente delle modifiche apportate ai dati, senza i metodi osservatore LiveData
.
Progetto Android Studio: GuessTheWord
- La libreria di associazione di dati funziona perfettamente con componenti di architettura Android come
ViewModel
eLiveData
. - I layout nella tua app possono essere associati ai dati nei componenti dell'architettura. In questo modo puoi già gestire il ciclo di vita del controller UI e ricevere notifiche sulle modifiche ai dati.
Associazione dati ViewModel
- Puoi associare una
ViewModel
a un layout utilizzando un'associazione di dati. - Gli oggetti
ViewModel
contengono i dati dell'interfaccia utente. Passando l'oggettoViewModel
nell'associazione di dati, puoi automatizzare alcune delle comunicazioni tra le viste e gli oggettiViewModel
.
Come associare un ViewModel
a un layout:
- Nel file di layout, aggiungi una variabile di associazione di dati del tipo
ViewModel
.
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
- Nel file
GameFragment
, trasmetti l'elementoGameViewModel
nell'associazione di dati.
binding.gameViewModel = viewModel
Associazioni listener
- Le associazioni di listener sono espressioni di binding nel layout che vengono eseguite quando vengono attivati eventi di clic come
onClick()
. - Le associazioni di listener sono scritte come espressioni lambda.
- L'utilizzo di associazioni di listener comporta la sostituzione dei listener di clic nei controller dell'interfaccia utente con le associazioni di listener nel file di layout.
- L'associazione di dati crea un listener e imposta il listener nella vista.
android:onClick="@{() -> gameViewModel.onSkip()}"
Aggiunta di LiveData all'associazione di dati
- Gli oggetti
LiveData
possono essere utilizzati come origine di associazione di dati per informare automaticamente l'interfaccia utente delle modifiche apportate ai dati. - Puoi associare la vista direttamente all'oggetto
LiveData
inViewModel
. QuandoLiveData
inViewModel
cambia, le visualizzazioni nel layout possono essere aggiornate automaticamente, senza i metodi osservatore nei controller dell'interfaccia utente.
android:text="@{gameViewModel.word}"
- Per far funzionare l'associazione di dati
LiveData
, imposta l'attività corrente (il controller dell'interfaccia utente) come proprietario del ciclo di vita della variabilebinding
nel controller dell'interfaccia utente.
binding.lifecycleOwner = this
Formattazione delle stringhe con associazione di dati
- Utilizzando l'associazione di dati, puoi formattare una risorsa stringa con segnaposto come
%s
per le stringhe e%d
per i numeri interi. - Per aggiornare l'attributo
text
della vista, trasmetti l'oggettoLiveData
come argomento alla stringa di formattazione.
android:text="@{@string/quote_format(gameViewModel.word)}"
Corso Udacity:
Documentazione per gli sviluppatori Android:
In questa sezione sono elencati i possibili compiti per gli studenti che lavorano attraverso questo codelab nell'ambito di un corso tenuto da un insegnante. Spetta all'insegnante fare quanto segue:
- Assegna i compiti, se necessario.
- Comunica agli studenti come inviare compiti.
- Valuta i compiti.
Gli insegnanti possono utilizzare i suggerimenti solo quanto e come vogliono e dovrebbero assegnare i compiti che ritengono appropriati.
Se stai lavorando da solo a questo codelab, puoi utilizzare questi compiti per mettere alla prova le tue conoscenze.
Rispondi a queste domande
Domanda 1
Quale delle seguenti affermazioni sulle associazioni degli ascoltatori non è vera?
- Le associazioni di listener sono espressioni di associazione eseguite quando si verifica un evento.
- Le associazioni degli ascoltatori funzionano con tutte le versioni del plug-in Android per Gradle.
- Le associazioni di listener sono scritte come espressioni lambda.
- Le associazioni di ascoltatori sono simili ai riferimenti ai metodi, ma consentono di eseguire espressioni di associazione di dati arbitrari.
Domanda 2
Supponiamo che la tua app includa questa risorsa di stringa:<string name="generic_name">Hello %s</string>
Qual è la sintassi corretta per formattare la stringa utilizzando l'espressione di associazione di dati?
android:text= "@{@string/generic_name(user.name)}"
android:text= "@{string/generic_name(user.name)}"
android:text= "@{@generic_name(user.name)}"
android:text= "@{@string/generic_name,user.name}"
Domanda 3
Quando viene valutata ed eseguita un'espressione di associazione degli ascoltatori?
- Quando vengono modificati i dati conservati tramite
LiveData
- Quando un'attività viene ricreata da una modifica della configurazione
- Quando si verifica un evento come
onClick()
- Quando l'attività passa in background
Inizia la lezione successiva:
Per i link ad altri codelab in questo corso, consulta la pagina di destinazione di Android Kotlin Fundamentals.