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 unAdapter
e unViewHolder
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 classeViewHolder
. Contiene le informazioni sulla visualizzazione di un elemento dal layout dell'elemento. I titolari delle visualizzazioni aggiungono anche le informazioni utilizzate daRecyclerView
per spostare efficacemente la visualizzazione dello schermo. - Un adattatore.
L'adattatore collega i tuoi dati aRecyclerView
. Adatta i dati in modo che possano essere visualizzati in unViewHolder
. UnRecyclerView
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
.
- Scarica l'app RecyclerViewFundamentals-Starter da GitHub.
- Crea ed esegui l'app. Osserva come i dati vengono visualizzati come testo semplice.
- Apri il file del layout
fragment_sleep_tracker.xml
nella scheda Design di Android Studio. - Nel riquadro Struttura ad albero, elimina
ScrollView
. Questa azione elimina ancheTextView
che è all'interno diScrollView
. - Nel riquadro Tavolozza, scorri l'elenco dei tipi di componenti a sinistra per trovare Contenitori, poi selezionalo.
- Trascina una
RecyclerView
dal riquadro Tavolozza nel riquadro Struttura ad albero. InserisciRecyclerView
all'interno diConstraintLayout
.
- 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.
- 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'
- Torna a
fragment_sleep_tracker.xml
. - Nella scheda Testo, cerca il codice
RecyclerView
mostrato di seguito:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />
- Dai al
id
unsleep_list
diRecyclerView
.
android:id="@+id/sleep_list"
- Posiziona l'elemento
RecyclerView
per occupare la parte rimanente dello schermo all'interno dell'elementoConstraintLayout
. Per fare ciò, limita la parte superiore delRecyclerView
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"
- Aggiungi un gestore di layout al file XML
RecyclerView
. OgniRecyclerView
deve avere un gestore di layout che gli indichi come posizionare gli elementi nell'elenco. Android fornisce un elementoLinearLayoutManager
, che per impostazione predefinita mostra 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
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.
- 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. - In
text_item_view.xml
, elimina tutto il codice fornito. - Aggiungi un carattere
TextView
con spaziatura interna all'inizio e alla fine di16dp
e dimensioni del testo pari a24sp
. Lascia che la larghezza corrisponda al valore principale e l'altezza avvolga i contenuti. Poiché questa vista viene mostrata all'interno diRecyclerView
, non è necessario posizionarla all'interno diViewGroup
.
<?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, in modo da creare la classeTextItemViewHolder
. Inserisci il codice nella parte inferiore del file dopo l'ultima parentesi di chiusura. Il codice è stato inserito inUtil.kt
perché il titolare della vista è temporaneo e lo sostituisci in un secondo momento.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
- Se richiesto, importa
android.widget.TextView
eandroidx.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.
- Nel pacchetto
sleeptracker
, crea una nuova classe Kotlin denominataSleepNightAdapter
. - Fai in modo che la classe
SleepNightAdapter
si estenda suRecyclerView.Adapter
. La classe si chiamaSleepNightAdapter
perché adatta un oggettoSleepNight
a qualcosa cheRecyclerView
può utilizzare. L'alimentatore deve sapere quale tipo di vista utilizzare, quindi passa inTextItemViewHolder
. Importa i componenti necessari quando richiesto in modo da visualizzare un errore, perché esistono metodi obbligatori da implementare.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
- Al livello più alto di
SleepNightAdapter
, crea una variabilelistOf
SleepNight
per contenere i dati.
var data = listOf<SleepNight>()
- In
SleepNightAdapter
, sostituiscigetItemCount()
per restituire le dimensioni dell'elenco delle notti di sonno indata
.RecyclerView
deve sapere quanti elementi ha l'alimentatore per visualizzarlo e lo fa chiamandogetItemCount()
.
override fun getItemCount() = data.size
- In
SleepNightAdapter
, sostituisci la funzioneonBindViewHolder()
, come mostrato di seguito.
La funzioneonBindViewHolder()
viene chiamata daRecyclerView
per visualizzare i dati relativi a un elemento dell'elenco nella posizione specificata. Il metodoonBindViewHolder()
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) {
}
- All'interno di
onBindViewHolder()
, crea una variabile per un elemento in una determinata posizione nei dati.
val item = data[position]
- Il
ViewHolder
che hai creato ha una proprietà denominatatextView
. All'interno dionBindViewHolder()
, imposta latext
ditextView
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()
- In
SleepNightAdapter
, sostituisci e implementaonCreateViewHolder()
, che viene chiamato quandoRecyclerView
richiede che un proprietario della vista rappresenti un elemento.
Questa funzione richiede due parametri e restituisce unViewHolder
. Il parametroparent
, ovvero il gruppo di viste che contiene il titolare della visualizzazione, è sempreRecyclerView
. Il parametroviewType
viene utilizzato quando ci sono più viste nello stessoRecyclerView
. Ad esempio, se inserisci un elenco di visualizzazioni di testo, un'immagine e un video nello stessoRecyclerView
, la funzioneonCreateViewHolder()
deve sapere che tipo di vista utilizzare.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
- In
onCreateViewHolder()
, crea un'istanza diLayoutInflater
.
Il programma di ridimensionamento del layout sa come creare visualizzazioni dai layout XML. Lacontext
contiene informazioni su come gonfiare correttamente la vista. In un adattatore per una visualizzazione ciclista, passi sempre nel contesto del gruppo di visteparent
, che èRecyclerView
.
val layoutInflater = LayoutInflater.from(parent.context)
- In
onCreateViewHolder()
, crea laview
chiedendo alayoutinflater
di gonfiarla.
Passaggio nel layout XML per la vista e nel gruppo di visteparent
per la vista. Il terzo, booleano, èattachToRoot
. Questo argomento deve esserefalse
, 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
- In
onCreateViewHolder()
, restituisci unTextItemViewHolder
effettuato conview
.
return TextItemViewHolder(view)
- L'adattatore deve informare
RecyclerView
quando ladata
è cambiata, in quantoRecyclerView
non sa nulla dei dati. ma sa solo chi dispone dell'adattatore.
Per comunicare aRecyclerView
quando i dati che ha visualizzato sono cambiati, aggiungi un setter personalizzato alla variabiledata
che si trova nella parte superiore della classeSleepNightAdapter
. Nel setter, assegna un nuovo valore adata
, quindi chiamanotifyDataSetChanged()
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.
- Apri
SleepTrackerFragment.kt
. - Crea un adattatore in
onCreateview()
. Inserisci questo codice dopo la creazione del modelloViewModel
e prima dell'istruzionereturn
.
val adapter = SleepNightAdapter()
- Associa
adapter
aRecyclerView
.
binding.sleepList.adapter = adapter
- Pulisci e ricrea il tuo progetto per aggiornare l'oggetto
binding
.
Se continui a riscontrare errori relativi abinding.sleepList
obinding.FragmentSleepTrackerBinding
, invalida le cache e riavvia. Seleziona File > Invalid invalid Caches / Restart (File > 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
.
- Apri
SleepTrackerViewModel
. - Trova la variabile
nights
, che memorizza tutte le notti di sonno, ovvero i dati da visualizzare. La variabilenights
viene impostata chiamandogetAllNights()
nel database. - Rimuovi
private
danights
perché creerai un osservatore che deve accedere a questa variabile. La tua 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 valoriSleepNight
comeLiveData
. Ciò significa che la variabilenights
contieneLiveData
, che viene aggiornata entro il giornoRoom
e puoi osservarenights
per sapere quando viene modificata. - Apri
SleepTrackerFragment
. - In
onCreateView()
, sotto la creazione diadapter
, crea un osservatore nella variabilenights
.
Se fornisciviewLifecycleOwner
come frammento del ciclo di vita come proprietario del ciclo di vita, puoi assicurarti che questo osservatore sia attivo solo quandoRecyclerView
è visualizzato sullo schermo.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})
- All'interno dell'osservatore, ogni volta che ricevi un valore non null (per
nights
), assegna il valore all'elementodata
dell'adattatore. Questo è 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 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.
- Nel corso
SleepNightAdapter
, aggiungi il seguente codice alla fine dionBindViewHolder()
.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}
- Esegui l'app.
- Aggiungi alcuni dati di bassa qualità e il numero sarà rosso.
- 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.
- 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
}
- 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.
- Crea un nuovo file di risorse di layout e assegnagli il nome
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. 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
- Apri
SleepNightAdapter.kt
. - Crea un corso all'interno di
SleepNightAdapter
chiamatoViewHolder
e fai in modo che venga estesoRecyclerView.ViewHolder
.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
- All'interno di
ViewHolder
puoi trovare riferimenti alle viste. Devi fare riferimento alle viste che verranno aggiornate questoViewHolder
. Ogni volta che colleghi questoViewHolder
, 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
- Nella definizione di
SleepNightAdapter
, invece diTextItemViewHolder
, utilizza ilSleepNightAdapter.ViewHolder
che hai appena creato.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {
Aggiorna onCreateViewHolder()
:
- Modifica la firma di
onCreateViewHolder()
per restituire l'elementoViewHolder
. - Modifica il gonfiatore di layout per utilizzare la risorsa di layout corretta,
list_item_sleep_night
. - Rimuovi il cast per
TextView
. - Invece di restituire un
TextItemViewHolder
, restituisci unViewHolder
.
Ecco la funzioneonCreateViewHolder()
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()
:
- Modifica la firma di
onBindViewHolder()
in modo che il parametroholder
siaViewHolder
anzichéTextItemViewHolder
. - All'interno di
onBindViewHolder()
, elimina tutto il codice, ad eccezione della definizione diitem
. - Definisci un
res
val
che contenga un riferimento all'elementoresources
per questa vista.
val res = holder.itemView.context.resources
- 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)
- Si è verificato un errore. È necessario definire
convertDurationToFormatted()
. ApriUtil.kt
e annulla il commento per il codice e le importazioni associate. Seleziona Codice > Commento con commenti sulla riga. - Torna a
onBindViewHolder()
, usaconvertNumericQualityToString()
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
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
})
- 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 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.
- In
SleepNightAdapter
, inonBindViewHolder()
, seleziona tutto tranne l'istruzione per dichiarare la variabileitem
. - Fai clic con il pulsante destro del mouse, poi seleziona Refactoring > Estrai > funzione.
- Assegna un nome alla funzione
bind
e accetta i parametri suggeriti. Fai clic su OK.
La funzionebind()
è posizionata sotto aonBindViewHolder()
.
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
holder
del parametroholder
dibind()
. PremiAlt+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) {...}
- Taglia e incolla la funzione
bind()
inViewHolder
. - Rendi
bind()
pubbliche. - Importa
bind()
nell'adattatore, se necessario. - Poiché è ora nel
ViewHolder
, puoi rimuovere la parteViewHolder
della 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: 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
.
- In
onCreateViewHolder()
, seleziona tutto il codice nel corpo della funzione. - Fai clic con il pulsante destro del mouse, poi seleziona Refactoring > Estrai > funzione.
- Assegna un nome alla funzione
from
e accetta i parametri suggeriti. Fai clic su OK. - Posiziona il cursore sul nome della funzione
from
. PremiAlt+Enter
(Option+Enter
su un Mac) per aprire il menu dell'intent. - Seleziona Sposta nell'oggetto companion. La funzione
from()
deve trovarsi in un oggetto companion, quindi può essere chiamata nella classeViewHolder
, non in un'istanzaViewHolder
. - Sposta l'oggetto
companion
nella classeViewHolder
. - Rendi
from()
pubbliche. - In
onCreateViewHolder()
, modifica l'istruzionereturn
in modo che restituisca il risultato di una chiamata afrom()
nella classeViewHolder
.
I metodionCreateViewHolder()
efrom()
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)
}
}
- Modifica la firma della classe
ViewHolder
in modo che il costruttore sia privato. Dal momento chefrom()
è ora un metodo che restituisce una nuova istanzaViewHolder
, non c'è più motivo per nessuno di chiamare il costruttore diViewHolder
.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
- 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 diRecyclerView
, definisci un elemento<RecyclerView>
nel file di layout. - LayoutManager
UnRecyclerView
utilizza una proprietàLayoutManager
per organizzare il layout degli elementi inRecyclerView
, ad esempio posizionandoli in una griglia o in un elenco lineare.
In<RecyclerView>
nel file di layout, imposta l'attributoapp:layoutManager
sul gestore di layout (ad esempioLinearLayoutManager
oGridLayoutManager
).
Puoi anche impostare l'elementoLayoutManager
per un elementoRecyclerView
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 unViewHolder
. Associa l'alimentatore alRecyclerView
.
QuandoRecyclerView
è 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 restituireViewHolder
per un elemento in elenco.
–onBindViewHolder()
per adattare i dati alle viste di un elemento in elenco. - ViewHolder
UnViewHolder
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 informareRecyclerView
quando cambiano i dati. UtilizzanotifyDataSetChanged()
per informareAdapter
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: