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
Questo codelab ti insegna a utilizzare un RecyclerView per visualizzare elenchi di elementi. Partendo dall'app di monitoraggio del sonno della serie precedente di codelab, imparerai un modo migliore e più versatile per visualizzare i dati, utilizzando un RecyclerView con un'architettura consigliata.
Cosa devi già sapere
Devi avere familiarità con:
- Creazione di un'interfaccia utente (UI) di base utilizzando un'attività, frammenti e visualizzazioni.
- Spostarsi tra i fragment e utilizzare
safeArgsper passare i dati tra i fragment. - Utilizzo di modelli di visualizzazione, fabbriche di modelli di visualizzazione, trasformazioni e
LiveDatae dei relativi osservatori. - Creazione di un database
Room, creazione di un DAO e definizione delle entità. - Utilizzo di coroutine per attività di database e altre attività di lunga durata.
Obiettivi didattici
- Come utilizzare un
RecyclerViewcon unAdaptere unViewHolderper visualizzare un elenco di elementi.
In questo lab proverai a:
- Modifica l'app TrackMySleepQuality della lezione precedente in modo che utilizzi un
RecyclerViewper visualizzare i dati sulla qualità del sonno.
In questo codelab, creerai la parte RecyclerView di un'app che monitora la qualità del sonno. L'app utilizza un database Room per archiviare i dati del sonno nel tempo.
L'app di monitoraggio del sonno iniziale ha due schermate, rappresentate da frammenti, come mostrato nella figura seguente.

La prima schermata, mostrata a sinistra, ha pulsanti per avviare e interrompere il monitoraggio. Questa schermata mostra anche tutti i dati sul sonno dell'utente. Il pulsante Cancella elimina definitivamente tutti i dati raccolti dall'app per l'utente. La seconda schermata, mostrata a destra, serve per 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 del sonno.

L'elenco delle notti di sonno visualizzato nella prima schermata è funzionale, ma non bello. L'app utilizza un formattatore complesso per creare stringhe di testo per la visualizzazione del testo e numeri per la qualità. Inoltre, questo design non è scalabile. Dopo aver risolto tutti questi problemi in questo codelab, l'app finale ha la stessa funzionalità e la schermata principale ha questo aspetto:

La visualizzazione di un elenco o di una griglia di dati è una delle attività più comuni dell'interfaccia utente in Android. Gli elenchi variano da semplici a molto complessi. Un elenco di visualizzazioni di testo potrebbe mostrare dati semplici, ad esempio una lista della spesa. Un elenco complesso, ad esempio un elenco annotato di destinazioni per le vacanze, potrebbe mostrare all'utente molti dettagli all'interno di una griglia scorrevole con intestazioni.
Per supportare tutti questi casi d'uso, Android fornisce il widget RecyclerView.

Il vantaggio principale di RecyclerView è che è molto efficiente per gli elenchi di grandi dimensioni:
- Per impostazione predefinita,
RecyclerViewelabora o disegna solo gli elementi attualmente visibili sullo schermo. Ad esempio, se il tuo elenco ha mille elementi, ma solo 10 sono visibili,RecyclerViewesegue solo il lavoro necessario per disegnare 10 elementi sullo schermo. Quando l'utente scorre,RecyclerViewcalcola quali nuovi elementi devono essere visualizzati sullo schermo e svolge il lavoro necessario per visualizzarli. - Quando un elemento scorre fuori dallo schermo, le visualizzazioni dell'elemento vengono riciclate. Ciò significa che l'elemento è pieno di nuovi contenuti che scorrono sullo schermo. Questo comportamento di
RecyclerViewconsente di risparmiare molto tempo di elaborazione e di scorrere gli elenchi in modo fluido. - Quando un elemento cambia, anziché ridisegnare l'intero elenco,
RecyclerViewpuò aggiornare solo quell'elemento. Si tratta di un enorme aumento dell'efficienza quando vengono visualizzati elenchi di elementi complessi.
Nella sequenza mostrata di seguito, puoi vedere che una visualizzazione è stata compilata con i dati, ABC. Dopo che la visualizzazione scorre fuori dallo schermo, RecyclerView la riutilizza per i nuovi dati, XYZ.
Il pattern dell'adattatore
Se viaggi spesso in paesi con prese elettriche diverse, probabilmente sai come collegare i tuoi dispositivi alle prese utilizzando un adattatore. L'adattatore consente di convertire un tipo di spina in un altro, ovvero di convertire un'interfaccia in un'altra.
Il pattern dell'adattatore nell'ingegneria del software aiuta un oggetto a funzionare con un'altra API. RecyclerView utilizza un adattatore per trasformare i dati delle app in un formato che RecyclerView può visualizzare, senza modificare il modo in cui l'app archivia ed elabora i dati. Per l'app di monitoraggio del sonno, crei un adattatore che adatta i dati del database Room in un formato che RecyclerView sa come visualizzare, senza modificare ViewModel.
Implementare un RecyclerView

Per visualizzare i dati in un RecyclerView, sono necessarie le seguenti parti:
- Dati da visualizzare.
- Un'istanza
RecyclerViewdefinita nel file di layout, che funge da contenitore per le visualizzazioni. - Un layout per una voce di dati.
Se tutti gli elementi dell'elenco hanno lo stesso aspetto, puoi utilizzare lo stesso layout per tutti, ma non è obbligatorio. Il layout dell'elemento deve essere creato separatamente dal layout del frammento, in modo che sia possibile creare e compilare con i dati una visualizzazione di un elemento alla volta. - Un gestore layout.
Il gestore layout gestisce l'organizzazione (il layout) dei componenti UI in una visualizzazione. - Un view holder.
Il view holder estende la classeViewHolder. Contiene le informazioni sulla visualizzazione per mostrare un elemento del layout. I segnaposto aggiungono anche informazioni cheRecyclerViewutilizza per spostare in modo efficiente le visualizzazioni sullo schermo. - Un adattatore.
L'adattatore connette i dati aRecyclerView. Adatta i dati in modo che possano essere visualizzati in unViewHolder. UnRecyclerViewutilizza l'adattatore per capire come visualizzare i dati sullo schermo.
In questa attività, aggiungerai un RecyclerView al file di layout e configurerai un Adapter per esporre i dati sul sonno al RecyclerView.
Passaggio 1: aggiungi RecyclerView con LayoutManager
In questo passaggio, sostituisci ScrollView con RecyclerView nel file fragment_sleep_tracker.xml.
- Scarica l'app RecyclerViewFundamentals-Starter da GitHub.
- Crea ed esegui l'app. Nota come i dati vengono visualizzati come testo semplice.
- Apri il file di layout
fragment_sleep_tracker.xmlnella scheda Progettazione di Android Studio. - Nel riquadro Struttura dei componenti, elimina
ScrollView. Questa azione elimina ancheTextViewall'interno diScrollView. - Nel riquadro Tavolozza, scorri l'elenco dei tipi di componenti a sinistra per trovare Contenitori, quindi selezionalo.
- Trascina un
RecyclerViewdal riquadro Palette al riquadro Struttura dei componenti. InserisciRecyclerViewall'interno diConstraintLayout.

- Se si apre una finestra di dialogo che ti chiede se vuoi aggiungere una dipendenza, fai clic su Ok per consentire ad Android Studio di aggiungere la dipendenza
recyclerviewal file Gradle. Potrebbero essere necessari alcuni secondi, dopodiché l'app si sincronizza.

- Apri il file
build.gradledel modulo, scorri fino alla fine e prendi nota della nuova dipendenza, che ha un aspetto simile al codice riportato di seguito:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- Torna a
fragment_sleep_tracker.xml. - Nella scheda Testo, cerca il codice
RecyclerViewmostrato di seguito:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />- Assegna a
RecyclerViewuniddisleep_list.
android:id="@+id/sleep_list"- Posiziona
RecyclerViewin modo che occupi la parte rimanente dello schermo all'interno diConstraintLayout. Per farlo, vincola la parte superiore diRecyclerViewal pulsante Avvia, la parte inferiore al pulsante Cancella e ciascun lato al contenitore principale. Imposta la larghezza e l'altezza del layout su 0 dp nell'editor del 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"- Aggiungi un gestore di layout al file XML
RecyclerView. OgniRecyclerViewha bisogno di un gestore del layout che indichi come posizionare gli elementi nell'elenco. Android fornisce unLinearLayoutManager, che per impostazione predefinita dispone gli elementi in un elenco verticale di righe a larghezza intera.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"- Passa alla scheda Design e nota che i vincoli aggiunti hanno causato l'espansione di
RecyclerViewper riempire lo spazio disponibile.

Passaggio 2: crea il layout dell'elemento di elenco e il segnaposto della visualizzazione di testo
RecyclerView è solo un contenitore. In questo passaggio, crei il layout e l'infrastruttura per gli elementi da visualizzare all'interno di RecyclerView.
Per ottenere un RecyclerView funzionante il più rapidamente possibile, all'inizio utilizzi un elemento di elenco semplificato che mostra solo la qualità del sonno come numero. Per questo, hai bisogno di un segnaposto della visualizzazione, TextItemViewHolder. Hai anche bisogno di una visualizzazione, un TextView, per i dati. In un passaggio successivo, scoprirai di più sui segnaposto della visualizzazione e su come disporre tutti i dati sul sonno.
- Crea un file di layout denominato
text_item_view.xml. Non importa cosa utilizzi come elemento principale, perché sostituirai il codice del modello. - In
text_item_view.xml, elimina tutto il codice fornito. - Aggiungi un
TextViewcon un riempimento di16dpall'inizio e alla fine e una dimensione del testo di24sp. Imposta la larghezza in modo che corrisponda a quella del contenitore principale e l'altezza in modo che contenga i contenuti. Poiché questa visualizzazione viene visualizzata all'interno diRecyclerView, non devi inserirla in unViewGroup.
<?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" />- Apri
Util.kt. Scorri fino alla fine e aggiungi la definizione mostrata di seguito, che crea la classeTextItemViewHolder. Inserisci il codice in fondo al file, dopo l'ultima parentesi graffa chiusa. Il codice va inserito inUtil.ktperché questo segnaposto della visualizzazione è temporaneo e lo sostituirai in un secondo momento.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)- Se richiesto, importa
android.widget.TextVieweandroidx.recyclerview.widget.RecyclerView.
Passaggio 3: crea SleepNightAdapter
L'attività principale nell'implementazione di un RecyclerView è la creazione dell'adattatore. Hai un semplice segnaposto per la visualizzazione degli articoli e un layout per ogni articolo. Ora puoi creare un adattatore. L'adattatore crea un segnaposto della visualizzazione e lo riempie con i dati da visualizzare per RecyclerView.
- Nel pacchetto
sleeptracker, crea una nuova classe Kotlin denominataSleepNightAdapter. - Fai in modo che la classe
SleepNightAdapterestendaRecyclerView.Adapter. La classe si chiamaSleepNightAdapterperché adatta un oggettoSleepNighta qualcosa cheRecyclerViewpuò utilizzare. L'adattatore deve sapere quale view holder utilizzare, quindi passaTextItemViewHolder. Importa i componenti necessari quando richiesto, quindi visualizzerai un errore perché ci sono metodi obbligatori da implementare.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}- Al livello superiore di
SleepNightAdapter, crea una variabilelistOfSleepNightper contenere i dati.
var data = listOf<SleepNight>()- In
SleepNightAdapter, esegui l'override digetItemCount()per restituire la dimensione dell'elenco delle notti di sonno indata.RecyclerViewdeve sapere quanti elementi ha l'adattatore da visualizzare e lo fa chiamandogetItemCount().
override fun getItemCount() = data.size- In
SleepNightAdapter, esegui l'override della funzioneonBindViewHolder(), come mostrato di seguito.
La funzioneonBindViewHolder()viene chiamata daRecyclerViewper visualizzare i dati di un elemento di elenco nella posizione specificata. Pertanto, il metodoonBindViewHolder()accetta due argomenti: un view holder e una posizione dei dati da associare. Per questa app, il titolare èTextItemViewHoldere la posizione è la posizione nell'elenco.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}- In
onBindViewHolder(), crea una variabile per un elemento in una determinata posizione nei dati.
val item = data[position]- Il
ViewHolderche hai creato ha una proprietà denominatatextView. All'interno dionBindViewHolder(), impostatextditextViewsul numero di qualità del sonno. Questo codice mostra solo un elenco di numeri, ma questo semplice esempio ti consente di vedere come l'adattatore inserisce i dati nel view holder e sullo schermo.
holder.textView.text = item.sleepQuality.toString()- In
SleepNightAdapter, esegui l'override e implementaonCreateViewHolder(), che viene chiamato quandoRecyclerViewha bisogno di un segnaposto della visualizzazione per rappresentare un elemento.
Questa funzione accetta due parametri e restituisce unViewHolder. Il parametroparent, ovvero il gruppo di visualizzazione che contiene il segnaposto della visualizzazione, è sempreRecyclerView. Il parametroviewTypeviene utilizzato quando sono presenti più visualizzazioni nello stessoRecyclerView. Ad esempio, se inserisci un elenco di visualizzazioni di testo, un'immagine e un video nello stessoRecyclerView, la funzioneonCreateViewHolder()deve sapere quale tipo di visualizzazione utilizzare.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}- In
onCreateViewHolder(), crea un'istanza diLayoutInflater.
Il layout inflater sa come creare viste dai layout XML.contextcontiene informazioni su come gonfiare correttamente la visualizzazione. In un adattatore per una visualizzazione del riciclatore, passi sempre il contesto del gruppo di visualizzazioneparent, ovveroRecyclerView.
val layoutInflater = LayoutInflater.from(parent.context)- In
onCreateViewHolder(), creaviewchiedendo alayoutinflaterdi gonfiarlo.
Passa nel layout XML per la visualizzazione e nel gruppo di visualizzazioneparentper la visualizzazione. Il terzo argomento, booleano, èattachToRoot. Questo argomento deve esserefalse, perchéRecyclerViewaggiunge questo elemento alla gerarchia delle visualizzazioni quando è il momento.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView- In
onCreateViewHolder(), restituisci unTextItemViewHolderacquistato conview.
return TextItemViewHolder(view)- L'adattatore deve comunicare a
RecyclerViewquandodataè cambiato, perchéRecyclerViewnon sa nulla dei dati. Conosce solo i segnaposto della visualizzazione che l'adattatore gli fornisce.
Per comunicare aRecyclerViewquando i dati visualizzati sono cambiati, aggiungi un setter personalizzato alla variabiledatache si trova nella parte superiore della classeSleepNightAdapter. Nel setter, assegna adataun nuovo valore, quindi chiamanotifyDataSetChanged()per attivare il ridisegno dell'elenco con i nuovi dati.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}Passaggio 4: comunica a RecyclerView l'adattatore
RecyclerView deve conoscere l'adattatore da utilizzare per ottenere i view holder.
- Apri
SleepTrackerFragment.kt. - In
onCreateview(), crea un adattatore. Inserisci questo codice dopo la creazione del modelloViewModele prima dell'istruzionereturn.
val adapter = SleepNightAdapter()- Associa
adapteraRecyclerView.
binding.sleepList.adapter = adapter- Pulisci e ricompila il progetto per aggiornare l'oggetto
binding.
Se continui a visualizzare errori relativi abinding.sleepListobinding.FragmentSleepTrackerBinding, invalida le cache e riavvia. (Seleziona File > Invalida cache / Riavvia.)
Se esegui l'app ora, non ci sono errori, ma non vedrai alcun dato visualizzato quando tocchi Avvia e poi Interrompi.
Passaggio 5: inserisci i dati nell'adattatore
Finora hai un adattatore e un modo per trasferire i dati dall'adattatore a RecyclerView. Ora devi inserire i dati nell'adattatore da ViewModel.
- Apri
SleepTrackerViewModel. - Trova la variabile
nights, che memorizza tutte le notti di sonno, ovvero i dati da visualizzare. La variabilenightsviene impostata chiamandogetAllNights()sul database. - Rimuovi
privatedanights, perché creerai un osservatore che deve accedere a questa variabile. La dichiarazione dovrebbe essere simile a questa:
val nights = database.getAllNights()- Nel pacchetto
database, apriSleepDatabaseDao. - Trova la funzione
getAllNights(). Tieni presente che questa funzione restituisce un elenco di valoriSleepNightcomeLiveData. Ciò significa che la variabilenightscontieneLiveData, che viene aggiornato daRoom, e puoi osservarenightsper sapere quando cambia. - Apri
SleepTrackerFragment. - In
onCreateView(), sotto la creazione diadapter, crea un osservatore sulla variabilenights.
FornendoviewLifecycleOwnerdel fragment come proprietario del ciclo di vita, puoi assicurarti che questo osservatore sia attivo solo quandoRecyclerViewè sullo schermo.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})- All'interno dell'observer, ogni volta che ricevi un valore non nullo (per
nights), assegna il valore adatadell'adattatore. Ecco il codice completato per l'osservatore e l'impostazione dei dati:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})- Crea ed esegui il codice.
Se l'adattatore funziona, vedrai i numeri della qualità del sonno in un elenco. Lo screenshot a sinistra mostra -1 dopo aver 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 segnaposto delle visualizzazioni
RecyclerView Ricicla i segnaposto delle visualizzazioni, il che significa che li riutilizza. Quando una visualizzazione esce dallo schermo, RecyclerView la riutilizza per la visualizzazione che sta per entrare nello schermo.
Poiché questi segnaposto di visualizzazione vengono riciclati, assicurati che onBindViewHolder() imposti o reimposti le personalizzazioni che gli elementi precedenti potrebbero aver impostato su un segnaposto di visualizzazione.
Ad esempio, puoi impostare il colore del testo su rosso nei segnaposto della visualizzazione che contengono valutazioni della qualità inferiori o uguali a 1 e rappresentano un sonno di scarsa qualità.
- Nella classe
SleepNightAdapter, aggiungi il seguente codice alla fine dionBindViewHolder().
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}- Esegui l'app.
- Aggiungi alcuni dati sulla scarsa qualità del sonno e il numero è rosso.
- Aggiungi valutazioni elevate per la qualità del sonno finché non vedi un numero rosso elevato sullo schermo.
PoichéRecyclerViewriutilizza i segnaposto delle visualizzazioni, alla fine riutilizza uno dei segnaposto rossi per una valutazione di alta qualità. Il punteggio elevato viene visualizzato erroneamente in rosso.

- Per risolvere il problema, aggiungi un'istruzione
elseper impostare il colore nero se la qualità non è inferiore o uguale a uno.
Con entrambe le condizioni esplicite, il segnaposto 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
}- Esegui l'app e i numeri dovrebbero sempre avere il colore corretto.
Complimenti! Ora hai un RecyclerView di base completamente funzionante.
In questa attività, sostituisci il semplice segnaposto della visualizzazione con uno che possa visualizzare più dati per una notte di sonno.
Il semplice ViewHolder che hai aggiunto a Util.kt racchiude un TextView in un TextItemViewHolder.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)Allora perché RecyclerView non utilizza direttamente un TextView? Questa riga di codice offre molte funzionalità. Un ViewHolder descrive la visualizzazione di un elemento e i metadati relativi alla sua posizione all'interno di RecyclerView. RecyclerView si basa su questa funzionalità per posizionare correttamente la visualizzazione durante lo scorrimento dell'elenco e per eseguire operazioni interessanti come animare le visualizzazioni quando vengono aggiunti o rimossi elementi in Adapter.
Se RecyclerView deve accedere alle viste archiviate in ViewHolder, può farlo utilizzando la proprietà itemView del titolare della vista. RecyclerView utilizza itemView quando associa un elemento da visualizzare sullo schermo, quando disegna decorazioni intorno a una visualizzazione come un bordo e per l'implementazione dell'accessibilità.
Passaggio 1: crea il layout dell'elemento
In questo passaggio, crei il file di layout per un elemento. Il layout è costituito da un ConstraintLayout con un ImageView per la qualità del sonno, un TextView per la durata del sonno e un TextView per la qualità come testo. Poiché hai già creato layout, copia e incolla il codice XML fornito.
- Crea un nuovo file di risorse di layout e chiamalo
list_item_sleep_night. - 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>- Passa alla scheda Design in Android Studio. Nella visualizzazione Progettazione, il layout è simile a quello dello screenshot a sinistra riportato di seguito. Nella visualizzazione Progetto, l'aspetto è simile a quello dello screenshot a destra.

Passaggio 2: crea ViewHolder
- Apri
SleepNightAdapter.kt. - Crea una classe all'interno di
SleepNightAdapterdenominataViewHoldere falla estendere aRecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}- All'interno di
ViewHolder, ottieni riferimenti alle visualizzazioni. Devi fare riferimento alle visualizzazioni che verranno aggiornate da questoViewHolder. Ogni volta che esegui il binding di questoViewHolder, devi accedere all'immagine e a entrambe le visualizzazioni di testo. (Convertirai questo codice per utilizzare il data binding 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
- Nella definizione di
SleepNightAdapter, utilizzaSleepNightAdapter.ViewHolderche hai appena creato anzichéTextItemViewHolder.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {Aggiornamento onCreateViewHolder():
- Modifica la firma di
onCreateViewHolder()per restituireViewHolder. - Modifica il layout inflator in modo che utilizzi la risorsa di layout corretta,
list_item_sleep_night. - Rimuovi la trasmissione a
TextView. - Anziché restituire un
TextItemViewHolder, restituisci unViewHolder.
Ecco la funzioneonCreateViewHolder()aggiornata:
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)
}Aggiornamento onBindViewHolder():
- Modifica la firma di
onBindViewHolder()in modo che il parametroholdersia unViewHolderanziché unTextItemViewHolder. - All'interno di
onBindViewHolder(), elimina tutto il codice, ad eccezione della definizione diitem. - Definisci un
valresche contenga un riferimento alresourcesper questa visualizzazione.
val res = holder.itemView.context.resources- Imposta il testo della visualizzazione di testo
sleepLengthsulla durata. Copia il codice riportato di seguito, che chiama una funzione di formattazione fornita con il codice iniziale.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)- Viene visualizzato un errore perché è necessario definire
convertDurationToFormatted(). ApriUtil.kte rimuovi il commento dal codice e dalle importazioni associate. (Seleziona Codice > Commenta con commenti di riga.) - In
onBindViewHolder(), utilizzaconvertNumericQualityToString()per impostare la qualità.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)- Potrebbe essere necessario importare manualmente queste funzioni.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString- Imposta l'icona corretta per la qualità. La nuova icona
ic_sleep_activeè fornita nel codice iniziale.
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
})- Ecco la funzione
onBindViewHolder()aggiornata e completata, che imposta tutti i dati perViewHolder:
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
})
}- Esegui l'app. Il display dovrebbe essere simile allo screenshot riportato di seguito, che mostra l'icona della qualità del sonno, insieme al testo relativo alla durata e alla qualità del sonno.

La tua RecyclerView è stata completata. Hai imparato a implementare un Adapter e un ViewHolder e li hai combinati per visualizzare un elenco con un RecyclerView Adapter.
Il codice finora mostra il processo di creazione di un adattatore e di un view holder. Tuttavia, puoi migliorare questo codice. Il codice da visualizzare e il codice per gestire i segnaposto della visualizzazione sono confusi e onBindViewHolder() conosce i dettagli su come aggiornare ViewHolder.
In un'app di produzione, potresti avere più titolari di visualizzazione, adattatori più complessi e più sviluppatori che apportano modifiche. Devi strutturare il codice in modo che tutto ciò che riguarda un view holder si trovi solo al suo interno.
Passaggio 1: esegui il refactoring di onBindViewHolder()
In questo passaggio, esegui il refactoring del codice e sposti tutte le funzionalità del segnaposto della visualizzazione in ViewHolder. Lo scopo di questo refactoring non è quello di modificare l'aspetto dell'app per l'utente, ma di rendere più facile e sicuro il lavoro degli sviluppatori sul codice. Fortunatamente, Android Studio dispone di strumenti utili.
- In
SleepNightAdapter, inonBindViewHolder(), seleziona tutto tranne l'istruzione per dichiarare la variabileitem. - Fai clic con il tasto destro del mouse, poi seleziona Refactor > Extract > Function.
- Assegna alla funzione il nome
binde accetta i parametri suggeriti. Fai clic su Ok.
La funzionebind()è posizionata sottoonBindViewHolder().
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
})
}- Posiziona il cursore sulla parola
holderdel parametroholderdibind(). PremiAlt+Enter(Option+Entersu Mac) per aprire il menu Intenzione. Seleziona Converti parametro in ricevitore per convertirlo in una funzione di estensione con la seguente firma:
private fun ViewHolder.bind(item: SleepNight) {...}- Taglia e incolla la funzione
bind()inViewHolder. - Rendi pubblico
bind(). - Se necessario, importa
bind()nell'adattatore. - Poiché ora si trova in
ViewHolder, puoi rimuovere la parteViewHolderdella firma. Ecco il codice finale per la funzionebind()nella classeViewHolder.
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: refactor di onCreateViewHolder
Il metodo onCreateViewHolder() nell'adattatore attualmente gonfia la visualizzazione dalla risorsa di layout per ViewHolder. Tuttavia, l'inflazione non ha nulla a che fare con l'adattatore, ma con il ViewHolder. L'inflazione dovrebbe verificarsi nel ViewHolder.
- In
onCreateViewHolder(), seleziona tutto il codice nel corpo della funzione. - Fai clic con il tasto destro del mouse, poi seleziona Refactor > Extract > Function.
- Assegna alla funzione il nome
frome accetta i parametri suggeriti. Fai clic su Ok. - Posiziona il cursore sul nome della funzione
from. PremiAlt+Enter(Option+Entersu Mac) per aprire il menu Intenzione. - Seleziona Sposta nell'oggetto complementare. La funzione
from()deve trovarsi in un oggetto complementare in modo che possa essere chiamata nella classeViewHolder, non in un'istanzaViewHolder. - Sposta l'oggetto
companionnella classeViewHolder. - Rendi pubblico
from(). - In
onCreateViewHolder(), modifica l'istruzionereturnin modo che restituisca il risultato della chiamata afrom()nella classeViewHolder.
I metodionCreateViewHolder()efrom()completati dovrebbero avere l'aspetto del codice riportato di seguito e il codice dovrebbe essere compilato 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)
}
}- Modifica la firma della classe
ViewHolderin modo che il costruttore sia privato. Poichéfrom()ora è un metodo che restituisce una nuova istanza diViewHolder, non c'è più motivo per chiamare il costruttore diViewHolder.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){- Esegui l'app. La tua app dovrebbe essere creata 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 dell'interfaccia utente in Android.
RecyclerViewè progettato per essere efficiente anche quando vengono visualizzati elenchi molto grandi. RecyclerViewesegue solo il lavoro necessario per elaborare o disegnare gli elementi attualmente visibili sullo schermo.- Quando un elemento scorre fuori dallo schermo, le relative visualizzazioni vengono riciclate. Ciò significa che l'elemento è pieno di nuovi contenuti che scorrono sullo schermo.
- Il pattern dell'adattatore nell'ingegneria del software aiuta un oggetto a funzionare insieme a un'altra API.
RecyclerViewutilizza un adattatore per trasformare i dati dell'app in un formato visualizzabile, senza dover modificare il modo in cui l'app archivia ed elabora i dati.
Per visualizzare i dati in un RecyclerView, sono necessarie le seguenti parti:
- RecyclerView
: per creare un'istanza diRecyclerView, definisci un elemento<RecyclerView>nel file di layout. - LayoutManager
UnRecyclerViewutilizza unLayoutManagerper organizzare il layout degli elementi nelRecyclerView, ad esempio disponendoli in una griglia o in un elenco lineare.
Nel<RecyclerView>nel file di layout, imposta l'attributoapp:layoutManagersul gestore del layout (ad esempioLinearLayoutManageroGridLayoutManager).
Puoi anche impostareLayoutManagerper unRecyclerViewin modo programmatico. Questa tecnica viene trattata in un codelab successivo. - Layout per 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 il modo in cui verranno visualizzati in unViewHolder. Associa l'adattatore alRecyclerView.
Quando viene eseguitoRecyclerView, utilizza l'adattatore per capire come visualizzare i dati sullo schermo.
L'adattatore richiede l'implementazione dei seguenti metodi:
–getItemCount()per restituire il numero di elementi.
–onCreateViewHolder()per restituireViewHolderper un elemento nell'elenco.
–onBindViewHolder()per adattare i dati alle visualizzazioni per un elemento nell'elenco. - ViewHolder
UnViewHoldercontiene le informazioni sulla visualizzazione per mostrare un elemento dal layout dell'elemento. - Il metodo
onBindViewHolder()nell'adattatore adatta i dati alle visualizzazioni. Esegui sempre l'override di questo metodo. In genere,onBindViewHolder()gonfia il layout di un elemento e inserisce i dati nelle visualizzazioni del layout. - Poiché
RecyclerViewnon conosce i dati,Adapterdeve informareRecyclerViewquando questi cambiano. UtilizzanotifyDataSetChanged()per comunicare aAdapterche i dati sono stati modificati.
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
Come vengono visualizzati gli elementi in RecyclerView? Seleziona tutte le opzioni pertinenti.
▢ Mostra gli elementi in un elenco o in una griglia.
▢ Scorri in verticale o in orizzontale.
▢ Scorrono in diagonale sui dispositivi più grandi, come i tablet.
▢ Consente layout personalizzati quando un elenco o una griglia non sono sufficienti per il caso d'uso.
Domanda 2
Quali sono i vantaggi dell'utilizzo di RecyclerView? Seleziona tutte le opzioni pertinenti.
▢ Visualizza in modo efficiente elenchi di grandi dimensioni.
▢ Aggiorna automaticamente i dati.
▢ Riduce al minimo la necessità di aggiornamenti quando un elemento viene aggiornato, eliminato o aggiunto all'elenco.
▢ Riutilizza la visualizzazione che scorre fuori dallo schermo per visualizzare l'elemento successivo che scorre sullo schermo.
Domanda 3
Quali sono alcuni dei motivi per utilizzare gli adattatori? Seleziona tutte le opzioni pertinenti.
▢ La separazione delle competenze semplifica la modifica e il test del codice.
▢ RecyclerView è indipendente dai dati visualizzati.
▢ I livelli di trattamento dati non devono preoccuparsi di come verranno visualizzati i dati.
▢ L'app verrà eseguita più rapidamente.
Domanda 4
Quali delle seguenti affermazioni su ViewHolder sono vere? Seleziona tutte le opzioni 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.
▢ Adapter associa i dati a ViewHolder.
Inizia la lezione successiva: