Grundlagen von Android und Kotlin 07.2: DiffUtil und Datenbindung mit RecyclerView

Dieses Codelab ist Teil des Kurses „Grundlagen von Android und Kotlin“. Sie können diesen Kurs am besten nutzen, wenn Sie die Codelabs der Reihe nach durcharbeiten. Alle Codelabs des Kurses sind auf der Landingpage für Codelabs zu den Grundlagen von Android und Kotlin aufgeführt.

Einführung

Im vorherigen Codelab haben Sie die App „TrackMySleepQuality“ so aktualisiert, dass Daten zur Schlafqualität in einem RecyclerView angezeigt werden. Die Techniken, die Sie beim Erstellen Ihres ersten RecyclerView gelernt haben, reichen für die meisten RecyclerViews aus, in denen einfache, nicht zu lange Listen angezeigt werden. Es gibt jedoch eine Reihe von Techniken, mit denen RecyclerView für große Listen effizienter wird und mit denen sich Ihr Code für komplexe Listen und Tabellen leichter warten und erweitern lässt.

In diesem Codelab bauen Sie auf der Sleep-Tracker-App aus dem vorherigen Codelab auf. Sie lernen eine effektivere Methode zum Aktualisieren der Liste der Schlafdaten kennen und erfahren, wie Sie die Datenbindung mit RecyclerView verwenden. Wenn Sie die App aus dem vorherigen Codelab nicht haben, können Sie den Startcode für dieses Codelab herunterladen.

Was Sie bereits wissen sollten

  • Eine einfache Benutzeroberfläche mit einer Aktivität, Fragmenten und Ansichten erstellen
  • Zwischen Fragmenten wechseln und safeArgs verwenden, um Daten zwischen Fragmenten zu übergeben.
  • Modelle, Modell-Factories, Transformationen und LiveData sowie deren Beobachter ansehen
  • So erstellen Sie eine Room-Datenbank, ein DAO und definieren Entitäten.
  • Verwendung von Coroutinen für Datenbanken und andere lang andauernde Aufgaben.
  • So implementieren Sie ein einfaches RecyclerView mit einem Adapter, einem ViewHolder und einem Elementlayout.

Lerninhalte

  • So verwenden Sie DiffUtil, um eine von RecyclerView angezeigte Liste effizient zu aktualisieren.
  • So verwenden Sie die Datenbindung mit RecyclerView.
  • Bindungsadapter zum Transformieren von Daten verwenden

Aufgaben

  • In diesem Codelab bauen Sie auf der App „TrackMySleepQuality“ aus dem vorherigen Codelab dieser Reihe auf.
  • Aktualisieren Sie SleepNightAdapter, um die Liste effizient mit DiffUtil zu aktualisieren.
  • Implementieren Sie die Datenbindung für RecyclerView und verwenden Sie Bindungsadapter, um die Daten zu transformieren.

Die Schlaftracking-App hat zwei Bildschirme, die durch Fragmente dargestellt werden, wie in der Abbildung unten zu sehen ist.

Auf dem ersten Bildschirm, der links angezeigt wird, befinden sich Schaltflächen zum Starten und Beenden des Trackings. Auf dem Display werden einige der Schlafdaten des Nutzers angezeigt. Mit der Schaltfläche Löschen werden alle Daten, die die App für den Nutzer erhoben hat, dauerhaft gelöscht. Auf dem zweiten Bildschirm rechts können Sie eine Bewertung der Schlafqualität auswählen.

Diese App ist so konzipiert, dass sie einen UI-Controller, ViewModel und LiveData sowie eine Room-Datenbank zum Speichern von Schlafdaten verwendet.

Die Schlafdaten werden in einem RecyclerView angezeigt. In diesem Codelab erstellen Sie den DiffUtil- und den Data Binding-Teil für RecyclerView. Nach diesem Codelab sieht Ihre App genauso aus wie vorher, ist aber effizienter und lässt sich leichter skalieren und warten.

