Concetti fondamentali di Android Kotlin 07.1: nozioni di base su RecyclerView

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

Questo codelab ti insegna a utilizzare un RecyclerView per mostrare elenchi di articoli. Utilizzando l'app di monitoraggio del sonno della precedente serie di codelab, scopri un modo migliore e più versatile di mostrare i dati utilizzando un RecyclerView con un'architettura consigliata.

Informazioni importanti

Dovresti acquisire familiarità con:

  • Creare un'interfaccia utente (UI) di base utilizzando un'attività, frammenti e viste.
  • Spostamento tra frammenti e utilizzo di safeArgs per trasmettere dati tra frammenti.
  • Utilizzo di modelli di visualizzazione, visualizzazione di fabbriche di modelli, trasformazioni e LiveData e i loro osservatori.
  • Creazione di un database Room, creazione di un DAO e definizione delle entità.
  • Utilizzo di coroutine per le attività di database e altre attività di lunga durata.

Obiettivi didattici

  • Come utilizzare un RecyclerView con un Adapter e un ViewHolder per visualizzare un elenco di articoli.

In questo lab proverai a:

  • Cambia l'app TrackMySleepQualità della lezione precedente per utilizzare un RecyclerView per visualizzare i dati sulla qualità del sonno.

In questo codelab, crei la parte RecyclerView di un'app che monitora la qualità del sonno. L'app utilizza un database Room per memorizzare i dati sul sonno nel tempo.

L'app di monitoraggio del sonno iniziale ha due schermate, rappresentate da frammenti, come mostrato nella figura che segue.

La prima schermata, mostrata a sinistra, ha i pulsanti per avviare e interrompere il monitoraggio. Questa schermata mostra anche tutti i dati relativi al sonno dell'utente. Il pulsante Cancella elimina definitivamente tutti i dati raccolti dall'app per l'utente. La seconda schermata, mostrata a destra, consente di selezionare una valutazione della qualità del sonno.

Questa app utilizza un'architettura semplificata con un controller UI, ViewModel e LiveData. L'app utilizza anche un database Room per rendere persistenti i dati relativi al sonno.

L'elenco delle notti di sonno visualizzato nella prima schermata è funzionale, ma non piacevole. L'app utilizza un formattatore complesso per creare stringhe di testo per la visualizzazione di testo e numeri per la qualità. Inoltre, questo modello non è in scala. Una volta risolti tutti questi problemi in questo codelab, l'app finale ha le stesse funzionalità e la schermata principale ha il seguente aspetto:

La visualizzazione di un elenco o di una griglia di dati è una delle attività più comuni nell'interfaccia utente di Android. Gli elenchi variano da semplice a molto complesso. Un elenco delle visualizzazioni di testo potrebbe mostrare dati semplici, ad esempio una lista della spesa. Un elenco complesso, ad esempio un elenco annotato di destinazioni per vacanze, potrebbe mostrare all'utente molti dettagli all'interno di una griglia a scorrimento con intestazioni.

Per supportare tutti questi casi d'uso, Android fornisce il widget RecyclerView.

Il vantaggio maggiore di RecyclerView è che è molto efficiente per gli elenchi di grandi dimensioni:

  • Per impostazione predefinita, RecyclerView funziona solo per elaborare o disegnare gli elementi attualmente visibili sullo schermo. Ad esempio, se l'elenco contiene mille elementi, ma sono visibili solo 10 elementi, RecyclerView è in grado di disegnare solo 10 elementi sullo schermo. Quando l'utente scorre la pagina, RecyclerView cerca di capire quali nuovi elementi dovrebbero essere visualizzati sullo schermo e funziona in modo sufficiente per visualizzarli.
  • Quando un elemento scorre sullo schermo, le sue visualizzazioni vengono riciclate. Ciò significa che l'elemento viene riempito con nuovi contenuti che scorrono sullo schermo. Questo comportamento di RecyclerView consente di risparmiare molto tempo di elaborazione e aiuta gli elenchi a scorrere senza problemi.
  • Quando un elemento cambia, RecyclerView può aggiornare l'intero elenco senza dover ricreare l'elenco. Si tratta di un enorme aumento dell'efficienza quando si visualizzano elenchi di elementi complessi.

Nella seguente sequenza riportata, puoi vedere che una vista è stata compilata con dati, ABC. Quando la visualizzazione scorre oltre lo schermo, RecyclerView la riutilizza per i nuovi dati, XYZ.

Il motivo dell'adattatore

