Android Kotlin Fundamentals 02.4: Nozioni di base sul data binding

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 questo corso, hai utilizzato la funzione findViewById() per ottenere riferimenti alle visualizzazioni. Quando la tua app ha gerarchie di visualizzazione complesse, findViewById() è costoso e rallenta l'app, perché Android attraversa la gerarchia di visualizzazione, a partire dalla radice, finché non trova la visualizzazione desiderata. Fortunatamente, esiste un modo migliore.

Per impostare i dati nelle visualizzazioni, hai utilizzato le risorse stringa e impostato i dati dall'attività. Sarebbe più efficiente se la visualizzazione conoscesse i dati. Fortunatamente, anche in questo caso è possibile.

In questo codelab imparerai a utilizzare il data binding per eliminare la necessità di findViewById(). Scoprirai anche come utilizzare il data binding per accedere ai dati direttamente da una visualizzazione.

Cosa devi già sapere

Devi avere familiarità con:

  • Che cos'è un'attività e come configurarla con un layout in onCreate().
  • Creazione di una visualizzazione di testo e impostazione del testo visualizzato.
  • Utilizzo di findViewById() per ottenere un riferimento a una visualizzazione.
  • Creazione e modifica di un layout XML di base per una visualizzazione.

Obiettivi didattici

  • Come utilizzare la libreria Data Binding per eliminare le chiamate inefficienti a findViewById().
  • Come accedere ai dati delle app direttamente da XML.

In questo lab proverai a:

  • Modifica un'app per utilizzare il data binding anziché findViewById() e per accedere ai dati direttamente dai file XML di layout.

In questo codelab, inizierai con l'app AboutMe e la modificherai in modo che utilizzi il data binding. Al termine, l'app avrà lo stesso aspetto.

Ecco cosa fa l'app AboutMe:

  • Quando l'utente apre l'app, vengono visualizzati un nome, un campo per inserire un nickname, un pulsante Fine, un'immagine a forma di stella e un testo scorrevole.
  • L'utente può inserire un nickname e toccare il pulsante Fine. Il campo modificabile e il pulsante vengono sostituiti da una visualizzazione di testo che mostra il nickname inserito.


Puoi utilizzare il codice creato nel codelab precedente oppure scaricare il codice AboutMeDataBinding-Starter da GitHub.

Il codice che hai scritto nei codelab precedenti utilizza la funzione findViewById() per ottenere riferimenti alle visualizzazioni.

Ogni volta che utilizzi findViewById() per cercare una visualizzazione dopo che è stata creata o ricreata, il sistema Android attraversa la gerarchia delle visualizzazioni in fase di runtime per trovarla. Quando la tua app ha solo poche visualizzazioni, non è un problema. Tuttavia, le app di produzione possono avere decine di visualizzazioni in un layout e, anche con il miglior design, ci saranno visualizzazioni nidificate.

Pensa a un layout lineare che contiene una visualizzazione scorrevole che contiene una visualizzazione di testo. Per una gerarchia di visualizzazione ampia o profonda, trovare una visualizzazione può richiedere un tempo sufficiente a rallentare notevolmente l'app per l'utente. La memorizzazione nella cache delle visualizzazioni nelle variabili può essere utile, ma devi comunque inizializzare una variabile per ogni visualizzazione, in ogni spazio dei nomi. Con molte visualizzazioni e più attività, anche questo contribuisce.

Una soluzione è creare un oggetto che contenga un riferimento a ogni vista. Questo oggetto, chiamato oggetto Binding, può essere utilizzato da tutta l'app. Questa tecnica è chiamata data binding. Una volta creato un oggetto di binding per la tua app, puoi accedere alle visualizzazioni e ad altri dati tramite l'oggetto di binding, senza dover attraversare la gerarchia delle visualizzazioni o cercare i dati.

L'associazione di dati offre i seguenti vantaggi:

  • Il codice è più breve, più facile da leggere e da gestire rispetto al codice che utilizza findByView().
  • Dati e visualizzazioni sono chiaramente separati. Questo vantaggio del data binding diventa sempre più importante più avanti nel corso.
  • Il sistema Android attraversa la gerarchia delle visualizzazioni una sola volta per ottenere ogni visualizzazione e ciò avviene all'avvio dell'app, non in fase di runtime quando l'utente interagisce con l'app.
  • Ottieni la sicurezza dei tipi per l'accesso alle visualizzazioni. (La sicurezza dei tipi indica che il compilatore convalida i tipi durante la compilazione e genera un errore se tenti di assegnare il tipo errato a una variabile.)

In questa attività, configurerai il data binding e lo utilizzerai per sostituire le chiamate a findViewById() con chiamate all'oggetto di binding.

Passaggio 1: attiva il data binding

Per utilizzare il data binding, devi abilitarlo nel file Gradle, in quanto non è abilitato per impostazione predefinita. Questo perché il data binding aumenta il tempo di compilazione e può influire sul tempo di avvio dell'app.

  1. Se non hai l'app AboutMe di un precedente codelab, scarica il codice AboutMeDataBinding-Starter da GitHub. Apri il file in Android Studio.
  2. Apri il file build.gradle (Module: app).
  3. All'interno della sezione android, prima della parentesi graffa chiusa, aggiungi una sezione dataBinding e imposta enabled su true.
dataBinding {
    enabled = true
}
  1. Quando richiesto, sincronizza il progetto. Se non ti viene richiesto, seleziona File > Sincronizza progetto con file Gradle.
  2. Puoi eseguire l'app, ma non vedrai alcuna modifica.

Passaggio 2: modifica il file di layout in modo che sia utilizzabile con il data binding

Per utilizzare il data binding, devi racchiudere il layout XML con un tag <layout>. In questo modo, la classe radice non è più un gruppo di visualizzazioni, ma un layout che contiene gruppi di visualizzazioni e visualizzazioni. L'oggetto di binding può quindi conoscere il layout e le relative visualizzazioni.

  1. Apri il file activity_main.xml.
  2. Passa alla scheda Testo.
  3. Aggiungi <layout></layout> come tag più esterno intorno a <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Scegli Code > Reformat code (Codice > Riformatta codice) per correggere il rientro del codice.

    Le dichiarazioni dello spazio dei nomi per un layout devono trovarsi nel tag più esterno.
  1. Taglia le dichiarazioni dello spazio dei nomi da <LinearLayout> e incollale nel tag <layout>. Il tag di apertura <layout> dovrebbe avere l'aspetto mostrato di seguito, mentre il tag <LinearLayout> deve contenere solo le proprietà della visualizzazione.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Crea ed esegui l'app per verificare di aver eseguito correttamente questa operazione.

Passaggio 3: crea un oggetto di binding nell'attività principale

Aggiungi un riferimento all'oggetto di binding all'attività principale, in modo da poterlo utilizzare per accedere alle visualizzazioni:

  1. Apri il file MainActivity.kt.
  2. Prima di onCreate(), crea una variabile per l'oggetto di binding al livello più alto. Questa variabile viene solitamente chiamata binding.

    Il tipo di binding, la classe ActivityMainBinding, viene creato dal compilatore appositamente per questa attività principale. Il nome deriva dal nome del file di layout, ovvero activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Se richiesto da Android Studio, importa ActivityMainBinding. Se non ti viene richiesto, fai clic su ActivityMainBinding e premi Alt+Enter (Option+Enter su Mac) per importare questo corso mancante. (Per altre scorciatoie da tastiera, vedi Scorciatoie da tastiera.)

    L'istruzione import dovrebbe avere un aspetto simile a quella mostrata di seguito.
import com.example.android.aboutme.databinding.ActivityMainBinding

Successivamente, sostituisci l'attuale funzione setContentView() con un'istruzione che esegue le seguenti operazioni:

  • Crea l'oggetto di associazione.
  • Utilizza la funzione setContentView() della classe DataBindingUtil per associare il layout activity_main a MainActivity. Questa funzione setContentView() si occupa anche di alcune configurazioni del data binding per le visualizzazioni.
  1. In onCreate(), sostituisci la chiamata setContentView() con la seguente riga di codice.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Importa DataBindingUtil.
import androidx.databinding.DataBindingUtil

Passaggio 4: utilizza l'oggetto di binding per sostituire tutte le chiamate a findViewById()