Sie können die SleepTracker-App aus dem vorherigen Codelab weiter verwenden oder die RecyclerViewDiffUtilDataBinding-Starter-App von GitHub herunterladen.

  1. Laden Sie bei Bedarf die RecyclerViewDiffUtilDataBinding-Starter-App von GitHub herunter und öffnen Sie das Projekt in Android Studio.
  2. Starten Sie die App.
  3. Öffnen Sie die Datei SleepNightAdapter.kt.
  4. Sehen Sie sich den Code an, um sich mit der Struktur der App vertraut zu machen. Das Diagramm unten fasst zusammen, wie RecyclerView mit dem Adaptermuster verwendet wird, um dem Nutzer Schlafdauerdaten anzuzeigen.

  • Die App erstellt aus Nutzereingaben eine Liste von SleepNight-Objekten. Jedes SleepNight-Objekt steht für eine einzelne Nacht mit Schlaf, ihre Dauer und Qualität.
  • Mit SleepNightAdapter wird die Liste der SleepNight-Objekte in ein Format umgewandelt, das von RecyclerView verwendet und angezeigt werden kann.
  • Der SleepNightAdapter-Adapter erzeugt ViewHolders, die die Ansichten, Daten und Metainformationen für die RecyclerView enthalten, um die Daten darzustellen.
  • RecyclerView verwendet SleepNightAdapter, um die Anzahl der anzuzeigenden Elemente (getItemCount()) zu ermitteln. RecyclerView verwendet onCreateViewHolder() und onBindViewHolder(), um ViewHolders abzurufen, die an Daten gebunden sind, die angezeigt werden sollen.

Die Methode „notifyDataSetChanged()“ ist ineffizient

Damit RecyclerView weiß, dass sich ein Element in der Liste geändert hat und aktualisiert werden muss, wird im aktuellen Code notifyDataSetChanged() in SleepNightAdapter aufgerufen, wie unten gezeigt.

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

notifyDataSetChanged() teilt RecyclerView jedoch mit, dass die gesamte Liste möglicherweise ungültig ist. Daher werden alle Elemente in der Liste neu gebunden und neu gezeichnet, auch Elemente, die nicht auf dem Bildschirm sichtbar sind.RecyclerView Das ist viel unnötige Arbeit. Bei großen oder komplexen Listen kann dieser Vorgang so lange dauern, dass das Display flackert oder ruckelt, wenn der Nutzer durch die Liste scrollt.

Um dieses Problem zu beheben, können Sie RecyclerView genau mitteilen, was sich geändert hat. RecyclerView kann dann nur die Ansichten aktualisieren, die sich auf dem Bildschirm geändert haben.

RecyclerView bietet eine umfangreiche API zum Aktualisieren einzelner Elemente. Mit notifyItemChanged() können Sie RecyclerView mitteilen, dass sich ein Element geändert hat. Ähnliche Funktionen gibt es für Elemente, die hinzugefügt, entfernt oder verschoben werden. Sie könnten alles manuell erledigen, aber das wäre nicht einfach und würde wahrscheinlich viel Code erfordern.

Glücklicherweise gibt es eine bessere Lösung.

DiffUtil ist effizient und nimmt Ihnen die Arbeit ab

RecyclerView hat eine Klasse namens DiffUtil, mit der die Unterschiede zwischen zwei Listen berechnet werden. DiffUtil vergleicht eine alte und eine neue Liste und ermittelt die Unterschiede. Es werden Elemente gefunden, die hinzugefügt, entfernt oder geändert wurden. Dazu wird ein Algorithmus namens Eugene W. Myers-Differenzalgorithmus, um die Mindestanzahl der Änderungen zu ermitteln, die an der alten Liste vorgenommen werden müssen, um die neue Liste zu erstellen.

Sobald DiffUtil herausgefunden hat, was sich geändert hat, kann RecyclerView diese Informationen verwenden, um nur die Elemente zu aktualisieren, die geändert, hinzugefügt, entfernt oder verschoben wurden. Das ist viel effizienter, als die gesamte Liste neu zu erstellen.

In dieser Aufgabe aktualisieren Sie SleepNightAdapter, um DiffUtil zu verwenden und RecyclerView für Änderungen an den Daten zu optimieren.

Schritt 1: SleepNightDiffCallback implementieren

Wenn Sie die Funktionalität der Klasse DiffUtil verwenden möchten, erweitern Sie DiffUtil.ItemCallback.

  1. Öffnen Sie SleepNightAdapter.kt.
  2. Erstellen Sie unter der vollständigen Klassendefinition für SleepNightAdapter eine neue Klasse der obersten Ebene mit dem Namen SleepNightDiffCallback, die DiffUtil.ItemCallback erweitert. Übergeben Sie SleepNight als generischen Parameter.
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
  1. Setzen Sie den Cursor auf den Klassennamen SleepNightDiffCallback.
  2. Drücke Alt+Enter (Option+Enter auf dem Mac) und wähle Mitglieder implementieren aus.
  3. Wählen Sie im daraufhin geöffneten Dialogfeld die Methoden areItemsTheSame() und areContentsTheSame() aus, indem Sie bei gedrückter Umschalttaste auf die Methoden klicken, und klicken Sie dann auf OK.

    Dadurch werden in SleepNightDiffCallback Stubs für die beiden Methoden generiert, wie unten dargestellt. DiffUtil verwendet diese beiden Methoden, um herauszufinden, wie sich die Liste und die Elemente geändert haben.
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
  1. Ersetzen Sie in areItemsTheSame() die TODO durch Code, der prüft, ob die beiden übergebenen SleepNight-Elemente oldItem und newItem identisch sind. Wenn die Elemente denselben nightId haben, handelt es sich um dasselbe Element. Gib also true zurück. Andernfalls wird false zurückgegeben. DiffUtil verwendet diesen Test, um festzustellen, ob ein Element hinzugefügt, entfernt oder verschoben wurde.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem.nightId == newItem.nightId
}
  1. Prüfen Sie in areContentsTheSame(), ob oldItem und newItem dieselben Daten enthalten, d. h., ob sie gleich sind. Bei dieser Gleichheitsprüfung werden alle Felder geprüft, da SleepNight eine Datenklasse ist. Data-Klassen definieren automatisch equals und einige andere Methoden für Sie. Wenn es Unterschiede zwischen oldItem und newItem gibt, wird DiffUtil durch diesen Code darüber informiert, dass das Element aktualisiert wurde.
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

Es ist ein gängiges Muster, eine RecyclerView zu verwenden, um eine Liste anzuzeigen, die sich ändert. RecyclerView bietet eine Adapterklasse, ListAdapter, mit der Sie einen RecyclerView-Adapter erstellen können, der auf einer Liste basiert.

ListAdapter verfolgt die Liste für Sie und benachrichtigt den Adapter, wenn die Liste aktualisiert wird.

Schritt 1: Adapter so ändern, dass er ListAdapter erweitert

  1. Ändern Sie in der Datei SleepNightAdapter.kt die Klassensignatur von SleepNightAdapter, sodass sie ListAdapter erweitert.
  2. Importieren Sie bei Aufforderung androidx.recyclerview.widget.ListAdapter.
  3. Fügen Sie SleepNight als erstes Argument zu ListAdapter vor SleepNightAdapter.ViewHolder hinzu.
  4. Fügen Sie SleepNightDiffCallback() als Parameter zum Konstruktor hinzu. ListAdapter verwendet diese Informationen, um herauszufinden, was sich in der Liste geändert hat. Die fertige SleepNightAdapter-Klassensignatur sollte so aussehen:
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. Löschen Sie in der Klasse SleepNightAdapter das Feld data, einschließlich des Setters. Sie benötigen sie nicht mehr, da ListAdapter die Liste für Sie im Blick behält.
  2. Löschen Sie die Überschreibung von getItemCount(), da ListAdapter diese Methode für Sie implementiert.
  3. Um den Fehler in onBindViewHolder() zu beheben, ändern Sie die Variable item. Rufen Sie anstelle von data zum Abrufen eines item die Methode getItem(position) auf, die von ListAdapter bereitgestellt wird.
val item = getItem(position)

Schritt 2: Mit submitList() die Liste auf dem neuesten Stand halten

Ihr Code muss ListAdapter mitteilen, wann eine geänderte Liste verfügbar ist. ListAdapter bietet eine Methode namens submitList(), um ListAdapter mitzuteilen, dass eine neue Version der Liste verfügbar ist. Wenn diese Methode aufgerufen wird, vergleicht ListAdapter die neue Liste mit der alten und erkennt Elemente, die hinzugefügt, entfernt, verschoben oder geändert wurden. Anschließend aktualisiert ListAdapter die von RecyclerView angezeigten Elemente.

  1. Öffnen Sie SleepTrackerFragment.kt.
  2. Suchen Sie in onCreateView() im Observer für sleepTrackerViewModel nach dem Fehler, in dem auf die gelöschte Variable data verwiesen wird.
  3. Ersetzen Sie adapter.data = it durch einen Aufruf von adapter.submitList(it). Der aktualisierte Code ist unten zu sehen.

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})
  1. Führen Sie Ihre App aus. Sie wird schneller ausgeführt, was bei einer kleinen Liste möglicherweise nicht auffällt.