Se ti sposti in paesi che utilizzano prese elettriche diverse, probabilmente sai come collegare i tuoi dispositivi alle prese utilizzando un adattatore. L'adattatore consente di convertire un tipo di presa in un'altra, convertendo davvero un'interfaccia in un'altra.

Il pattern dell'adattatore nel software engineering consente a un oggetto di lavorare con un'altra API. RecyclerView utilizza un adattatore per trasformare i dati dell'app in un elemento che RecyclerView può visualizzare, senza modificare la modalità di archiviazione ed elaborazione dei dati da parte dell'app. Per l'app di monitoraggio del sonno, crei un adattatore che adatta i dati del database Room in qualcosa che RecyclerView sa come mostrare, senza modificare ViewModel.

Implementazione di un RecyclerView

Per visualizzare i tuoi dati in un RecyclerView, hai bisogno delle seguenti parti:

  • Dati da visualizzare.
  • Un'istanza RecyclerView definita nel file di layout, affinché funga da contenitore delle viste.
  • Un layout di un elemento di dati.
    Se tutti gli elementi dell'elenco hanno lo stesso aspetto, puoi utilizzare lo stesso layout per tutti gli elementi, ma non è obbligatorio. Il layout dell'elemento deve essere creato separatamente dal layout dei frammenti, in modo da poter creare e inserire dati per una visualizzazione elemento alla volta.
  • Un gestore del layout.
    Il gestore del layout gestisce l'organizzazione (il layout) dei componenti dell'interfaccia utente in una vista.
  • Un titolare della visualizzazione.
    Il titolare della vista estende la classe ViewHolder. Contiene le informazioni sulla visualizzazione di un elemento dal layout dell'elemento. I titolari delle visualizzazioni aggiungono anche le informazioni utilizzate da RecyclerView per spostare efficacemente la visualizzazione dello schermo.
  • Un adattatore.
    L'adattatore collega i tuoi dati a RecyclerView. Adatta i dati in modo che possano essere visualizzati in un ViewHolder. Un RecyclerView utilizza l'adattatore per capire come mostrare i dati sullo schermo.

In questa attività, aggiungi un RecyclerView al file di layout e configuri un Adapter per esporre i dati relativi al sonno al RecyclerView.

Passaggio 1: aggiungi RecyclerView con LayoutManager

In questo passaggio devi sostituire il tag ScrollView con un valore RecyclerView nel file fragment_sleep_tracker.xml.

  1. Scarica l'app RecyclerViewFundamentals-Starter da GitHub.
  2. Crea ed esegui l'app. Osserva come i dati vengono visualizzati come testo semplice.
  3. Apri il file del layout fragment_sleep_tracker.xml nella scheda Design di Android Studio.
  4. Nel riquadro Struttura ad albero, elimina ScrollView. Questa azione elimina anche TextView che è all'interno di ScrollView.
  5. Nel riquadro Tavolozza, scorri l'elenco dei tipi di componenti a sinistra per trovare Contenitori, poi selezionalo.
  6. Trascina una RecyclerView dal riquadro Tavolozza nel riquadro Struttura ad albero. Inserisci RecyclerView all'interno di ConstraintLayout.

  1. Se viene visualizzata una finestra di dialogo che chiede se vuoi aggiungere una dipendenza, fai clic su OK per consentire ad Android Studio di aggiungere la dipendenza recyclerview al file Gradle. La sincronizzazione dell'app potrebbe richiedere alcuni secondi.

  1. Apri il file del modulo build.gradle, scorri fino alla fine e prendi nota della nuova dipendenza, simile al codice seguente:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
  1. Torna a fragment_sleep_tracker.xml.
  2. Nella scheda Testo, cerca il codice RecyclerView mostrato di seguito:
<androidx.recyclerview.widget.RecyclerView
   android:layout_width="match_parent"
   android:layout_height="match_parent" />
  1. Dai al id un sleep_list di RecyclerView.
android:id="@+id/sleep_list"
  1. Posiziona l'elemento RecyclerView per occupare la parte rimanente dello schermo all'interno dell'elemento ConstraintLayout. Per fare ciò, limita la parte superiore del RecyclerView al pulsante Start, poi la parte inferiore al pulsante Cancella e ogni lato al genitore. Imposta la larghezza e l'altezza del layout su 0 dp nell'editor di layout o in XML, utilizzando il seguente codice:
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@+id/clear_button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/stop_button"
  1. Aggiungi un gestore di layout al file XML RecyclerView. Ogni RecyclerView deve avere un gestore di layout che gli indichi come posizionare gli elementi nell'elenco. Android fornisce un elemento LinearLayoutManager, che per impostazione predefinita mostra gli elementi in un elenco verticale di righe a larghezza intera.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
  1. Passa alla scheda Design e nota che i vincoli aggiunti hanno causato l'espansione di RecyclerView per riempire lo spazio disponibile.

Passaggio 2: crea il layout della voce di elenco e il contenitore della visualizzazione di testo

RecyclerView è solo un contenitore. In questo passaggio, creerai il layout e l'infrastruttura per gli elementi da visualizzare all'interno di RecyclerView.

Per accedere a un RecyclerView funzionante il più rapidamente possibile, utilizza innanzitutto un elenco semplificato che mostra solo la qualità del sonno sotto forma di numero. Per questa operazione ti serve un proprietario della vista, TextItemViewHolder. Per i dati, devi disporre anche di una TextView. In un passaggio successivo, scoprirai di più sui titolari delle viste e su come disporre tutti i dati del sonno.

  1. Crea un file di layout denominato text_item_view.xml. Non ha importanza ciò che utilizzi come elemento principale, in quanto dovrai sostituire il codice del modello.
  2. In text_item_view.xml, elimina tutto il codice fornito.
  3. Aggiungi un carattere TextView con spaziatura interna all'inizio e alla fine di 16dp e dimensioni del testo pari a 24sp. Lascia che la larghezza corrisponda al valore principale e l'altezza avvolga i contenuti. Poiché questa vista viene mostrata all'interno di RecyclerView, non è necessario posizionarla all'interno di ViewGroup.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:textSize="24sp"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:layout_width="match_parent"       
    android:layout_height="wrap_content" />
  1. Apri Util.kt. Scorri fino alla fine e aggiungi la definizione mostrata di seguito, in modo da creare la classe TextItemViewHolder. Inserisci il codice nella parte inferiore del file dopo l'ultima parentesi di chiusura. Il codice è stato inserito in Util.kt perché il titolare della vista è temporaneo e lo sostituisci in un secondo momento.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
  1. Se richiesto, importa android.widget.TextView e androidx.recyclerview.widget.RecyclerView.

Passaggio 3: crea SleepNightAdapter

L'attività principale per l'implementazione di un RecyclerView è la creazione dell'adattatore. Per la visualizzazione dell'elemento, disporrai di un semplice contenitore per la visualizzazione e di un layout per ogni elemento. Ora puoi creare un adattatore. L'adattatore crea un titolare della visualizzazione e lo riempie con dati che RecyclerView può mostrare.

  1. Nel pacchetto sleeptracker, crea una nuova classe Kotlin denominata SleepNightAdapter.
  2. Fai in modo che la classe SleepNightAdapter si estenda su RecyclerView.Adapter. La classe si chiama SleepNightAdapter perché adatta un oggetto SleepNight a qualcosa che RecyclerView può utilizzare. L'alimentatore deve sapere quale tipo di vista utilizzare, quindi passa in TextItemViewHolder. Importa i componenti necessari quando richiesto in modo da visualizzare un errore, perché esistono metodi obbligatori da implementare.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
  1. Al livello più alto di SleepNightAdapter, crea una variabile listOf SleepNight per contenere i dati.
var data =  listOf<SleepNight>()
  1. In SleepNightAdapter, sostituisci getItemCount() per restituire le dimensioni dell'elenco delle notti di sonno in data. RecyclerView deve sapere quanti elementi ha l'alimentatore per visualizzarlo e lo fa chiamando getItemCount().
override fun getItemCount() = data.size
  1. In SleepNightAdapter, sostituisci la funzione onBindViewHolder(), come mostrato di seguito.

    La funzione onBindViewHolder()viene chiamata da RecyclerView per visualizzare i dati relativi a un elemento dell'elenco nella posizione specificata. Il metodo onBindViewHolder() prevede quindi due argomenti: un titolare della visualizzazione e una posizione dei dati da associare. Per questa app, il titolare è TextItemViewHolder e la posizione è la posizione nell'elenco.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
  1. All'interno di onBindViewHolder(), crea una variabile per un elemento in una determinata posizione nei dati.
 val item = data[position]
  1. Il ViewHolder che hai creato ha una proprietà denominata textView. All'interno di onBindViewHolder(), imposta la text di textView sul numero per la qualità del sonno. Questo codice mostra solo un elenco di numeri, ma questo semplice esempio ti consente di vedere in che modo l'adattatore trasmette i dati al titolare della visualizzazione e allo schermo.