Ora puoi sostituire tutte le chiamate a findViewById() con riferimenti alle visualizzazioni presenti nell'oggetto di binding. Quando viene creato l'oggetto di binding, il compilatore genera i nomi delle visualizzazioni nell'oggetto di binding dagli ID delle visualizzazioni nel layout, convertendoli in camel case. Quindi, ad esempio, done_button è doneButton nell'oggetto di binding, nickname_edit diventa nicknameEdit e nickname_text diventa nicknameText.

  1. In onCreate(), sostituisci il codice che utilizza findViewById() per trovare done_button con il codice che fa riferimento al pulsante nell'oggetto di binding.

    Sostituisci questo codice: findViewById<Button>(R.id.done_button)
    con: binding.doneButton

    Il codice finito per impostare il listener di clic in onCreate() dovrebbe avere questo aspetto.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Fai lo stesso per tutte le chiamate a findViewById() nella funzione addNickname().
    Sostituisci tutte le occorrenze di findViewById<View>(R.id.id_view) con binding.idView. Procedi nel seguente modo:
  • Elimina le definizioni delle variabili editText e nicknameTextView insieme alle relative chiamate a findViewById(). Verranno visualizzati errori.
  • Correggi gli errori recuperando le visualizzazioni nicknameText, nicknameEdit e doneButton dall'oggetto binding anziché dalle variabili (eliminate).
  • Sostituisci view.visibility con binding.doneButton.visibility. L'utilizzo di binding.doneButton anziché di view passato rende il codice più coerente.

    Il risultato è il seguente codice:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Non sono previste modifiche alla funzionalità. Se vuoi, ora puoi eliminare il parametro view e aggiornare tutti gli utilizzi di view in modo che utilizzino binding.doneButton all'interno di questa funzione.
  1. nicknameText richiede String e nicknameEdit.text è un Editable. Quando utilizzi il data binding, è necessario convertire esplicitamente Editable in String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Puoi eliminare le importazioni visualizzate in grigio.
  2. Converti la funzione in Kotlin utilizzando apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Crea ed esegui la tua app...e dovrebbe avere lo stesso aspetto e funzionare esattamente come prima.

Puoi sfruttare il data binding per rendere una classe di dati direttamente disponibile per una vista. Questa tecnica semplifica il codice ed è estremamente utile per gestire i casi più complessi.

Per questo esempio, anziché impostare il nome e il nickname utilizzando le risorse stringa, crei una classe di dati per il nome e il nickname. Rendi disponibile la classe di dati alla visualizzazione utilizzando il data binding.

Passaggio 1: crea la classe di dati MyName

  1. In Android Studio, nella directory java, apri il file MyName.kt. Se non hai questo file, creane uno nuovo in Kotlin e chiamalo MyName.kt.
  2. Definisci una classificazione dei dati per il nome e il nickname. Utilizza stringhe vuote come valori predefiniti.
data class MyName(var name: String = "", var nickname: String = "")

Passaggio 2: aggiungi i dati al layout

Nel file activity_main.xml, il nome è attualmente impostato in un TextView da una risorsa stringa. Devi sostituire il riferimento al nome con un riferimento ai dati nella classe di dati.

  1. Apri activity_main.xml nella scheda Testo.
  2. Nella parte superiore del layout, tra i tag <layout> e <LinearLayout>, inserisci un tag <data></data>. È qui che collegherai la visualizzazione ai dati.
<data>
  
</data>

All'interno dei tag di dati, puoi dichiarare variabili denominate che contengono un riferimento a una classe.

  1. All'interno del tag <data>, aggiungi un tag <variable>.
  2. Aggiungi un parametro name per assegnare alla variabile il nome "myName". Aggiungi un parametro type e imposta il tipo su un nome completo della classe di dati MyName (nome del pacchetto + nome della variabile).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Ora, anziché utilizzare la risorsa stringa per il nome, puoi fare riferimento alla variabile myName.

  1. Sostituisci android:text="@string/name" con il codice riportato di seguito.

@={} è un'istruzione per ottenere i dati a cui viene fatto riferimento all'interno delle parentesi graffe.

myName fa riferimento alla variabile myName definita in precedenza, che punta alla classe di dati myName e recupera la proprietà name dalla classe.

android:text="@={myName.name}"

Passaggio 3: crea i dati

Ora hai un riferimento ai dati nel file di layout. A questo punto, crea i dati effettivi.

  1. Apri il file MainActivity.kt.
  2. Sopra onCreate(), crea una variabile privata, chiamata anche myName per convenzione. Assegna alla variabile un'istanza della classe di dati MyName, passando il nome.
private val myName: MyName = MyName("Aleks Haecky")
  1. In onCreate(), imposta il valore della variabile myName nel file di layout sul valore della variabile myName che hai appena dichiarato. Non puoi accedere direttamente alla variabile nel file XML. Devi accedervi tramite l'oggetto di binding.
binding.myName = myName
  1. Potrebbe essere visualizzato un errore perché devi aggiornare l'oggetto di binding dopo aver apportato le modifiche. Crea la tua app e l'errore dovrebbe scomparire.

Passaggio 4: utilizza la classe di dati per il nickname in TextView

Il passaggio finale consiste nell'utilizzare la classe di dati anche per il nickname in TextView.

  1. Apri activity_main.xml.
  2. Nella visualizzazione di testo nickname_text, aggiungi una proprietà text. Fai riferimento a nickname nella classe di dati, come mostrato di seguito.
android:text="@={myName.nickname}"
  1. In ActivityMain, sostituisci
    nicknameText.text = nicknameEdit.text.toString()
    con il codice per impostare il nickname nella variabile myName.
myName?.nickname = nicknameEdit.text.toString()

Una volta impostato il nickname, vuoi che il codice aggiorni la UI con i nuovi dati. Per farlo, devi invalidare tutte le espressioni di binding in modo che vengano ricreate con i dati corretti.

  1. Aggiungi invalidateAll() dopo aver impostato il nickname in modo che l'interfaccia utente venga aggiornata con il valore nell'oggetto di binding aggiornato.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Crea ed esegui la tua app, che dovrebbe funzionare esattamente come prima.

Progetto Android Studio: AboutMeDataBinding

Passaggi per utilizzare il data binding per sostituire le chiamate a findViewById():

  1. Attiva il data binding nella sezione android del file build.gradle:
    dataBinding { enabled = true }
  2. Utilizza <layout> come visualizzazione principale nel layout XML.
  3. Definisci una variabile di binding:
    private lateinit var binding: ActivityMainBinding
  4. Crea un oggetto di associazione in MainActivity, sostituendo setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Sostituisci le chiamate a findViewById() con i riferimenti alla visualizzazione nell'oggetto di binding. Ad esempio:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (nell'esempio, il nome della visualizzazione viene generato in formato camel case dal id della visualizzazione nel file XML).

Passaggi per associare le visualizzazioni ai dati:

  1. Crea una classe di dati per i tuoi dati.
  2. Aggiungi un blocco <data> all'interno del tag <layout>.
  3. Definisci un <variable> con un nome e un tipo che sia la classe di dati.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. In MainActivity, crea una variabile con un'istanza della classe di dati. Ad esempio:
    private val myName: MyName = MyName("Aleks Haecky")
  1. Nell'oggetto di binding, imposta la variabile sulla variabile che hai appena creato:
    binding.myName = myName
  1. Nel file XML, imposta i contenuti della visualizzazione sulla variabile definita nel blocco <data>. Utilizza la notazione con il punto per accedere ai dati all'interno della classe di dati.
    android:text="@={myName.name}"

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

Perché vuoi ridurre al minimo le chiamate esplicite e implicite a findViewById()?

  • Ogni volta che viene chiamato findViewById(), viene attraversata la gerarchia delle visualizzazioni.
  • findViewById() viene eseguito sul thread principale o dell'interfaccia utente.
  • Queste chiamate possono rallentare l'interfaccia utente.
  • È meno probabile che la tua app si arresti in modo anomalo.

Domanda 2

Come descriveresti il data binding?

Ad esempio, ecco alcune cose che potresti dire sul data binding:

  • L'idea principale del data binding è creare un oggetto che colleghi/mappi/associ due informazioni distanti tra loro in fase di compilazione, in modo da non dover cercare i dati in fase di runtime.
  • L'oggetto che mostra questi binding è chiamato oggetto di binding.
  • L'oggetto di binding viene creato dal compilatore.

Domanda 3

Quale dei seguenti NON è un vantaggio del data binding?

  • Il codice è più breve, più facile da leggere e da gestire.
  • Dati e visualizzazioni sono chiaramente separati.
  • Il sistema Android attraversa la gerarchia delle visualizzazioni una sola volta per ottenere ogni visualizzazione.
  • La chiamata a findViewById() genera un errore del compilatore.
  • Sicurezza dei tipi per accedere alle visualizzazioni.

Domanda 4

Qual è la funzione del tag <layout>?

  • Lo avvolgi intorno alla visualizzazione principale nel layout.
  • I binding vengono creati per tutte le visualizzazioni in un layout.
  • Indica la visualizzazione di primo livello in un layout XML che utilizza il data binding.
  • Puoi utilizzare il tag <data> all'interno di un <layout> per associare una variabile a una classe di dati.

Domanda 5

Qual è il modo corretto per fare riferimento ai dati associati nel layout XML?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Inizia la lezione successiva: 3.1: Crea un frammento

Per i link ad altri codelab di questo corso, consulta la pagina di destinazione dei codelab di Android Kotlin Fundamentals.