In dieser Aufgabe verwenden Sie dieselbe Technik wie in früheren Codelabs, um die Datenbindung einzurichten, und entfernen Aufrufe von findViewById().

Schritt 1: Datenbindung zur Layoutdatei hinzufügen

  1. Öffnen Sie die Layoutdatei list_item_sleep_night.xml auf dem Tab Text.
  2. Setzen Sie den Cursor auf das Tag ConstraintLayout und drücken Sie Alt+Enter (Option+Enter auf einem Mac). Das Intention-Menü (das Menü für schnelle Korrekturen) wird geöffnet.
  3. Wählen Sie In Layout für die Datenbindung konvertieren aus. Dadurch wird das Layout in <layout> eingeschlossen und ein <data>-Tag eingefügt.
  4. Scrollen Sie bei Bedarf wieder nach oben und deklarieren Sie innerhalb des <data>-Tags eine Variable mit dem Namen sleep.
  5. Machen Sie type zum vollständig qualifizierten Namen von SleepNight, com.example.android.trackmysleepquality.database.SleepNight. Das fertige <data>-Tag sollte so aussehen:
   <data>
        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight"/>
    </data>
  1. Wenn Sie die Erstellung des Binding-Objekts erzwingen möchten, wählen Sie Build > Clean Project (Erstellen > Projekt bereinigen) und dann Build > Rebuild Project (Erstellen > Projekt neu erstellen) aus. Wenn Sie weiterhin Probleme haben, wählen Sie Datei > Caches ungültig machen / Neu starten aus. Das ListItemSleepNightBinding-Bindungsobjekt wird zusammen mit dem zugehörigen Code den generierten Dateien des Projekts hinzugefügt.

Schritt 2: Elementlayout mithilfe der Datenbindung aufblähen

  1. Öffnen Sie SleepNightAdapter.kt.
  2. Suchen Sie in der Klasse ViewHolder nach der Methode from().
  3. Löschen Sie die Deklaration der Variablen view.

Code zum Löschen:

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)
  1. Definieren Sie dort, wo sich die Variable view befand, eine neue Variable namens binding, die das ListItemSleepNightBinding-Bindungsobjekt aufbläht, wie unten dargestellt. Importieren Sie das Binding-Objekt.
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
  1. Geben Sie am Ende der Funktion anstelle von view binding zurück.
return ViewHolder(binding)
  1. Um den Fehler zu beheben, bewegen Sie den Cursor auf das Wort binding. Drücken Sie Alt+Enter (Option+Enter auf einem Mac), um das Intention-Menü zu öffnen.
  1. Wählen Sie Change parameter 'itemView' type of primary constructor of class 'ViewHolder' to 'ListItemSleepNightBinding' (Ändere den Parametertyp „itemView“ des primären Konstruktors der Klasse „ViewHolder“ in „ListItemSleepNightBinding“) aus. Dadurch wird der Parametertyp der Klasse ViewHolder aktualisiert.

  1. Scrollen Sie nach oben zur Klassendefinition von ViewHolder, um die Änderung an der Signatur zu sehen. Sie sehen einen Fehler für itemView, weil Sie itemView in der Methode from() in binding geändert haben.

    Klicken Sie in der Klassendefinition ViewHolder mit der rechten Maustaste auf eine der Instanzen von itemView und wählen Sie Refactor > Rename aus. Ändern Sie den Namen in binding.
  2. Stellen Sie dem Konstruktorparameter binding das Präfix val voran, um ihn zu einer Property zu machen.
  3. Ändern Sie im Aufruf der übergeordneten Klasse RecyclerView.ViewHolder den Parameter von binding zu binding.root. Sie müssen ein View übergeben. binding.root ist das Stamm-ConstraintLayout in Ihrem Artikellayout.
  4. Ihre fertige Klassendeklaration sollte so aussehen:
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

Außerdem wird ein Fehler für die Aufrufe von findViewById() angezeigt. Diesen beheben Sie als Nächstes.

Schritt 3: findViewById() ersetzen

Sie können jetzt die Attribute sleepLength, quality und qualityImage aktualisieren, um das Objekt binding anstelle von findViewById() zu verwenden.

  1. Ändern Sie die Initialisierungen von sleepLength, qualityString und qualityImage, sodass die Ansichten des binding-Objekts verwendet werden, wie unten gezeigt. Danach sollten keine Fehler mehr in Ihrem Code angezeigt werden.
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage

Wenn das Bindungsobjekt vorhanden ist, müssen Sie die Attribute sleepLength, quality und qualityImage nicht mehr definieren. DataBinding speichert die Suchvorgänge im Cache, sodass diese Eigenschaften nicht deklariert werden müssen.

  1. Klicken Sie mit der rechten Maustaste auf die Attributnamen sleepLength, quality und qualityImage. Wählen Sie Refactor > Inline aus oder drücken Sie Control+Command+N (Option+Command+N auf einem Mac).
  2. Führen Sie Ihre App aus. Wenn Fehler auftreten, müssen Sie Ihr Projekt möglicherweise bereinigen und neu erstellen.

In dieser Aufgabe aktualisieren Sie Ihre App, um die Datenbindung mit Bindungsadaptern zu verwenden, um die Daten in Ihren Ansichten festzulegen.

In einem früheren Codelab haben Sie die Klasse Transformations verwendet, um LiveData zu übernehmen und formatierte Strings zu generieren, die in Textansichten angezeigt werden. Wenn Sie jedoch verschiedene oder komplexe Typen binden müssen, können Sie Bindungsadapter bereitstellen, damit die Datenbindung diese Typen verwenden kann. Binding-Adapter nehmen Ihre Daten entgegen und passen sie so an, dass sie für die Datenbindung verwendet werden können, um eine Ansicht zu binden, z. B. Text oder ein Bild.

Sie implementieren drei Binding-Adapter, einen für das Qualitätsbild und einen für jedes Textfeld. Zusammenfassend lässt sich sagen, dass Sie zum Deklarieren eines Binding-Adapters eine Methode definieren, die ein Element und eine Ansicht akzeptiert, und sie mit @BindingAdapter annotieren. Im Methodenkörper implementieren Sie die Transformation. In Kotlin können Sie einen Binding-Adapter als Erweiterungsfunktion für die Ansichtsklasse schreiben, die die Daten empfängt.

Schritt 1: Bindungsadapter erstellen

Beachten Sie, dass Sie in diesem Schritt eine Reihe von Klassen importieren müssen. Diese werden nicht einzeln aufgeführt.

  1. Öffnen Sie SleepNightAdapater.kt.
  2. Suchen Sie in der Klasse ViewHolder nach der Methode bind() und sehen Sie sich noch einmal an, was diese Methode macht. Sie nehmen den Code, mit dem die Werte für binding.sleepLength, binding.quality und binding.qualityImage berechnet werden, und verwenden ihn stattdessen im Adapter. Lassen Sie den Code vorerst so, wie er ist. Sie verschieben ihn in einem späteren Schritt.
  3. Erstellen Sie im Paket sleeptracker eine Datei mit dem Namen BindingUtils.kt und öffnen Sie sie.
  4. Deklarieren Sie eine Erweiterungsfunktion für TextView mit dem Namen setSleepDurationFormatted und übergeben Sie eine SleepNight. Diese Funktion ist Ihr Adapter zum Berechnen und Formatieren der Schlafdauer.
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
  1. Binden Sie die Daten im Text von setSleepDurationFormatted an die Ansicht, wie Sie es in ViewHolder.bind() getan haben. Rufen Sie convertDurationToFormatted() auf und legen Sie dann die text von TextView auf den formatierten Text fest. Da es sich um eine Erweiterungsfunktion für TextView handelt, können Sie direkt auf das Attribut text zugreifen.
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
  1. Um die Datenbindung über diesen Bindungsadapter zu informieren, annotieren Sie die Funktion mit @BindingAdapter.
  2. Diese Funktion ist der Adapter für das Attribut sleepDurationFormatted. Übergeben Sie sleepDurationFormatted also als Argument an @BindingAdapter.