holder.textView.text = item.sleepQuality.toString()
  1. In SleepNightAdapter, sostituisci e implementa onCreateViewHolder(), che viene chiamato quando RecyclerView richiede che un proprietario della vista rappresenti un elemento.

    Questa funzione richiede due parametri e restituisce un ViewHolder. Il parametro parent, ovvero il gruppo di viste che contiene il titolare della visualizzazione, è sempre RecyclerView. Il parametro viewType viene utilizzato quando ci sono più viste nello stesso RecyclerView. Ad esempio, se inserisci un elenco di visualizzazioni di testo, un'immagine e un video nello stesso RecyclerView, la funzione onCreateViewHolder() deve sapere che tipo di vista utilizzare.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
  1. In onCreateViewHolder(), crea un'istanza di LayoutInflater.

    Il programma di ridimensionamento del layout sa come creare visualizzazioni dai layout XML. La context contiene informazioni su come gonfiare correttamente la vista. In un adattatore per una visualizzazione ciclista, passi sempre nel contesto del gruppo di viste parent, che è RecyclerView.
val layoutInflater = LayoutInflater.from(parent.context)
  1. In onCreateViewHolder(), crea la view chiedendo a layoutinflater di gonfiarla.

    Passaggio nel layout XML per la vista e nel gruppo di viste parent per la vista. Il terzo, booleano, è attachToRoot. Questo argomento deve essere false, perché RecyclerView aggiunge questo elemento alla gerarchia di visualizzazione quando è il momento.
val view = layoutInflater
       .inflate(R.layout.text_item_view, parent, false) as TextView
  1. In onCreateViewHolder(), restituisci un TextItemViewHolder effettuato con view.
return TextItemViewHolder(view)
  1. L'adattatore deve informare RecyclerView quando la data è cambiata, in quanto RecyclerView non sa nulla dei dati. ma sa solo chi dispone dell'adattatore.

    Per comunicare a RecyclerView quando i dati che ha visualizzato sono cambiati, aggiungi un setter personalizzato alla variabile data che si trova nella parte superiore della classe SleepNightAdapter. Nel setter, assegna un nuovo valore a data, quindi chiama notifyDataSetChanged() per attivare il nuovo ritiro dell'elenco con i nuovi dati.
var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

Passaggio 4: informa RecyclerView sull'adattatore

Il RecyclerView deve conoscere l'adattatore da utilizzare per acquisire i proprietari delle viste.

  1. Apri SleepTrackerFragment.kt.
  2. Crea un adattatore in onCreateview(). Inserisci questo codice dopo la creazione del modello ViewModel e prima dell'istruzione return.
val adapter = SleepNightAdapter()
  1. Associa adapter a RecyclerView.
binding.sleepList.adapter = adapter
  1. Pulisci e ricrea il tuo progetto per aggiornare l'oggetto binding.

    Se continui a riscontrare errori relativi a binding.sleepList o binding.FragmentSleepTrackerBinding, invalida le cache e riavvia. Seleziona File > Invalid invalid Caches / Restart (File &GT; Invalida cache/Riavvia).

    Se esegui l'app ora, non ci sono errori, ma non vedrai dati visualizzati quando tocchi Avvia, quindi Interrompi.

Passaggio 5: inserisci i dati nell'adattatore

Finora disponi di un adattatore e di un metodo per recuperare i dati dall'adattatore in RecyclerView. Ora devi inserire dati nell'adattatore da ViewModel.

  1. Apri SleepTrackerViewModel.
  2. Trova la variabile nights, che memorizza tutte le notti di sonno, ovvero i dati da visualizzare. La variabile nights viene impostata chiamando getAllNights() nel database.
  3. Rimuovi private da nights perché creerai un osservatore che deve accedere a questa variabile. La tua dichiarazione dovrebbe essere simile a questa:
val nights = database.getAllNights()
  1. Nel pacchetto database, apri SleepDatabaseDao.
  2. Trova la funzione getAllNights(). Tieni presente che questa funzione restituisce un elenco di valori SleepNight come LiveData. Ciò significa che la variabile nights contiene LiveData, che viene aggiornata entro il giorno Room e puoi osservare nights per sapere quando viene modificata.
  3. Apri SleepTrackerFragment.
  4. In onCreateView(), sotto la creazione di adapter, crea un osservatore nella variabile nights.

    Se fornisci viewLifecycleOwner come frammento del ciclo di vita come proprietario del ciclo di vita, puoi assicurarti che questo osservatore sia attivo solo quando RecyclerView è visualizzato sullo schermo.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   })
  1. All'interno dell'osservatore, ogni volta che ricevi un valore non null (per nights), assegna il valore all'elemento data dell'adattatore. Questo è il codice completato per l'osservatore e l'impostazione dei dati:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.data = it
   }
})
  1. Crea ed esegui il tuo codice.

    Se l'alimentatore funziona, vedrai i numeri relativi alla qualità del sonno sotto forma di elenco. Lo screenshot a sinistra mostra -1 dopo che avrai toccato Avvia. Lo screenshot a destra mostra il numero aggiornato della qualità del sonno dopo aver toccato Interrompi e selezionato una valutazione della qualità.

