Questo codelab fa parte del corso Android Kotlin Fundamentals. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire le codelab in sequenza. Tutti i codelab del corso sono elencati nella pagina di destinazione dei codelab Android Kotlin Fundamentals.
Introduzione
Nei codelab precedenti di questa lezione, hai migliorato il codice dell'app GuessTheWord. L'app ora utilizza oggetti ViewModel
, quindi i dati dell'app sopravvivono alle modifiche alla configurazione del dispositivo, come rotazioni dello schermo e modifiche alla disponibilità della tastiera. Hai anche aggiunto l'oggetto osservabile LiveData
, in modo che le visualizzazioni vengano notificate automaticamente quando i dati osservati cambiano.
In questo codelab, continui a lavorare con l'app GuessTheWord. Colleghi le visualizzazioni alle classi ViewModel
nell'app in modo che le visualizzazioni nel layout comunichino direttamente con gli oggetti ViewModel
. (Finora nella tua app, le visualizzazioni hanno comunicato indirettamente con ViewModel
tramite i fragment dell'app.) Dopo aver integrato il binding dei dati con gli oggetti ViewModel
, non hai più bisogno dei gestori dei clic nei fragment dell'app, quindi li rimuovi.
Inoltre, modifichi l'app GuessTheWord in modo che utilizzi LiveData
come origine del data binding per comunicare all'interfaccia utente le modifiche ai dati, senza utilizzare i metodi di osservazione LiveData
.
Cosa devi già sapere
- Come creare app per Android di base in Kotlin.
- Come funzionano i cicli di vita di attività e frammenti.
- Come utilizzare gli oggetti
ViewModel
nella tua app. - Come archiviare i dati utilizzando
LiveData
in unViewModel
. - Come aggiungere metodi di osservazione per osservare le modifiche nei dati
LiveData
.
Obiettivi didattici
- Come utilizzare gli elementi della libreria Data Binding.
- Come integrare
ViewModel
con il data binding. - Come integrare
LiveData
con il data binding. - Come utilizzare i binding dei listener per sostituire i listener dei clic in un fragmento.
- Come aggiungere la formattazione delle stringhe alle espressioni di data binding.
In questo lab proverai a:
- Le visualizzazioni nei layout di IndovinaParola comunicano indirettamente con gli oggetti
ViewModel
, utilizzando i controller UI (frammenti) per trasmettere le informazioni. In questo codelab, associ le visualizzazioni dell'app agli oggettiViewModel
in modo che le visualizzazioni comunichino direttamente con gli oggettiViewModel
. - Modifica l'app in modo che utilizzi
LiveData
come origine del data binding. Dopo questa modifica, gli oggettiLiveData
notificano alla UI le modifiche ai dati e i metodi di osservazioneLiveData
non sono più necessari.
Nei codelab della lezione 5, sviluppi l'app IndovinaParola, partendo dal codice iniziale. IndovinaParola è un gioco in stile Sarabanda per due giocatori, in cui i giocatori collaborano per ottenere il punteggio più alto possibile.
Il primo giocatore guarda le parole nell'app e le mima a turno, assicurandosi di non mostrarle al secondo giocatore. Il secondo giocatore cerca di 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 recita la parola, facendo attenzione a non dirla.
- Quando il secondo giocatore indovina la parola correttamente, il primo giocatore preme il pulsante Indovinato, 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 Salta, che diminuisce il conteggio di uno e passa alla parola successiva.
- Per terminare la partita, premi il pulsante Termina partita. Questa funzionalità non è presente nel codice iniziale del primo codelab della serie.
In questo codelab, migliorerai l'app GuessTheWord integrando il data binding con LiveData
negli oggetti ViewModel
. In questo modo, la comunicazione tra le visualizzazioni nel layout e gli oggetti ViewModel
viene automatizzata e il codice può essere semplificato utilizzando LiveData
.
Schermata del titolo | Schermata di gioco | Schermata del punteggio |
In questa attività, individua ed esegui il codice iniziale per questo codelab. Puoi utilizzare l'app IndovinaParola che hai creato nel codelab precedente come codice iniziale oppure scaricare un'app iniziale.
- (Facoltativo) Se non utilizzi il codice del codelab precedente, scarica il codice iniziale per questo codelab. Decomprimi il codice e apri il progetto in Android Studio.
- Esegui l'app e gioca.
- Tieni presente che il pulsante Ho capito mostra la parola successiva e aumenta il punteggio di un punto, mentre il pulsante Salta mostra la parola successiva e diminuisce il punteggio di un punto. Il pulsante Termina partita termina la partita.
- Scorri tutte le parole e noterai che l'app passa automaticamente alla schermata del punteggio.
In un codelab precedente, hai utilizzato il data binding come modo type-safe per accedere alle visualizzazioni nell'app GuessTheWord. Ma il vero potere del data binding sta nel fare ciò che suggerisce il nome: collegare i dati direttamente agli oggetti View nella tua app.
Architettura attuale dell'app
Nella tua app, le viste sono definite nel layout XML e i dati per queste viste sono contenuti negli oggetti ViewModel
. Tra ogni visualizzazione e il relativo ViewModel
si trova un controller UI, che funge da relè tra i due.
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 fragmento
GameFragment
chiama il listener di clic corrispondente inGameViewModel
. - Il punteggio viene aggiornato in
GameViewModel
.
La visualizzazione Button
e GameViewModel
non comunicano direttamente, ma hanno bisogno del listener di clic presente in GameFragment
.
ViewModel passato al data binding
Sarebbe più semplice se le visualizzazioni nel layout comunicassero direttamente con i dati negli oggetti ViewModel
, senza fare affidamento sui controller UI come intermediari.
Gli oggetti ViewModel
contengono tutti i dati dell'interfaccia utente nell'app IndovinaParola. Passando gli oggetti ViewModel
nel data binding, puoi automatizzare parte della comunicazione tra le visualizzazioni e gli oggetti ViewModel
.
In questa attività, associa le classi GameViewModel
e ScoreViewModel
ai layout XML corrispondenti. Configuri anche i binding dei listener per gestire gli eventi di clic.
Passaggio 1: aggiungi l'associazione di dati per GameViewModel
In questo passaggio, associa GameViewModel
al file di layout corrispondente, game_fragment.xml
.
- Nel file
game_fragment.xml
, aggiungi una variabile di associazione di dati di tipoGameViewModel
. Se si verificano errori in Android Studio, pulisci e ricompila il progetto.
<layout ...>
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout...
- Nel file
GameFragment
, passaGameViewModel
nel data binding.
Per farlo, assegnaviewModel
alla variabilebinding.gameViewModel
, che hai dichiarato nel passaggio precedente. Inserisci questo codice all'interno dionCreateView()
, dopo l'inizializzazione diviewModel
. Se si verificano errori in Android Studio, pulisci e ricompila 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 i binding dei listener per la gestione degli eventi
I binding dei listener sono espressioni di binding che vengono eseguite quando vengono attivati eventi come onClick()
, onZoomIn()
o onZoomOut()
. I binding dei listener vengono scritti come espressioni lambda.
Il data binding crea un listener e lo imposta sulla visualizzazione. Quando si verifica l'evento ascoltato, il listener valuta l'espressione lambda. I binding dei listener funzionano con il plug-in Android per Gradle versione 2.0 o successive. Per scoprire di più, leggi Layout ed espressioni di binding.
In questo passaggio, sostituisci i listener dei clic in GameFragment
con i binding dei listener nel file game_fragment.xml
.
- In
game_fragment.xml
, aggiungi l'attributoonClick
askip_button
. Definisci un'espressione di binding e chiama il metodoonSkip()
inGameViewModel
. Questa espressione di binding è chiamata binding del listener.
<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 le funzioni chiamate dai listener di clic. Non ti servono 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 il binding dei dati per ScoreViewModel
In questo passaggio, associa ScoreViewModel
al file di layout corrispondente, score_fragment.xml
.
- Nel file
score_fragment.xml
, aggiungi una variabile di binding di tipoScoreViewModel
. Questo passaggio è simile a quello che hai fatto perGameViewModel
sopra.
<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
aplay_again_button
. Definisci un binding del listener e chiama il metodoonPlayAgain()
inScoreViewModel
.
<Button
android:id="@+id/play_again_button"
...
android:onClick="@{() -> scoreViewModel.onPlayAgain()}"
... />
- In
ScoreFragment
, all'interno dionCreateView()
, inizializzaviewModel
. Poi inizializza la variabile di bindingbinding.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 ricompila 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 di clic dei pulsanti inScoreFragment
.
Risoluzione dei problemi relativi ai messaggi di errore del data binding
Quando un'app utilizza il data binding, il processo di compilazione genera classi intermedie utilizzate per il data binding. Un'app può presentare errori che Android Studio non rileva finché non provi a compilarla, quindi non vedi avvisi o codice rosso mentre scrivi il codice. Tuttavia, in fase di compilazione, ricevi errori criptici provenienti dalle classi intermedie generate.
Se ricevi un messaggio di errore criptico:
- Esamina attentamente il messaggio nel riquadro Build di Android Studio. Se vedi una località che termina con
databinding
, si è verificato un errore con il binding dei dati. - Nel file XML di layout, controlla la presenza di errori negli attributi
onClick
che utilizzano il data binding. Cerca la funzione chiamata dall'espressione lambda e assicurati che esista. - Nella sezione
<data>
del codice XML, controlla l'ortografia della variabile di associazione dei dati.
Ad esempio, nota l'errore ortografico nel nome della funzione onCorrect()
nel seguente valore dell'attributo:
android:onClick="@{() -> gameViewModel.onCorrectx()}"
Tieni presente anche l'errore ortografico 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 come questi finché non compili l'app, dopodiché il compilatore mostra un messaggio di errore come il seguente:
error: cannot find symbol import com.example.android.guesstheword.databinding.GameFragmentBindingImpl" symbol: class GameFragmentBindingImpl location: package com.example.android.guesstheword.databinding
Il data binding funziona bene con LiveData
utilizzato con gli oggetti ViewModel
. Ora che hai aggiunto l'associazione dei dati agli oggetti ViewModel
, puoi incorporare LiveData
.
In questa attività, modifichi l'app GuessTheWord in modo che utilizzi LiveData
come origine del data binding per comunicare all'interfaccia utente le modifiche ai dati, senza utilizzare i metodi di osservazione LiveData
.
Passaggio 1: aggiungi la parola LiveData al file game_fragment.xml
In questo passaggio, associ la visualizzazione del testo della parola 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 binding gameViewModel
.
<TextView
android:id="@+id/word_text"
...
android:text="@{gameViewModel.word}"
... />
Tieni presente che non devi utilizzare word.value
. In alternativa, puoi utilizzare l'oggetto LiveData
effettivo. L'oggetto LiveData
mostra il valore corrente di word
. Se il valore di word
è null, l'oggetto LiveData
visualizza una stringa vuota.
- In
GameFragment
, inonCreateView()
, dopo aver inizializzatogameViewModel
, imposta l'attività corrente come proprietario del ciclo di vita della variabilebinding
. Definisce l'ambito dell'oggettoLiveData
riportato sopra, consentendo all'oggetto di aggiornare automaticamente le visualizzazioni 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 perLiveData
word
.
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 observer nel controller UI.
Passaggio 2: aggiungi LiveData del punteggio al file score_fragment.xml
In questo passaggio, colleghi LiveData
score
alla visualizzazione del testo del punteggio nel frammento del punteggio.
- In
score_fragment.xml
, aggiungi l'attributoandroid:text
alla visualizzazione del testo della partitura. 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 inizializzatoscoreViewModel
, 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. Nota che il punteggio nel frammento del punteggio viene visualizzato correttamente, senza un osservatore.
Passaggio 3: aggiungi la formattazione delle stringhe con l'associazione di dati
Nel layout, puoi aggiungere la formattazione delle stringhe insieme all'associazione di dati. In questa attività, formatti la parola corrente per aggiungere le virgolette. Inoltre, formatti la stringa del punteggio in modo da anteporre Punteggio attuale, come mostrato nell'immagine seguente.
- In
string.xml
, aggiungi le seguenti stringhe, che utilizzerai per formattare le visualizzazioni di testoword
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 che utilizzi la risorsa stringaquote_format
. Passa agameViewModel.word
. In questo modo, la parola corrente viene passata come argomento alla stringa di formattazione.
<TextView
android:id="@+id/word_text"
...
android:text="@{@string/quote_format(gameViewModel.word)}"
... />
- Formatta la visualizzazione del testo
score
in modo simile aword_text
. Ingame_fragment.xml
, aggiungi l'attributotext
alla visualizzazione di testoscore_text
. Utilizza la risorsa stringascore_format
, che accetta 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 dell'observerscore
.
Codice da rimuovere:
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Pulisci, ricompila ed esegui l'app, quindi gioca. Nota che la parola corrente e il punteggio sono formattati nella schermata di gioco.
Complimenti! Hai integrato LiveData
e ViewModel
con il data binding nella tua app. In questo modo, le visualizzazioni nel layout possono comunicare direttamente con ViewModel
, senza utilizzare i gestori dei clic nel fragmento. Hai anche utilizzato gli oggetti LiveData
come origine del binding dei dati per notificare automaticamente all'interfaccia utente le modifiche ai dati, senza i metodi di osservazione LiveData
.
Progetto Android Studio: GuessTheWord
- La libreria Data Binding funziona perfettamente con i componenti dell'architettura Android come
ViewModel
eLiveData
. - I layout della tua app possono essere associati ai dati nei componenti dell'architettura, che ti aiutano già a gestire il ciclo di vita del controller UI e a inviare notifiche sulle modifiche ai dati.
Associazione di dati ViewModel
- Puoi associare un
ViewModel
a un layout utilizzando il data binding. - Gli oggetti
ViewModel
contengono i dati dell'interfaccia utente. Passando gli oggettiViewModel
nel data binding, puoi automatizzare parte della comunicazione tra le visualizzazioni e gli oggettiViewModel
.
Per associare un ViewModel
a un layout:
- Nel file di layout, aggiungi una variabile di data binding di tipo
ViewModel
.
<data>
<variable
name="gameViewModel"
type="com.example.android.guesstheword.screens.game.GameViewModel" />
</data>
- Nel file
GameFragment
, passaGameViewModel
al data binding.
binding.gameViewModel = viewModel
Associazioni di listener
- I binding dei listener sono espressioni di binding nel layout che vengono eseguite quando vengono attivati eventi di clic come
onClick()
. - I binding dei listener vengono scritti come espressioni lambda.
- Utilizzando i binding dei listener, sostituisci i listener di clic nei controller UI con i binding dei listener nel file di layout.
- Il data binding crea un listener e lo imposta sulla visualizzazione.
android:onClick="@{() -> gameViewModel.onSkip()}"
Aggiungere LiveData all'associazione di dati
- Gli oggetti
LiveData
possono essere utilizzati come origine di data binding per comunicare automaticamente all'interfaccia utente le modifiche ai dati. - Puoi associare la visualizzazione direttamente all'oggetto
LiveData
inViewModel
. QuandoLiveData
inViewModel
cambia, le visualizzazioni nel layout possono essere aggiornate automaticamente, senza i metodi observer nei controller UI.
android:text="@{gameViewModel.word}"
- Per far funzionare il binding dei dati
LiveData
, imposta l'attività corrente (il controller UI) come proprietario del ciclo di vita della variabilebinding
nel controller UI.
binding.lifecycleOwner = this
Formattazione delle stringhe con l'associazione di dati
- Utilizzando il data binding, puoi formattare una risorsa stringa con segnaposto come
%s
per le stringhe e%d
per gli interi. - Per aggiornare l'attributo
text
della visualizzazione, passa l'oggettoLiveData
come argomento alla stringa di formattazione.
android:text="@{@string/quote_format(gameViewModel.word)}"
Corso Udacity:
Documentazione per sviluppatori Android:
Questa sezione elenca i possibili compiti a casa per gli studenti che seguono questo codelab nell'ambito di un corso guidato da un insegnante. Spetta all'insegnante:
- Assegna i compiti, se richiesto.
- Comunica agli studenti come inviare i compiti.
- Valuta i compiti a casa.
Gli insegnanti possono utilizzare questi suggerimenti nella misura che ritengono opportuna e sono liberi di assegnare qualsiasi altro compito a casa che ritengono appropriato.
Se stai seguendo questo codelab in autonomia, sentiti libero di utilizzare questi compiti per casa per mettere alla prova le tue conoscenze.
Rispondi a queste domande
Domanda 1
Quale delle seguenti affermazioni non è vera in merito ai binding dei listener?
- I binding dei listener sono espressioni di binding che vengono eseguite quando si verifica un evento.
- I binding dei listener funzionano con tutte le versioni del plug-in Android per Gradle.
- I binding dei listener vengono scritti come espressioni lambda.
- I binding dei listener sono simili ai riferimenti ai metodi, ma ti consentono di eseguire espressioni di data binding arbitrarie.
Domanda 2
Supponiamo che la tua app includa questa risorsa stringa:<string name="generic_name">Hello %s</string>
Quale delle seguenti è la sintassi corretta per formattare la stringa utilizzando l'espressione di associazione dei 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 binding del listener?
- Quando i dati detenuti da
LiveData
vengono modificati - Quando un'attività viene ricreata a seguito di una modifica alla configurazione
- Quando si verifica un evento come
onClick()
- Quando l'attività passa in background
Inizia la lezione successiva:
Per i link ad altri codelab di questo corso, consulta la pagina di destinazione dei codelab di Android Kotlin Fundamentals.