@BindingAdapter("sleepDurationFormatted")
  1. Der zweite Adapter legt die Schlafqualität basierend auf dem Wert in einem SleepNight-Objekt fest. Erstellen Sie eine Erweiterungsfunktion mit dem Namen setSleepQualityString() für TextView und übergeben Sie eine SleepNight.
  2. Binden Sie die Daten im Text an die Ansicht, wie Sie es in ViewHolder.bind() getan haben. Rufe convertNumericQualityToString auf und lege text fest.
  3. Kommentieren Sie die Funktion mit @BindingAdapter("sleepQualityString").
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
  1. Der dritte Binding-Adapter legt das Bild in einer Image-Ansicht fest. Erstellen Sie die Erweiterungsfunktion für ImageView, rufen Sie setSleepImage auf und verwenden Sie den Code aus ViewHolder.bind(), wie unten gezeigt.
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
   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: SleepNightAdapter aktualisieren

  1. Öffnen Sie SleepNightAdapter.kt.
  2. Löschen Sie alles in der bind()-Methode, da Sie jetzt die Datenbindung und Ihre neuen Adapter verwenden können, um diese Aufgabe zu erledigen.
fun bind(item: SleepNight) {
}
  1. Weisen Sie in bind() den Schlaf item zu, da Sie das Bindungsobjekt über Ihren neuen SleepNight informieren müssen.
binding.sleep = item
  1. Fügen Sie darunter binding.executePendingBindings() ein. Dieser Aufruf ist eine Optimierung, mit der die Datenbindung aufgefordert wird, alle ausstehenden Bindungen sofort auszuführen. Es ist immer eine gute Idee, executePendingBindings() aufzurufen, wenn Sie Bindungsadapter in einem RecyclerView verwenden, da dies die Größenanpassung der Ansichten leicht beschleunigen kann.
 binding.executePendingBindings()

Schritt 3: Bindungen zum XML-Layout hinzufügen

  1. Öffnen Sie list_item_sleep_night.xml.
  2. Fügen Sie im ImageView ein app-Attribut mit demselben Namen wie der Binding-Adapter hinzu, der das Bild festlegt. Übergeben Sie die Variable sleep, wie unten gezeigt.

    Diese Eigenschaft stellt über den Adapter die Verbindung zwischen der Ansicht und dem Bindungsobjekt her. Immer wenn auf sleepImage verwiesen wird, werden die Daten aus SleepNight vom Adapter angepasst.
app:sleepImage="@{sleep}"
  1. Wiederholen Sie diesen Schritt für die Textansichten sleep_length und quality_string. Immer wenn auf sleepDurationFormatted oder sleepQualityString verwiesen wird, passen die Adapter die Daten aus SleepNight an.
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
  1. Führen Sie Ihre App aus. Sie funktioniert genau wie zuvor. Die Binding-Adapter übernehmen die gesamte Formatierung und Aktualisierung der Ansichten, wenn sich die Daten ändern. Das vereinfacht ViewHolder und sorgt für eine viel bessere Struktur des Codes als zuvor.

Du hast in den letzten Übungen immer dieselbe Liste angezeigt. Das ist so gewollt, um zu zeigen, dass die Adapter-Schnittstelle viele verschiedene Möglichkeiten bietet, Ihren Code zu strukturieren. Je komplexer Ihr Code ist, desto wichtiger ist es, ihn gut zu strukturieren. In Produktions-Apps werden diese und andere Muster mit RecyclerView verwendet. Alle Muster funktionieren und haben ihre Vorteile. Welche Sie auswählen, hängt davon ab, was Sie entwickeln.

Glückwunsch! Sie sind auf dem besten Weg, RecyclerView auf Android zu beherrschen.

Android Studio-Projekt: RecyclerViewDiffUtilDataBinding.

DiffUtil:

  • RecyclerView hat eine Klasse namens DiffUtil, mit der die Unterschiede zwischen zwei Listen berechnet werden.
  • DiffUtil hat eine Klasse namens ItemCallBack, die Sie erweitern, um den Unterschied zwischen zwei Listen zu ermitteln.
  • In der ItemCallback-Klasse müssen Sie die Methoden areItemsTheSame() und areContentsTheSame() überschreiben.

ListAdapter:

  • Wenn Sie eine kostenlose Listenverwaltung benötigen, können Sie anstelle von RecyclerView.Adapter die Klasse ListAdapter verwenden. Wenn Sie ListAdapter verwenden, müssen Sie jedoch einen eigenen Adapter für andere Layouts schreiben. In diesem Codelab wird gezeigt, wie das geht.
  • Wenn Sie das Intention-Menü in Android Studio öffnen möchten, platzieren Sie den Cursor auf einem beliebigen Codeelement und drücken Sie Alt+Enter (Option+Enter auf einem Mac). Dieses Menü ist besonders hilfreich, um Code umzugestalten und Stubs zum Implementieren von Methoden zu erstellen. Das Menü ist kontextbezogen. Sie müssen den Cursor also genau positionieren, um das richtige Menü aufzurufen.

Datenbindung:

  • Verwenden Sie die Datenbindung im Elementlayout, um Daten an die Ansichten zu binden.

Bindungsadapter:

  • Bisher haben Sie Transformations verwendet, um Strings aus Daten zu erstellen. Wenn Sie Daten unterschiedlicher oder komplexer Typen binden müssen, stellen Sie Bindungsadapter bereit, damit die Datenbindung sie verwenden kann.
  • Um einen Binding-Adapter zu deklarieren, definieren Sie eine Methode, die ein Element und eine Ansicht akzeptiert, und versehen Sie die Methode mit der Annotation @BindingAdapter. In Kotlin können Sie den Binding-Adapter als Erweiterungsfunktion für View schreiben. Übergeben Sie den Namen der Eigenschaft, die vom Adapter angepasst wird. Beispiel:
@BindingAdapter("sleepDurationFormatted")
  • Legen Sie im XML-Layout eine app-Property mit demselben Namen wie der Binding-Adapter fest. Übergeben Sie eine Variable mit den Daten. Beispiel:
.app:sleepDurationFormatted="@{sleep}"

Udacity-Kurse:

Android-Entwicklerdokumentation:

Weitere Ressourcen:

In diesem Abschnitt werden mögliche Hausaufgaben für Schüler und Studenten aufgeführt, die dieses Codelab im Rahmen eines von einem Kursleiter geleiteten Kurses durcharbeiten. Es liegt in der Verantwortung des Kursleiters, Folgendes zu tun:

  • Weisen Sie bei Bedarf Aufgaben zu.
  • Teilen Sie den Schülern/Studenten mit, wie sie Hausaufgaben abgeben können.
  • Benoten Sie die Hausaufgaben.

Lehrkräfte können diese Vorschläge nach Belieben nutzen und auch andere Hausaufgaben zuweisen, die sie für angemessen halten.

Wenn Sie dieses Codelab selbst durcharbeiten, können Sie mit diesen Hausaufgaben Ihr Wissen testen.

Beantworten Sie diese Fragen

Frage 1

Was ist erforderlich, um DiffUtil zu verwenden? Wähle alle zutreffenden Antworten aus.

▢ Erweitern Sie die Klasse ItemCallBack.

▢ Überschreiben areItemsTheSame().

▢ Überschreiben areContentsTheSame().

▢ Mit der Datenbindung können Sie die Unterschiede zwischen Elementen nachverfolgen.

Frage 2

Welche der folgenden Aussagen zu Binding-Adaptern sind richtig?

▢ Ein Binding-Adapter ist eine Funktion, die mit @BindingAdapter annotiert ist.

▢ Mit einem Binding-Adapter können Sie die Datenformatierung vom View-Holder trennen.

▢ Wenn Sie Binding-Adapter verwenden möchten, müssen Sie RecyclerViewAdapter verwenden.

▢ Binding-Adapter sind eine gute Lösung, wenn Sie komplexe Daten transformieren müssen.

Frage 3

Wann sollten Sie Transformations anstelle eines Binding-Adapters verwenden? Wähle alle zutreffenden Antworten aus.

▢ Ihre Daten sind einfach.

▢ Sie formatieren einen String.

▢ Ihre Liste ist sehr lang.

▢ Ihre ViewHolder enthält nur eine Ansicht.

Nächste Lektion: 7.3: GridLayout mit RecyclerView