Passaggio 6: scopri come vengono riciclati i titolari delle viste

RecyclerView ricicla i proprietari, il che significa che li riutilizza. Man mano che si scorre la schermata, RecyclerView la riutilizza per la vista che sta per scorrere sullo schermo.

Poiché questi supporti vista vengono riciclati, assicurati che onBindViewHolder() imposti o reimposti eventuali personalizzazioni impostate sugli elementi precedenti su un supporto.

Ad esempio, potresti impostare il colore del testo di colore rosso sui titolari di visualizzazione che hanno valutazioni di qualità inferiori o uguali a 1 e rappresentano una qualità del sonno scadente.

  1. Nel corso SleepNightAdapter, aggiungi il seguente codice alla fine di onBindViewHolder().
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
}
  1. Esegui l'app.
  2. Aggiungi alcuni dati di bassa qualità e il numero sarà rosso.
  3. Aggiungi valutazioni alte per la qualità del sonno fino a quando non appare un numero alto rosso sullo schermo.

    Poiché RecyclerView riutilizza i contenitori per visualizzazioni, alla fine riutilizza uno dei display per una valutazione alta. La valutazione elevata viene mostrata per errore in rosso.

  1. Per risolvere questo problema, aggiungi un'istruzione else per impostare il colore nero su un foglio di colore scuro se la qualità non è inferiore o uguale a una delle due.

    Se entrambe le condizioni sono esplicite, il titolare della visualizzazione utilizzerà il colore del testo corretto per ogni elemento.
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
} else {
   // reset
   holder.textView.setTextColor(Color.BLACK) // black
}
  1. Esegui l'app e i numeri dovrebbero sempre avere il colore corretto.

Complimenti! Ora hai un RecyclerView di base completamente funzionante.

In questa attività, sostituirai il titolare della visualizzazione semplice con uno in grado di visualizzare più dati per una notte di sonno.

Il semplice ViewHolder che hai aggiunto a Util.kt aggrega un elemento TextView in un elemento TextItemViewHolder.

class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)

Quindi RecyclerView perché non utilizza direttamente TextView? Questa riga di codice fornisce molte funzionalità. ViewHolder descrive una visualizzazione elemento e i metadati sulla relativa posizione all'interno di RecyclerView. RecyclerView si affida a questa funzionalità per posizionare correttamente la vista mentre l'elenco scorre e per fare cose interessanti come animare le visualizzazioni quando vengono aggiunti o rimossi elementi in Adapter.

Se RecyclerView deve accedere alle viste memorizzate in ViewHolder, può farlo usando la proprietà itemView del titolare della vista. RecyclerView utilizza itemView quando associa un elemento da visualizzare sullo schermo, mentre disegna decorazioni intorno a una visualizzazione, ad esempio un bordo, e per l'implementazione dell'accessibilità.

Passaggio 1: crea il layout degli elementi

In questo passaggio, devi creare il file di layout per un elemento. Il layout comprende un ConstraintLayout con ImageView per la qualità del sonno, TextView per la durata del sonno e TextView per la qualità come testo. Poiché hai eseguito i layout in precedenza, copia e incolla il codice XML fornito.

  1. Crea un nuovo file di risorse di layout e assegnagli il nome list_item_sleep_night.
  2. Sostituisci tutto il codice nel file con il codice riportato di seguito. Poi acquisisci familiarità con il layout che hai appena creato.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <ImageView
       android:id="@+id/quality_image"
       android:layout_width="@dimen/icon_size"
       android:layout_height="60dp"
       android:layout_marginStart="16dp"
       android:layout_marginTop="8dp"
       android:layout_marginBottom="8dp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       tools:srcCompat="@drawable/ic_sleep_5" />

   <TextView
       android:id="@+id/sleep_length"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginStart="8dp"
       android:layout_marginTop="8dp"
       android:layout_marginEnd="16dp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toEndOf="@+id/quality_image"
       app:layout_constraintTop_toTopOf="@+id/quality_image"
       tools:text="Wednesday" />

   <TextView
       android:id="@+id/quality_string"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginTop="8dp"
       app:layout_constraintEnd_toEndOf="@+id/sleep_length"
       app:layout_constraintHorizontal_bias="0.0"
       app:layout_constraintStart_toStartOf="@+id/sleep_length"
       app:layout_constraintTop_toBottomOf="@+id/sleep_length"
       tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>
  1. Passa alla scheda Design in Android Studio. In Vista struttura, il layout è simile allo screenshot a sinistra di seguito. Nella visualizzazione del progetto base, sembra lo screenshot a destra.

Passaggio 2: crea ViewHolder

  1. Apri SleepNightAdapter.kt.
  2. Crea un corso all'interno di SleepNightAdapter chiamato ViewHolder e fai in modo che venga esteso RecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
  1. All'interno di ViewHolder puoi trovare riferimenti alle viste. Devi fare riferimento alle viste che verranno aggiornate questo ViewHolder. Ogni volta che colleghi questo ViewHolder, devi accedere all'immagine e a entrambe le visualizzazioni di testo. Se converti questo codice per utilizzare l'associazione di dati in un secondo momento.
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)

Passaggio 3: utilizza Viewholder in SleepNightAdapter

  1. Nella definizione di SleepNightAdapter, invece di TextItemViewHolder, utilizza il SleepNightAdapter.ViewHolder che hai appena creato.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {

Aggiorna onCreateViewHolder():

  1. Modifica la firma di onCreateViewHolder() per restituire l'elemento ViewHolder.
  2. Modifica il gonfiatore di layout per utilizzare la risorsa di layout corretta, list_item_sleep_night.
  3. Rimuovi il cast per TextView.
  4. Invece di restituire un TextItemViewHolder, restituisci un ViewHolder.

    Ecco la funzione onCreateViewHolder() aggiornata e terminata:
    override fun onCreateViewHolder(
            parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = 
            LayoutInflater.from(parent.context)
        val view = layoutInflater
                .inflate(R.layout.list_item_sleep_night, 
                         parent, false)
        return ViewHolder(view)
    }

Aggiorna onBindViewHolder():

  1. Modifica la firma di onBindViewHolder() in modo che il parametro holder sia ViewHolder anziché TextItemViewHolder.
  2. All'interno di onBindViewHolder(), elimina tutto il codice, ad eccezione della definizione di item.
  3. Definisci un res val che contenga un riferimento all'elemento resources per questa vista.
val res = holder.itemView.context.resources
  1. Imposta il testo della visualizzazione di testo sleepLength sulla durata. Copia il codice riportato di seguito, che chiama una funzione di formattazione fornita con il codice di avvio.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
  1. Si è verificato un errore. È necessario definire convertDurationToFormatted(). Apri Util.kt e annulla il commento per il codice e le importazioni associate. Seleziona Codice > Commento con commenti sulla riga.
  2. Torna a onBindViewHolder(), usa convertNumericQualityToString() per impostare la qualità.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
  1. Potrebbe essere necessario importare manualmente queste funzioni.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
  1. Imposta l'icona corretta per la qualità. La nuova icona ic_sleep_active viene fornita nel codice di avvio.
holder.qualityImage.setImageResource(when (item.sleepQuality) {
   0 -> R.drawable.ic_sleep_0
   1 -> R.drawable.ic_sleep_1
   2 -> R.drawable.ic_sleep_2
   3 -> R.drawable.ic_sleep_3
   4 -> R.drawable.ic_sleep_4
   5 -> R.drawable.ic_sleep_5
   else -> R.drawable.ic_sleep_active
})
  1. Ecco la funzione onBindViewHolder() aggiornata e completata, che imposta tutti i dati per ViewHolder:
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = data[position]
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. Esegui l'app. Il display dovrebbe essere simile allo screenshot di seguito, che mostra l'icona della qualità del sonno e il testo per visualizzare la durata e la qualità del sonno.

L'ordine RecyclerView è stato completato. Hai imparato a implementare gli elementi Adapter e ViewHolder e li hai messi insieme per visualizzare un elenco contenente gli RecyclerView di Adapter.

Finora il codice indica il processo di creazione di un adattatore e un supporto per le viste. Tuttavia, puoi migliorare questo codice. Il codice da visualizzare e il codice per gestire i titolari delle viste sono vari e onBindViewHolder() conosce i dettagli su come aggiornare ViewHolder.

In un'app in produzione, potrebbero essere presenti più titolari, adattatori più complessi e più sviluppatori che apportano le modifiche necessarie. Il codice deve essere strutturato in modo che tutti gli elementi correlati a un tipo di visualizzazione siano disponibili solo in quest'ultimo.

Passaggio 1: refactoring onBindViewHolder()

In questo passaggio, esegui il refactoring del codice e trasferisci tutte le funzionalità di visualizzazione in ViewHolder. Lo scopo del refactoring non è modificare l'aspetto dell'app per l'utente, ma consentire agli sviluppatori di lavorare più facilmente e in modo più sicuro sul codice. Fortunatamente, Android Studio offre strumenti utili.

  1. In SleepNightAdapter, in onBindViewHolder(), seleziona tutto tranne l'istruzione per dichiarare la variabile item.
  2. Fai clic con il pulsante destro del mouse, poi seleziona Refactoring > Estrai > funzione.
  3. Assegna un nome alla funzione bind e accetta i parametri suggeriti. Fai clic su OK.

    La funzione bind() è posizionata sotto a onBindViewHolder().
    private fun bind(holder: ViewHolder, item: SleepNight) {
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. Posiziona il cursore sulla parola holder del parametro holder di bind(). Premi Alt+Enter (Option+Enter su un Mac) per aprire il menu dell'intent. Seleziona Converti parametro in destinatario per convertirlo in una funzione di estensione con la seguente firma:
private fun ViewHolder.bind(item: SleepNight) {...}
  1. Taglia e incolla la funzione bind() in ViewHolder.
  2. Rendi bind() pubbliche.
  3. Importa bind() nell'adattatore, se necessario.
  4. Poiché è ora nel ViewHolder, puoi rimuovere la parte ViewHolder della firma. Ecco il codice finale per la funzione bind() nella classe ViewHolder.
fun bind(item: SleepNight) {
   val res = itemView.context.resources
   sleepLength.text = convertDurationToFormatted(
           item.startTimeMilli, item.endTimeMilli, res)
   quality.text = convertNumericQualityToString(
           item.sleepQuality, res)
   qualityImage.setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

Passaggio 2: refactoring su CreateViewHolder

Al momento, il metodo onCreateViewHolder() nell'adattatore aumenta la visibilità dalla risorsa di layout per ViewHolder. L'aumento non ha nulla a che fare con l'adattatore e tutto ciò che ha a che fare con il ViewHolder. L'aumento dovrebbe avvenire nel ViewHolder.

  1. In onCreateViewHolder(), seleziona tutto il codice nel corpo della funzione.
  2. Fai clic con il pulsante destro del mouse, poi seleziona Refactoring > Estrai > funzione.
  3. Assegna un nome alla funzione from e accetta i parametri suggeriti. Fai clic su OK.
  4. Posiziona il cursore sul nome della funzione from. Premi Alt+Enter (Option+Enter su un Mac) per aprire il menu dell'intent.
  5. Seleziona Sposta nell'oggetto companion. La funzione from() deve trovarsi in un oggetto companion, quindi può essere chiamata nella classe ViewHolder, non in un'istanza ViewHolder.
  6. Sposta l'oggetto companion nella classe ViewHolder.
  7. Rendi from() pubbliche.
  8. In onCreateViewHolder(), modifica l'istruzione return in modo che restituisca il risultato di una chiamata a from() nella classe ViewHolder.

    I metodi onCreateViewHolder() e from() completati dovrebbero essere simili al seguente codice e il codice dovrebbe essere creato ed eseguito senza errori.
    override fun onCreateViewHolder(parent: ViewGroup, viewType: 
Int): ViewHolder {
        return ViewHolder.from(parent)
    }
companion object {
   fun from(parent: ViewGroup): ViewHolder {
       val layoutInflater = LayoutInflater.from(parent.context)
       val view = layoutInflater
               .inflate(R.layout.list_item_sleep_night, parent, false)
       return ViewHolder(view)
   }
}
  1. Modifica la firma della classe ViewHolder in modo che il costruttore sia privato. Dal momento che from() è ora un metodo che restituisce una nuova istanza ViewHolder, non c'è più motivo per nessuno di chiamare il costruttore di ViewHolder.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
  1. Esegui l'app. L'app deve essere realizzata ed eseguita come prima, ovvero il risultato desiderato dopo il refactoring.

Progetto Android Studio: RecyclerViewFundamentals

  • La visualizzazione di un elenco o di una griglia di dati è una delle attività più comuni nell'interfaccia utente di Android. RecyclerView è progettato per essere efficiente anche quando gli elenchi sono molto grandi.
  • RecyclerView si occupa solo del lavoro necessario per elaborare o disegnare gli elementi attualmente visibili sullo schermo.
  • Quando un elemento scorre oltre lo schermo, le sue visualizzazioni vengono riciclate. Ciò significa che l'elemento viene riempito con nuovi contenuti che scorrono sullo schermo.
  • Il pattern dell'adattatore nel software engineering consente a un oggetto di collaborare con un'altra API. RecyclerView utilizza un adattatore per trasformare i dati dell'app in qualcosa che può visualizzare, senza bisogno di modificare il modo in cui l'app archivia ed elabora i dati.

Per visualizzare i tuoi dati in un RecyclerView, hai bisogno delle seguenti parti:

  • RecyclerView
    Per creare un'istanza di RecyclerView, definisci un elemento <RecyclerView> nel file di layout.
  • LayoutManager
    Un RecyclerView utilizza una proprietà LayoutManager per organizzare il layout degli elementi in RecyclerView, ad esempio posizionandoli in una griglia o in un elenco lineare.

    In <RecyclerView> nel file di layout, imposta l'attributo app:layoutManager sul gestore di layout (ad esempio LinearLayoutManager o GridLayoutManager).

    Puoi anche impostare l'elemento LayoutManager per un elemento RecyclerView in modo programmatico. Questa tecnica è trattata in un codelab successivo.
  • Layout di ogni elemento
    Crea un layout per un elemento di dati in un file di layout XML.
  • Adattatore
    Crea un adattatore che prepari i dati e li venga visualizzati in un ViewHolder. Associa l'alimentatore al RecyclerView.

    Quando RecyclerView è in esecuzione, utilizza l'adattatore per capire come mostrare i dati sullo schermo.

    L'adattatore richiede di implementare i seguenti metodi:
    getItemCount() per restituire il numero di elementi.
    onCreateViewHolder() per restituire ViewHolder per un elemento in elenco.
    onBindViewHolder() per adattare i dati alle viste di un elemento in elenco.

  • ViewHolder
    Un ViewHolder contiene le informazioni sulla visualizzazione per la visualizzazione di un elemento dal layout dell'elemento.
  • Il metodo onBindViewHolder() nell'adattatore adatta i dati alle viste. Questo metodo viene sempre sostituito. In genere, onBindViewHolder() gonfia il layout di un elemento e inserisce i dati nelle visualizzazioni al suo interno.
  • Poiché RecyclerView non sa nulla dei dati, Adapter deve informare RecyclerView quando cambiano i dati. Utilizza notifyDataSetChanged() per informare Adapter che i dati sono stati modificati.

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

In che modo RecyclerView visualizza gli elementi? Seleziona tutte le risposte pertinenti.

▢ Visualizza gli elementi in un elenco o in una griglia.

▢ Scorri in verticale o in orizzontale.

▢ Scorri in diagonale su dispositivi più grandi, come i tablet.

▢ Consente layout personalizzati quando un elenco o una griglia non è sufficiente per il caso d'uso.

Domanda 2

Quali sono i vantaggi di RecyclerView? Seleziona tutte le risposte pertinenti.

▢ Visualizza in modo efficiente elenchi di grandi dimensioni.

▢ Aggiorna automaticamente i dati.

▢ Riduci al minimo la necessità di aggiornamenti quando un articolo viene aggiornato, eliminato o aggiunto all'elenco.

▢ Riutilizza la visualizzazione che scorre oltre lo schermo per visualizzare l'elemento successivo che scorre sullo schermo.

Domanda 3

Quali sono alcuni dei motivi per cui vengono utilizzati gli adattatori? Seleziona tutte le risposte pertinenti.

▢ La separazione dei problemi semplifica la modifica e il test del codice.

RecyclerView è indipendente dai dati visualizzati.

▢ I livelli di elaborazione dati non devono preoccuparsi di come verranno visualizzati i dati.

▢ L'app verrà eseguita più velocemente.

Domanda 4

Quale delle seguenti affermazioni relative a ViewHolder è vera? Seleziona tutte le risposte pertinenti.

▢ Il layout ViewHolder è definito nei file di layout XML.

▢ Esiste un ViewHolder per ogni unità di dati nel set di dati.

▢ Puoi avere più di un ViewHolder in un RecyclerView.

▢ Il Adapter associa i dati al ViewHolder.

Inizia la lezione successiva: 7.2: DiffUtil e data binding con RecyclerView