Dieses Codelab ist Teil des Android Kotlin Fundamentals-Kurss. Sie profitieren von diesem Kurs, wenn Sie nacheinander die Codelabs durcharbeiten. Alle Kurs-Codelabs finden Sie auf der Landingpage für Kotlin-Grundlagen für Android-Entwickler.
Einführung
In diesem Codelab lernen Sie, wie Sie mit RecyclerView
eine Liste von Elementen aufrufen. Bauen Sie auf der Schlaf-Tracker-App aus der vorherigen Serie von Codelabs auf und lernen Sie eine bessere und vielseitigere Art der Datendarstellung mithilfe einer RecyclerView
mit einer empfohlenen Architektur kennen.
Was Sie bereits wissen sollten
Sie sollten mit Folgendem vertraut sein:
- Erstellen einer einfachen Benutzeroberfläche mit einer Aktivität, Fragmenten und Ansichten
- Die Navigation zwischen den Fragmenten und die Verwendung von
safeArgs
zum Übergeben von Daten zwischen Fragmenten. - Mithilfe von Modellen, Modellfabriken, Transformationen und
LiveData
sowie deren Betrachter ansehen - Erstellen einer
Room
-Datenbank, Erstellen eines DAO und Definieren von Entitäten - Koroutinen für Datenbankaufgaben und andere lang andauernde Aufgaben verwenden.
Lerninhalte
RecyclerView
mitAdapter
undViewHolder
verwenden, um eine Liste von Elementen aufzurufen.
Aufgaben
- Ändere die TrackMySleep Quality-App aus der vorherigen Lektion, um eine
RecyclerView
zu nutzen, um Schlafqualitätsdaten anzuzeigen.
In diesem Codelab erstellst du den RecyclerView
-Teil einer App, die deine Schlafqualität verfolgt. Die App verwendet eine Room
-Datenbank, um Schlafdaten im Zeitverlauf zu speichern.
Die Starter-App für den Schlaftracker hat zwei Bildschirme, die als Fragmente dargestellt sind (siehe Abbildung unten).
Auf dem ersten Bildschirm auf der linken Seite finden Sie Schaltflächen zum Starten und Beenden der Aufzeichnung. Auf diesem Bildschirm werden auch alle Schlafdaten des Nutzers angezeigt. Mit der Schaltfläche Löschen werden alle Daten gelöscht, die die App für den Nutzer erfasst hat. Auf dem zweiten Bildschirm rechts wird die Qualität des Schlafs angezeigt.
Diese App verwendet eine vereinfachte Architektur mit einem UI-Controller, ViewModel
und LiveData
. Die App nutzt auch eine Room
-Datenbank, um Schlafdaten dauerhaft zu speichern.
Die auf dem ersten Bildschirm angezeigte Liste der Schlafabende ist funktionsfähig, aber nicht sehr gut. In der App werden komplexe Textformatierungen verwendet, um Textstrings für die Textansicht und Zahlen für die Qualität zu erstellen. Außerdem lässt sich dieses Design nicht skalieren. Nachdem Sie all diese Probleme in diesem Codelab behoben haben, hat die endgültige App dieselben Funktionen und der Hauptbildschirm sieht so aus:
Das Anzeigen einer Liste oder ein Raster von Daten ist eine der häufigsten Aufgaben auf der Android-Benutzeroberfläche. Die Listen variieren von einfach bis sehr komplex. Eine Liste mit Textaufrufen kann einfache Daten enthalten, wie etwa eine Einkaufsliste. In einer komplexen Liste, zum Beispiel mit einer kommentierten Liste von Reisezielen, werden dem Nutzer möglicherweise viele Details in einem scrollbaren Raster mit Headern angezeigt.
Für alle diese Anwendungsfälle bietet Android das Widget RecyclerView
.
Der größte Vorteil von RecyclerView
ist, dass das Tool für große Listen sehr effizient ist:
- Standardmäßig kann
RecyclerView
nur Elemente verarbeiten oder zeichnen, die auf dem Bildschirm sichtbar sind. Beispiel: Wenn Ihre Liste 1.000 Elemente enthält, aber nur 10 Elemente sichtbar sind, reichtRecyclerView
aus, 10 Elemente auf dem Bildschirm zu zeichnen. Wenn der Nutzer scrollt, entscheidetRecyclerView
, welche Elemente auf dem Bildschirm zu sehen sein sollen, und reicht gerade aus, diese Elemente anzuzeigen. - Wenn ein Element vom Bildschirm scrollt, werden die Aufrufe des Elements recycelt. Das heißt, das Element wird mit neuem Content gefüllt, der auf dem Bildschirm scrollt. Mit diesem Verhalten von
RecyclerView
lässt sich viel Zeit sparen. Außerdem wird die Fließbewegung eingeschränkt. - Wenn ein Element geändert wird, zieht die
RecyclerView
dieses Element dann nicht mehr neu, sondern aktualisiert es vollständig. Das ist ein enormer Effizienzgewinn, wenn Sie Listen mit komplexen Elementen anzeigen!
In der folgenden Abbildung sehen Sie, dass eine Ansicht mit Daten gefüllt wurde: ABC
. Nachdem die Ansicht vom Bildschirm gescrollt wurde, verwendet RecyclerView
die Ansicht für neue Daten (XYZ
).
Das Adaptermuster
Wenn Sie in andere Länder reisen, die unterschiedliche Steckdosen verwenden, wissen Sie wahrscheinlich, wie Sie Ihre Geräte mithilfe eines Adapters an eine Steckdose anschließen können. Mit dem Adapter können Sie ganz einfach eine Steckdose in eine andere umwandeln.
Das adapter-Muster in Software Engineering hilft einem Objekt bei der Arbeit mit einer anderen API. RecyclerView
verwendet einen Adapter, um App-Daten in etwas umzuwandeln, das RecyclerView
anzeigen kann, ohne dass die Daten in der App gespeichert und verarbeitet werden. Für die Schlaf-Tracker-App erstellst du einen Adapter, der Daten aus der Room
-Datenbank in etwas anpasst, das RecyclerView
anzeigen kann, ohne den ViewModel
zu ändern.
RecyclerView implementieren
Um Ihre Daten in einer RecyclerView
anzuzeigen, benötigen Sie die folgenden Teile:
- Anzuzeigende Daten.
- Eine in Ihrer Layoutdatei definierte
RecyclerView
-Instanz, die als Container für die Ansichten verwendet wird. - Ein Layout für ein Element mit Daten.
Wenn alle Listenelemente gleich aussehen, können Sie für alle Elemente dasselbe Layout verwenden. Dies ist jedoch nicht zwingend erforderlich. Das Elementlayout muss getrennt vom Layout des Fragments erstellt werden, damit eine Elementansicht der einzelnen erstellt und mit Daten gefüllt werden kann. - Ein Layoutmanager.
Der Layoutmanager verwaltet die Organisation (das Layout) der UI-Komponenten in einer Datenansicht. - Ein Aufrufer.
Der Inhaber der Ansicht erweitert die KlasseViewHolder
. Die Datei enthält die Ansichtsinformationen für einen Element aus dem Layout des Elements. Ansichtsinhaber fügen auch Informationen hinzu, dieRecyclerView
zum effizienten Verschieben von Ansichten auf dem Bildschirm verwendet. - Ein Adapter.
Der Adapter verbindet Ihre Daten mit demRecyclerView
. Die Daten werden so angepasst, dass sie in einemViewHolder
angezeigt werden können.RecyclerView
nutzt den Adapter, um herauszufinden, wie die Daten auf dem Bildschirm dargestellt werden sollen.
In dieser Aufgabe fügst du deiner Layoutdatei ein RecyclerView
hinzu und richte ein Adapter
ein, um Schlafdaten für RecyclerView
verfügbar zu machen.
Schritt 1: RecyclerView mit LayoutManager hinzufügen
In diesem Schritt ersetzen Sie ScrollView
in der Datei fragment_sleep_tracker.xml
durch RecyclerView
.
- Laden Sie die App RecyclerViewFundamentals-Starter von GitHub herunter.
- Erstellen Sie die App und führen Sie sie aus. Die Daten werden in einem einfachen Text angezeigt.
- Öffne die Android-
fragment_sleep_tracker.xml
Layoutdatei auf dem Tab Design. - Löschen Sie im Bereich Baumbaum den
ScrollView
. Durch diese Aktion werden auch dieTextView
gelöscht, die sich innerhalb desScrollView
befinden. - Scrollen Sie im Bereich Palette durch die Liste der Komponententypen auf der linken Seite, um nach Containern zu suchen und sie auszuwählen.
- Ziehen Sie eine
RecyclerView
aus dem Bereich Palette in die Baumstruktur. PlatziereRecyclerView
in derConstraintLayout
.
- Wenn Sie ein Dialogfeld öffnen, in dem Sie gefragt werden, ob Sie eine Abhängigkeit hinzufügen möchten, klicken Sie auf OK, damit Android Studio die Abhängigkeit
recyclerview
zu Ihrer Gradle-Datei hinzufügen kann. Das kann einige Sekunden dauern. Dann wird Ihre App synchronisiert.
- Öffnen Sie die Modul-
build.gradle
-Datei, scrollen Sie zum Ende und notieren Sie sich die neue Abhängigkeit, die dem folgenden Code ähnelt:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- Zurück zu
fragment_sleep_tracker.xml
. - Suchen Sie auf dem Tab Text nach dem folgenden
RecyclerView
-Code:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />
- Gib der
RecyclerView
eineid
vonsleep_list
.
android:id="@+id/sleep_list"
- Positioniere
RecyclerView
so, dass der verbleibende Teil des Bildschirms inConstraintLayout
enthalten ist. Sie können dazu den oberen Bereich vonRecyclerView
auf die Schaltfläche Starten, den unteren Rand auf die Schaltfläche Löschen beschränken und jede Seite zum übergeordneten Element hinzufügen. Legen Sie im Layouteditor oder in XML die Layoutbreite und -höhe auf 0 dp fest. Verwenden Sie dazu den folgenden Code:
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"
- Fügen Sie den Layout-Manager der
RecyclerView
-XML-Datei hinzu. JederRecyclerView
benötigt einen Layoutmanager, der angibt, wie Elemente in der Liste positioniert werden sollen. Android stellt eineLinearLayoutManager
zur Verfügung, die standardmäßig die Elemente in einer vertikalen Liste von Zeilen mit voller Breite darstellt.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- Wechseln Sie zum Tab Design. Die zusätzlichen Einschränkungen haben dazu geführt, dass
RecyclerView
zu sehen ist, um den verfügbaren Platz zu füllen.
Schritt 2: Layout und Listenhalter erstellen
RecyclerView
ist nur ein Container. In diesem Schritt erstellen Sie das Layout und die Infrastruktur für die Elemente, die in der RecyclerView
angezeigt werden sollen.
Damit du so schnell wie möglich zu einer funktionierenden RecyclerView
gelangt, verwendest du zuerst ein einfaches Listenelement, das nur die Schlafqualität als Zahl anzeigt. Dafür benötigen Sie den Datenansichtsinhaber TextItemViewHolder
. Sie benötigen außerdem eine Ansicht, eine TextView
für die Daten. In einem späteren Schritt erfahren Sie mehr über Aufrufer und wie Sie alle Schlafdaten herausstellen.
- Erstellen Sie eine Layoutdatei mit dem Namen
text_item_view.xml
. Es spielt keine Rolle, was Sie als Stammelement verwenden, da Sie den Vorlagencode ersetzen. - In
text_item_view.xml
wird der gesamte Code gelöscht. - Fügen Sie einen
TextView
mit16dp
-Text am Anfang und Ende und eine Textgröße von24sp
hinzu. Breite und Breite des Inhalts müssen von der Höhe umschlossen werden. Da diese Ansicht innerhalb vonRecyclerView
angezeigt wird, musst du sie in einemViewGroup
platzieren.
<?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" />
- Öffnen Sie
Util.kt
. Scrollen Sie nach unten und fügen Sie die Definition unten hinzu, mit der dieTextItemViewHolder
-Klasse erstellt wird. Fügen Sie den Code am Ende der Datei nach der letzten schließenden geschweiften Klammer ein. Der Code wird inUtil.kt
eingefügt, weil dieser Inhaber der Ansicht vorübergehend ist und später ersetzt wird.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
- Importieren Sie
android.widget.TextView
undandroidx.recyclerview.widget.RecyclerView
, wenn Sie dazu aufgefordert werden.
Schritt 3: SleepNightAdapter erstellen
Die Hauptaufgabe beim Implementieren eines RecyclerView
ist das Erstellen des Adapters. Sie haben einen einfachen Ansichtshalter für die Artikelansicht und ein Layout für jeden Artikel. Sie können jetzt einen Adapter erstellen. Der Adapter erstellt einen Aufrufhalter und füllt ihn mit Daten, die in RecyclerView
angezeigt werden können.
- Erstellen Sie im Paket
sleeptracker
eine neue Kotlin-Klasse mit dem NamenSleepNightAdapter
. - Richten Sie die
SleepNightAdapter
-Klasse umRecyclerView.Adapter
aus. Die Klasse heißtSleepNightAdapter
, da sie einSleepNight
-Objekt in etwas anpasst, dasRecyclerView
verwenden kann. Der Adapter muss wissen, welcher Ansichtshalter verwendet werden soll. Übergeben Sie daherTextItemViewHolder
. Importieren Sie die erforderlichen Komponenten, wenn Sie dazu aufgefordert werden. Im Anschluss wird eine Fehlermeldung angezeigt, weil die Implementierungsmethoden obligatorisch sind.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
- Erstellen Sie auf der obersten Ebene von
SleepNightAdapter
einelistOf
SleepNight
-Variable, in der die Daten gespeichert werden.
var data = listOf<SleepNight>()
- Überschreibe in
SleepNightAdapter
den Wert „getItemCount()
“ für die Größe der Liste der Schlafnächte indata
. DerRecyclerView
muss wissen, wie viele Elemente der Adapter anzeigen kann, indem ergetItemCount()
aufruft.
override fun getItemCount() = data.size
- Überschreibe in „
SleepNightAdapter
“ die Funktion „onBindViewHolder()
“, wie unten gezeigt.
Die FunktiononBindViewHolder()
wird vonRecyclerView
aufgerufen, um die Daten eines Listenelements an der angegebenen Position anzuzeigen. Die MethodeonBindViewHolder()
verwendet daher zwei Argumente: einen Ansichtsinhaber und eine Position der zu bindenden Daten. Für diese App ist der Inhaber derTextItemViewHolder
und die Position die Position in der Liste.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
- Erstellen Sie innerhalb von
onBindViewHolder()
eine Variable für ein Element an einer bestimmten Position in den Daten.
val item = data[position]
- Der von dir erstellte
ViewHolder
hat eine Property namens „textView
“. Lege inonBindViewHolder()
dentext
vontextView
auf die Schlafqualität fest. In diesem Code wird nur eine Liste mit Zahlen angezeigt. In diesem einfachen Beispiel können Sie jedoch sehen, wie der Adapter die Daten in den Eigentümer der Ansicht und auf den Bildschirm übernimmt.
holder.textView.text = item.sleepQuality.toString()
- Überschreiben und implementieren Sie in
SleepNightAdapter
onCreateViewHolder()
, das aufgerufen wird, wennRecyclerView
einen Ansichtshalter zur Darstellung eines Elements benötigt.
Diese Funktion verwendet zwei Parameter und gibt einViewHolder
zurück. Der Parameterparent
, also die Datenansichtsgruppe, die den Inhaber der Ansicht enthält, ist immerRecyclerView
. Der ParameterviewType
wird verwendet, wenn mehrere Ansichten in derselbenRecyclerView
vorhanden sind. Wenn du beispielsweise eine Liste mit Textaufrufen, einem Bild und einem Video in der gleichenRecyclerView
hinzufügst, muss die FunktiononCreateViewHolder()
wissen, welche Art von Ansicht sie verwenden möchten.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
- Erstellen Sie in
onCreateViewHolder()
eine Instanz vonLayoutInflater
.
Der Layout-Inflation kann Aufrufe aus XML-Layouts erstellen. Dascontext
enthält Informationen zum korrekten Erhöhen der Ansicht. In einem Adapter für eine Recycler-Ansicht übergeben Sie immer den Kontext derparent
-Datenansichtsgruppe, also derRecyclerView
.
val layoutInflater = LayoutInflater.from(parent.context)
- Erstellen Sie in
onCreateViewHolder()
dasview
, indem Sie denlayoutinflater
bitten, den Wert zu erhöhen.
Übergeben Sie das XML-Layout für die Ansicht und die Ansichtsgruppeparent
für die Ansicht. Das dritte, boolesche Argument istattachToRoot
. Dieses Argument muss auffalse
gesetzt sein, daRecyclerView
dieses Element der Ansichtshierarchie für Sie hinzufügt, wenn es Zeit ist.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView
- Gibt in
onCreateViewHolder()
einTextItemViewHolder
mitview
zurück.
return TextItemViewHolder(view)
- Der Adapter muss
RecyclerView
darüber informieren, wenndata
geändert wurde, weilRecyclerView
nichts über die Daten weiß. Es werden nur die Inhaber der Datenansicht berücksichtigt, die der Adapter an sie weitergibt.
UmRecyclerView
mitzuteilen, dass sich die angezeigten Daten geändert haben, fügen Sie oben in der KlasseSleepNightAdapter
einen benutzerdefinierten Setter zurdata
-Variable hinzu. Geben Sie in der Setterdata
einen neuen Wert ein und rufen Sie dannnotifyDataSetChanged()
auf, um die Neuzeichnung der Liste mit den neuen Daten auszulösen.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}
Schritt 4: RecyclerView über den Adapter informieren
Das Gerät „RecyclerView
“ benötigt Informationen zum Adapter, mit dem sich Halter für die Ansicht anzeigen lassen.
- Öffnen Sie
SleepTrackerFragment.kt
. - Erstellen Sie in
onCreateview()
einen Adapter. Fügen Sie den Code nach der Erstellung desViewModel
-Modells und vor derreturn
-Anweisung ein.
val adapter = SleepNightAdapter()
adapter
mitRecyclerView
verknüpfen.
binding.sleepList.adapter = adapter
- Löschen Sie das Projekt und erstellen Sie es neu, um das
binding
-Objekt zu aktualisieren.
Wenn immer noch Fehler in der Nähe vonbinding.sleepList
oderbinding.FragmentSleepTrackerBinding
angezeigt werden, entwerten Sie die Caches und starten Sie das Gerät neu. Wählen Sie Datei > Caches wiederherstellen / Neu starten aus.
Wenn Sie die App jetzt ausführen, sind keine Fehler aufgetreten, es werden jedoch keine Daten angezeigt, wenn Sie auf Starten und dann auf Beenden tippen.
Schritt 5: Daten in den Adapter laden
Bisher haben Sie einen Adapter und eine Möglichkeit, Daten vom Adapter in die RecyclerView
zu übertragen. Jetzt müssen Sie Daten vom ViewModel
mit dem Adapter abrufen.
- Öffnen Sie
SleepTrackerViewModel
. - Suchen Sie die Variable
nights
, in der alle Schlafnächte gespeichert werden. Das sind die Daten, die angezeigt werden sollen. Die Variablenights
wird durch Aufrufen vongetAllNights()
für die Datenbank festgelegt. - Entfernen Sie
private
ausnights
, da Sie einen Beobachter erstellen, der Zugriff auf diese Variable benötigt. Die Erklärung sollte so aussehen:
val nights = database.getAllNights()
- Öffnen Sie im Paket
database
dasSleepDatabaseDao
. - Suchen Sie die Funktion
getAllNights()
. Diese Funktion gibt eine Liste vonSleepNight
-Werten alsLiveData
zurück. Das bedeutet, dass die Variablenights
LiveData
enthält, die vonRoom
aktualisiert wird, und du kannst dirnights
ansehen, wenn es geändert wird. - Öffnen Sie
SleepTrackerFragment
. - Erstellen Sie in
onCreateView()
unter der Erstellung vonadapter
einen Beobachter für dienights
-Variable.
Wenn du das FragmentviewLifecycleOwner
als Lebenszyklusinhaber angibst, kann dieser Beobachter nur aktiv sein, wenn sich derRecyclerView
auf dem Bildschirm befindet.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})
- Wenn Sie innerhalb des Observers einen Wert ermitteln, der nicht null ist (bei
nights
), weisen Sie den Wert dem Adapterdata
zu. Dies ist der abgeschlossene Code für den Observer und das Festlegen der Daten:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})
- Erstellen Sie Ihren Code und führen Sie ihn aus.
Wenn dein Adapter funktioniert, werden dir die Zahlen für die Schlafqualität als Liste angezeigt. Im Screenshot links wird „-1“ angezeigt, nachdem Sie auf Starten getippt haben. Der Screenshot rechts zeigt die aktualisierte Zahl für die Schlafqualität, nachdem Sie auf Stopp getippt und eine Qualitätsbewertung ausgewählt haben.
Schritt 6: Verwendung von recycelten View-Inhabern
RecyclerView
recycelt Aufrufhalter, d. h., sie werden wiederverwendet. Wenn eine Ansicht vom Bildschirm scrollt, wird sie von RecyclerView
wiederverwendet, um durch die Ansicht zu scrollen.
Da diese Ansichtshalter recycelt werden, muss onBindViewHolder()
alle Anpassungen, die vorherige Elemente einer Ansichtshalter vornehmen konnten, festlegen oder zurücksetzen.
So könnten Sie zum Beispiel die Textfarbe in Sichtbehältern auf Rot legen, wenn die Qualitätsfreigaben kleiner oder gleich 1 sind und schlechten Schlaf darstellen.
- Fügen Sie in der Klasse
SleepNightAdapter
den folgenden Code am Ende vononBindViewHolder()
ein.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}
- Führen Sie die App aus.
- Füge Daten zur Schlafqualität hinzu, und die Zahl ist rot.
- Füge hohe Bewertungen für die Schlafqualität hinzu, bis auf dem Bildschirm eine rote hohe Zahl angezeigt wird.
WährendRecyclerView
die Aufrufer wiederverwendet, wird letztendlich einer der roten Halter für eine hohe Bewertung wiederverwendet. Die hohe Bewertung wird fälschlicherweise rot angezeigt.
- Fügen Sie eine
else
-Anweisung hinzu, um die Farbe auf „Schwarz“ zu setzen, wenn die Qualität mindestens so hoch ist wie der Wert in ein Schwarz.
Wenn beide Bedingungen explizit sind, verwendet der Ansichtshalter die richtige Textfarbe für jedes Element.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}
- Wenn du die App ausführst, sollten die Zahlen immer die richtige Farbe haben.
Glückwunsch! Sie haben jetzt eine voll funktionsfähige Basis-RecyclerView
.
Für diese Aufgabe ersetzen Sie den einfachen Ansichtshalter durch einen, der mehr Daten für eine Nacht anzeigen kann.
Die einfache ViewHolder
, die du Util.kt
hinzugefügt hast, bricht einen TextView
in einem TextItemViewHolder
ein.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
Warum verwendet RecyclerView
nicht einfach direkt ein TextView
-Objekt? Diese eine Zeile bietet zahlreiche Funktionen. Ein ViewHolder
beschreibt eine Elementansicht und zugehörige Metadaten innerhalb des RecyclerView
. RecyclerView
stützt sich auf diese Funktion, um die Ansicht beim Scrollen der Liste korrekt zu platzieren, und um interessante Dinge zu erledigen, wie z. B. Animieren von Ansichten, wenn Elemente hinzugefügt oder entfernt werden Adapter
.
Wenn RecyclerView
auf die in ViewHolder
gespeicherten Ansichten zugreifen muss, kann dies die Property itemView
des Ansichtsinhabers sein. RecyclerView
verwendet itemView
, wenn ein Element zur Anzeige auf dem Bildschirm verknüpft, als Teil eines Rahmens wie ein Rahmen gezeichnet wird und die Barrierefreiheit implementiert wird.
Schritt 1: Elementlayout erstellen
In diesem Schritt erstellen Sie die Layoutdatei für ein Element. Das Layout besteht aus einem ConstraintLayout
mit einem ImageView
für die Schlafqualität, einem TextView
für die Schlafdauer und einem TextView
für die Qualität als Text. Da Sie bereits Layouts erstellt haben, können Sie den bereitgestellten XML-Code kopieren und einfügen.
- Erstellen Sie eine neue Layoutressourcendatei und nennen Sie sie
list_item_sleep_night
. - Ersetzen Sie den gesamten Code in der Datei durch den folgenden Code. Sehen Sie sich das soeben erstellte Layout an.
<?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>
- Wechseln Sie in Android Studio zum Tab Design. In der Designansicht sieht das Layout wie im Screenshot unten links aus. In der Entwurfsansicht sehen Sie den Screenshot rechts.
Schritt 2: ViewHolder erstellen
- Öffnen Sie
SleepNightAdapter.kt
. - Erstellen Sie innerhalb der
SleepNightAdapter
eine Klasse namensViewHolder
und erweitern Sie sieRecyclerView.ViewHolder
.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
- In
ViewHolder
erhältst du Verweise auf die Ansichten. Du benötigst einen Verweis auf die Ansichten, die von dieserViewHolder
aktualisiert werden. Jedes Mal, wenn Sie diesenViewHolder
binden, müssen Sie auf das Bild und beide Textansichten zugreifen. (Sie wandeln diesen Code um, um die Datenbindung später zu verwenden.)
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)
Schritt 3: ViewHolder in SleepNightAdapter verwenden
- Verwenden Sie in der
SleepNightAdapter
-Definition anstelle vonTextItemViewHolder
die soeben erstellteSleepNightAdapter.ViewHolder
.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {
Aktualisieren Sie onCreateViewHolder()
:
- Ändern Sie die Signatur von
onCreateViewHolder()
, umViewHolder
zurückzugeben. - Ändere den Layout-Inflator, um die richtige Layout-Ressource zu verwenden,
list_item_sleep_night
. - Entferne die Übertragung zu
TextView
. - Statt ein
TextItemViewHolder
-Element zurückzugeben, wird einViewHolder
-Element zurückgegeben.
Hier ist die aktualisierte FunktiononCreateViewHolder()
:
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)
}
Aktualisieren Sie onBindViewHolder()
:
- Ändern Sie die Signatur von
onBindViewHolder()
so, dass der Parameterholder
einViewHolder
stattTextItemViewHolder
ist. - Löschen Sie innerhalb der Datei
onBindViewHolder()
den gesamten Code, mit Ausnahme der Definition vonitem
. - Definieren Sie einen
val
-res
, der einen Verweis auf denresources
für diese Ansicht enthält.
val res = holder.itemView.context.resources
- Text für die Textansicht „
sleepLength
“ auf die Dauer festlegen. Kopieren Sie den Code unten. Dadurch wird eine Formatierungsfunktion aufgerufen, die mit dem Startcode bereitgestellt wird.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
- Dabei wird ein Fehler ausgegeben, weil
convertDurationToFormatted()
definiert werden muss. Öffnen SieUtil.kt
und entfernen Sie den Kommentar für den Code und die zugehörigen Importe. Wählen Sie Code > Kommentar mit Zeilenkommentaren aus. - Gehe zurück zu
onBindViewHolder()
und verwendeconvertNumericQualityToString()
, um die Qualität festzulegen.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
- Möglicherweise müssen Sie diese Funktionen manuell importieren.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
- Legen Sie das richtige Symbol für die Qualität fest. Im Startcode finden Sie das neue
ic_sleep_active
-Symbol.
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
})
- Dies ist die fertige aktualisierte
onBindViewHolder()
-Funktion, mit der alle Daten fürViewHolder
festgelegt werden:
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
})
}
- Führe deine App aus. Dein Display sollte wie der Screenshot unten aussehen, auf dem das Symbol für die Schlafqualität zusammen mit dem Text für die Schlafdauer und die Schlafqualität angezeigt wird.
Ihr RecyclerView
ist jetzt vollständig. Sie haben nun eine Adapter
und eine ViewHolder
implementiert und sie zusammengefügt, um eine Liste mit einer RecyclerView
Adapter
aufzurufen.
Der bisherige Code für die Erstellung eines Adapters und eines Aufrufhalters wird in Ihrem Code angezeigt. Sie können diesen Code jedoch verbessern. Der anzuzeigende Code und der Code zum Verwalten von Ansichtsinhabern sind fehlerhaft und onBindViewHolder()
weiß, wie er die ViewHolder
aktualisieren muss.
In einer Produktions-App kann es vorkommen, dass mehrere Halter für die Ansicht, komplexere Adapter und mehrere Entwickler Änderungen vornehmen. Strukturieren Sie Ihren Code so, dass sich nur ein Aufrufer in der Nähe des Aufrufers befindet.
Schritt 1: onBindViewHolder() refaktorieren
In diesem Schritt refaktorieren Sie den Code und verschieben alle Funktionen des Aufrufers in ViewHolder
. Ziel dieser Refaktorierung ist nicht, das Aussehen der App für die Nutzer zu verändern, sondern Entwicklern die Arbeit am Code zu erleichtern und sicherer zu machen. Glücklicherweise gibt es in Android Studio Tools, die Ihnen dabei helfen.
- Wählen Sie in
SleepNightAdapter
unteronBindViewHolder()
alles außer der Anweisung aus, um die Variableitem
zu deklarieren. - Klicken Sie mit der rechten Maustaste und wählen Sie Refaktorieren > Extrahieren > Funktion aus.
- Benennen Sie die Funktion mit
bind
und akzeptieren Sie die vorgeschlagenen Parameter. Klicken Sie auf OK.
Die Funktionbind()
befindet sich unteronBindViewHolder()
.
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
})
}
- Setzen Sie den Cursor auf das Wort
holder
des Parametersholder
vonbind()
. Drücken SieAlt+Enter
(Option+Enter
auf einem Mac), um das Intent-Menü zu öffnen. Wählen Sie Parameter in Empfänger umwandeln aus, um dies in eine Erweiterungsfunktion mit der folgenden Signatur zu konvertieren:
private fun ViewHolder.bind(item: SleepNight) {...}
- Fügen Sie die
bind()
-Funktion aus und fügen Sie sie inViewHolder
ein. - „
bind()
“ öffentlich machen. - Importieren Sie
bind()
in den Adapter, falls erforderlich. - Da es sich jetzt in der
ViewHolder
befindet, kannst du denViewHolder
-Teil der Signatur entfernen. Hier ist der endgültige Code für die Funktionbind()
in der KlasseViewHolder
.
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
})
}
Schritt 2: ReCreateonHolder-Refaktorierung durchführen
Die Methode onCreateViewHolder()
im Adapter erhöht die Ansicht derzeit aus der Layoutressource für den ViewHolder
. Allerdings hat die Inflation nichts mit dem Adapter und alles mit dem ViewHolder
zu tun. Die Steigerung sollte in der ViewHolder
erfolgen.
- Wählen Sie in
onCreateViewHolder()
den gesamten Code im Text der Funktion aus. - Klicken Sie mit der rechten Maustaste und wählen Sie Refaktorieren > Extrahieren > Funktion aus.
- Benennen Sie die Funktion mit
from
und akzeptieren Sie die vorgeschlagenen Parameter. Klicken Sie auf OK. - Setzen Sie den Cursor auf den Funktionsnamen
from
. Drücken SieAlt+Enter
(Option+Enter
auf einem Mac), um das Intent-Menü zu öffnen. - Wählen Sie Zu Companion-Objekt verschieben aus. Die
from()
-Funktion muss sich in einem Companion-Objekt befinden, damit sie in derViewHolder
-Klasse aufgerufen werden kann, nicht bei einerViewHolder
-Instanz. - Verschiebe das
companion
-Objekt in dieViewHolder
-Klasse. - „
from()
“ öffentlich machen. - Ändern Sie in
onCreateViewHolder()
diereturn
-Anweisung, umfrom()
nach dem Aufrufen vonfrom()
in der KlasseViewHolder
zurückzugeben.
Ihre abgeschlossenen MethodenonCreateViewHolder()
undfrom()
sollten wie unten dargestellt aussehen und der Code sollte ohne Fehler erstellt und ausgeführt werden.
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)
}
}
- Ändere die Signatur der Klasse
ViewHolder
so, dass der Konstruktor privat ist. Dafrom()
jetzt eine Methode ist, die eine neueViewHolder
-Instanz zurückgibt, kann jeder den Konstruktor vonViewHolder
nicht mehr aufrufen.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
- Führen Sie die Anwendung aus. Ihre Anwendung sollte wie zuvor erstellt und ausgeführt werden, was nach dem Refaktorieren das gewünschte Ergebnis ist.
Android Studio-Projekt: RecyclerViewFundamentals
- Das Anzeigen einer Liste oder ein Raster von Daten ist eine der häufigsten Aufgaben auf der Android-Benutzeroberfläche.
RecyclerView
ist darauf ausgelegt, auch bei extrem großen Listen effizient zu sein. RecyclerView
führt nur die Schritte aus, die erforderlich sind, um Elemente zu verarbeiten oder zu zeichnen, die auf dem Bildschirm sichtbar sind.- Wenn ein Element vom Bildschirm scrollt, werden die Aufrufe recycelt. Das heißt, das Element wird mit neuem Content gefüllt, der auf dem Bildschirm scrollt.
- Das Adaptive-Muster in Software Engineering hilft einem Objekt dabei, mit einer anderen API zusammenzuarbeiten. Die App-Daten werden von
RecyclerView
mithilfe eines Adapters in eine Anzeige umgewandelt, ohne dass sich die Art und Weise ändert, wie Daten in der App gespeichert und verarbeitet werden.
Um Ihre Daten in einer RecyclerView
anzuzeigen, benötigen Sie die folgenden Teile:
- RecyclerView
Definieren Sie zum Erstellen einer Instanz vonRecyclerView
in der Layoutdatei ein<RecyclerView>
-Element. - LayoutManager
EinRecyclerView
verwendet einLayoutManager
, um das Layout der Elemente in derRecyclerView
zu organisieren, beispielsweise in einem Raster oder in einer linearen Liste.
Legen Sie in der<RecyclerView>
-Layoutdatei das<RecyclerView>
-Attribut auf den Layoutmanager fest (z. B.LinearLayoutManager
oderGridLayoutManager
).
Sie könnenLayoutManager
auch für einRecyclerView
programmatisch festlegen. Diese Technik wird in einem späteren Codelab behandelt. - Layout für jedes Element
Erstellen Sie ein Layout für ein Datenelement in einer XML-Layoutdatei. - Adapter
Erstellen Sie einen Adapter, um die Daten vorzubereiten und sie in einemViewHolder
anzuzeigen. Verbinde den Adapter mit demRecyclerView
.
WennRecyclerView
ausgeführt wird, ermittelt der Adapter, wie die Daten auf dem Bildschirm angezeigt werden.
Für den Adapter müssen die folgenden Methoden implementiert werden:
–getItemCount()
, um die Anzahl der Elemente zurückzugeben.
–onCreateViewHolder()
, um dieViewHolder
für ein Element in der Liste zurückzugeben.
–onBindViewHolder()
, um die Daten an die Ansichten eines Elements in der Liste anzupassen. - ViewHolder
EinViewHolder
enthält die Ansichtsinformationen für ein Element aus dem Element. - Mit der Methode
onBindViewHolder()
im Adapter werden die Daten an die Ansichten angepasst. Sie überschreiben diese Methode immer. Normalerweise erhöhtonBindViewHolder()
das Layout eines Elements und fügt die Daten in die Ansichten im Layout ein. - Da die
RecyclerView
nichts über die Daten weiß, muss dieAdapter
dieRecyclerView
informieren, wenn sich diese Daten ändern. MitnotifyDataSetChanged()
kannst duAdapter
benachrichtigen, dass sich die Daten geändert haben.
Udacity-Kurs:
Android-Entwicklerdokumentation:
In diesem Abschnitt werden mögliche Hausaufgaben für Schüler oder Studenten aufgeführt, die an diesem von einem Kursleiter geleiteten Codelab arbeiten. Die Lehrkraft kann Folgendes tun:
- Bei Bedarf können Sie die entsprechenden Aufgaben zuweisen.
- Schülern mitteilen, wie sie Aufgaben für die Aufgabe abgeben
- Benoten Sie die Hausaufgaben.
Lehrkräfte können diese Vorschläge so oft oder so oft verwenden, wie sie möchten. anderen Aufgaben können sie nach Belieben zugewiesen werden.
Wenn Sie alleine an diesem Codelab arbeiten, können Sie Ihr Wissen mit diesen Hausaufgaben testen.
Diese Fragen beantworten
Frage 1
Wie zeigt RecyclerView
Elemente an? Wählen Sie alle zutreffenden Antworten aus.
▢ Zeigt Elemente in einer Liste oder einem Raster an.
▢ Scrollt vertikal oder horizontal.
▢ Auf größeren Geräten wie Tablets diagonal scrollt.
▢: Benutzerdefinierte Layouts werden zugelassen, wenn eine Liste oder ein Raster für den Anwendungsfall nicht ausreicht.
Frage 2
Welche Vorteile bietet die Nutzung von RecyclerView
? Wählen Sie alle zutreffenden Antworten aus.
▢ Zeigt große Listen effizient an.
▢ Aktualisiert die Daten automatisch.
▢. Ein Element wird nicht aktualisiert, wenn es aktualisiert, gelöscht oder der Liste hinzugefügt wird.
▢ Dadurch wird die Ansicht wiederverwendet, bei der vom Bildschirm gescrollt wird, um das nächste Element auf dem Bildschirm zu sehen.
Frage 3
Was sind die Gründe für die Verwendung von Adaptern? Wählen Sie alle zutreffenden Antworten aus.
▢ Dank der Trennung von Bedenken ist es einfacher, Code zu ändern und zu testen.
▢ RecyclerView
ist unabhängig von den angezeigten Daten.
▢ Datenverarbeitungsschichten müssen sich nicht darum kümmern, wie Daten angezeigt werden.
▢ Die App wird schneller ausgeführt.
Frage 4
Welche der folgenden Aussagen trifft auf ViewHolder
zu? Wählen Sie alle zutreffenden Antworten aus.
▢ Das ViewHolder
-Layout ist in XML-Layoutdateien definiert.
▢ Es gibt eine ViewHolder
für jede Dateneinheit im Dataset.
▢ In einem RecyclerView
können mehrere ViewHolder
ausgeführt werden.
▢ Mit dem Adapter
werden Daten an die ViewHolder
gebunden.
Starten Sie die nächste Lektion: