Grundlagen von Android und Kotlin 02.4: Grundlagen der Datenbindung

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

In früheren Codelabs dieses Kurses haben Sie die Funktion findViewById() verwendet, um Referenzen zu Ansichten abzurufen. Wenn Ihre App komplexe Ansichtshierarchien hat, ist findViewById() ressourcenintensiv und verlangsamt die App, da Android die Ansichtshierarchie durchläuft, beginnend mit dem Stamm, bis die gewünschte Ansicht gefunden wird. Glücklicherweise gibt es eine bessere Lösung.

Um Daten in Ansichten festzulegen, haben Sie String-Ressourcen verwendet und die Daten aus der Aktivität festgelegt. Es wäre effizienter, wenn die Ansicht die Daten kennen würde. Glücklicherweise ist das möglich.

In diesem Codelab erfahren Sie, wie Sie die Datenbindung verwenden, um findViewById() zu vermeiden. Außerdem erfahren Sie, wie Sie mit der Datenbindung direkt über eine Ansicht auf Daten zugreifen.

Was Sie bereits wissen sollten

Sie sollten mit Folgendem vertraut sein:

  • Was eine Aktivität ist und wie Sie eine Aktivität mit einem Layout in onCreate() einrichten.
  • Erstellen einer Textansicht und Festlegen des Texts, der in der Textansicht angezeigt wird.
  • Mit findViewById() eine Referenz auf eine Ansicht abrufen.
  • Erstellen und Bearbeiten eines einfachen XML-Layouts für eine Ansicht.

Lerninhalte

  • So verwenden Sie die Data Binding Library, um ineffiziente Aufrufe von findViewById() zu vermeiden.
  • So greifen Sie direkt über XML auf App-Daten zu.

Aufgaben

  • Ändern Sie eine App so, dass sie anstelle von findViewById() die Datenbindung verwendet und direkt über die Layout-XML-Dateien auf Daten zugreift.

In diesem Codelab beginnen Sie mit der AboutMe-App und ändern sie so, dass sie Data Binding verwendet. Die App sieht danach genauso aus wie vorher.

Das kann die AboutMe App:

  • Wenn der Nutzer die App öffnet, werden ein Name, ein Feld zur Eingabe eines Alias, die Schaltfläche Fertig, ein Sternbild und scrollbarer Text angezeigt.
  • Der Nutzer kann einen Alias eingeben und auf die Schaltfläche Fertig tippen. Das bearbeitbare Feld und die Schaltfläche werden durch eine Textansicht ersetzt, in der der eingegebene Alias angezeigt wird.


Sie können den Code verwenden, den Sie im vorherigen Codelab erstellt haben, oder den AboutMeDataBinding-Starter-Code von GitHub herunterladen.

Im Code, den Sie in früheren Codelabs geschrieben haben, wird die Funktion findViewById() verwendet, um Referenzen auf Ansichten abzurufen.

Jedes Mal, wenn Sie mit findViewById() nach einer Ansicht suchen, nachdem sie erstellt oder neu erstellt wurde, durchläuft das Android-System die Ansichtshierarchie zur Laufzeit, um sie zu finden. Wenn Ihre App nur wenige Aufrufe hat, ist das kein Problem. In Produktions-Apps kann es jedoch Dutzende von Ansichten in einem Layout geben und selbst bei optimalem Design sind verschachtelte Ansichten nicht zu vermeiden.

Stellen Sie sich ein lineares Layout vor, das eine Scrollansicht mit einer Textansicht enthält. Bei einer großen oder tiefen Ansichtshierarchie kann das Suchen nach einer Ansicht so lange dauern, dass die App für den Nutzer spürbar langsamer wird. Das Zwischenspeichern von Ansichten in Variablen kann helfen, aber Sie müssen trotzdem für jede Ansicht in jedem Namespace eine Variable initialisieren. Bei vielen Aufrufen und mehreren Aktivitäten summiert sich das.

Eine Lösung besteht darin, ein Objekt zu erstellen, das einen Verweis auf jede Ansicht enthält. Dieses Objekt, ein Binding-Objekt, kann von Ihrer gesamten App verwendet werden. Diese Technik wird als Datenbindung bezeichnet. Nachdem ein Bindungsobjekt für Ihre App erstellt wurde, können Sie über das Bindungsobjekt auf die Ansichten und andere Daten zugreifen, ohne die Ansichtshierarchie durchlaufen oder nach den Daten suchen zu müssen.

Die Datenbindung bietet folgende Vorteile:

  • Code, der findByView() verwendet, ist kürzer, leichter zu lesen und einfacher zu warten.
  • Daten und Ansichten sind klar getrennt. Dieser Vorteil der Datenbindung wird später in diesem Kurs immer wichtiger.
  • Das Android-System durchläuft die Ansichtshierarchie nur einmal, um jede Ansicht abzurufen. Dies geschieht beim Start der App, nicht zur Laufzeit, wenn der Nutzer mit der App interagiert.
  • Sie erhalten Typsicherheit für den Zugriff auf Ansichten. Typsicherheit bedeutet, dass der Compiler Typen während der Kompilierung validiert und einen Fehler ausgibt, wenn Sie versuchen, einer Variablen den falschen Typ zuzuweisen.

In dieser Aufgabe richten Sie die Datenbindung ein und ersetzen Aufrufe von findViewById() durch Aufrufe des Bindungsobjekts.

Schritt 1: Datenbindung aktivieren

Wenn Sie die Datenbindung verwenden möchten, müssen Sie sie in Ihrer Gradle-Datei aktivieren, da sie standardmäßig nicht aktiviert ist. Das liegt daran, dass die Datenbindung die Kompilierungszeit verlängert und sich auf die Startzeit der App auswirken kann.

  1. Wenn Sie die AboutMe-App aus einem vorherigen Codelab nicht haben, laden Sie den Code AboutMeDataBinding-Starter von GitHub herunter. Öffnen Sie es in Android Studio.
  2. Öffnen Sie die Datei build.gradle (Module: app).
  3. Fügen Sie im Abschnitt android vor der schließenden geschweiften Klammer einen Abschnitt dataBinding hinzu und setzen Sie enabled auf true.
dataBinding {
    enabled = true
}
  1. Wenn Sie dazu aufgefordert werden, synchronisieren Sie das Projekt. Wenn Sie nicht dazu aufgefordert werden, wählen Sie File > Sync Project with Gradle Files (Datei > Projekt mit Gradle-Dateien synchronisieren) aus.
  2. Sie können die App ausführen, aber es werden keine Änderungen angezeigt.

Schritt 2: Layoutdatei für die Verwendung mit der Datenbindung ändern

Wenn Sie Datenbindung verwenden möchten, müssen Sie Ihr XML-Layout mit einem <layout>-Tag umschließen. Dadurch ist die Stammklasse keine Ansichtsgruppe mehr, sondern ein Layout, das Ansichtsgruppen und Ansichten enthält. Das Bindungsobjekt kann dann das Layout und die darin enthaltenen Ansichten kennen.

  1. Öffnen Sie die Datei activity_main.xml.
  2. Wechseln Sie zum Tab Text.
  3. Fügen Sie <layout></layout> als äußerstes Tag um <LinearLayout> hinzu.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Wählen Sie Code > Code neu formatieren aus, um die Codeeinrückung zu korrigieren.

    Die Namespace-Deklarationen für ein Layout müssen sich im äußersten Tag befinden.
  1. Schneiden Sie die Namespace-Deklarationen aus <LinearLayout> aus und fügen Sie sie in das <layout>-Tag ein. Ihr öffnendes <layout>-Tag sollte wie unten dargestellt aussehen und das <LinearLayout>-Tag sollte nur Ansichtseigenschaften enthalten.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Erstellen und führen Sie Ihre App aus, um zu prüfen, ob Sie alles richtig gemacht haben.

Schritt 3: Bindungsobjekt in der Hauptaktivität erstellen

Fügen Sie der Hauptaktivität einen Verweis auf das Bindungsobjekt hinzu, damit Sie damit auf Ansichten zugreifen können:

  1. Öffnen Sie die Datei MainActivity.kt.
  2. Erstellen Sie vor onCreate() auf der obersten Ebene eine Variable für das Bindungsobjekt. Diese Variable wird üblicherweise als binding bezeichnet.

    Der Typ von binding, die Klasse ActivityMainBinding, wird vom Compiler speziell für diese Hauptaktivität erstellt. Der Name wird vom Namen der Layoutdatei abgeleitet, also activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Wenn Sie von Android Studio dazu aufgefordert werden, importieren Sie ActivityMainBinding. Wenn Sie nicht dazu aufgefordert werden, klicken Sie auf ActivityMainBinding und drücken Sie Alt+Enter (Option+Enter auf einem Mac), um diese fehlende Klasse zu importieren. Weitere Tastenkombinationen finden Sie hier.

    Die import-Anweisung sollte in etwa so aussehen:
import com.example.android.aboutme.databinding.ActivityMainBinding

Als Nächstes ersetzen Sie die aktuelle setContentView()-Funktion durch eine Anweisung, die Folgendes ausführt:

  • Erstellt das Bindungsobjekt.
  • Verwendet die Funktion setContentView() aus der Klasse DataBindingUtil, um das activity_main-Layout mit dem MainActivity zu verknüpfen. Diese setContentView()-Funktion übernimmt auch die Einrichtung der Datenbindung für die Ansichten.
  1. Ersetzen Sie in onCreate() den setContentView()-Aufruf durch die folgende Codezeile.
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Importieren Sie DataBindingUtil.
import androidx.databinding.DataBindingUtil

Schritt 4: Alle Aufrufe von findViewById() durch das Binding-Objekt ersetzen

Sie können jetzt alle Aufrufe von findViewById() durch Verweise auf die Ansichten im Bindungsobjekt ersetzen. Wenn das Binding-Objekt erstellt wird, generiert der Compiler die Namen der Ansichten im Binding-Objekt aus den IDs der Ansichten im Layout und wandelt sie in CamelCase um. done_button ist beispielsweise doneButton im Bindungsobjekt, nickname_edit wird zu nicknameEdit und nickname_text zu nicknameText.

  1. Ersetzen Sie in onCreate() den Code, der findViewById() verwendet, um die done_button zu finden, durch Code, der auf die Schaltfläche im Bindungsobjekt verweist.

    Ersetzen Sie diesen Code: findViewById<Button>(R.id.done_button)
    durch: binding.doneButton

    Ihr fertiger Code zum Festlegen des Klick-Listeners in onCreate() sollte so aussehen.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Wiederholen Sie diesen Schritt für alle Aufrufe von findViewById() in der Funktion addNickname().
    Ersetzen Sie alle Vorkommen von findViewById<View>(R.id.id_view) durch binding.idView. Gehen Sie dazu so vor:
  • Löschen Sie die Definitionen für die Variablen editText und nicknameTextView sowie die zugehörigen Aufrufe von findViewById(). Das führt zu Fehlern.
  • Beheben Sie die Fehler, indem Sie die Ansichten nicknameText, nicknameEdit und doneButton aus dem binding-Objekt anstelle der (gelöschten) Variablen abrufen.
  • Ersetzen Sie view.visibility durch binding.doneButton.visibility. Durch die Verwendung von binding.doneButton anstelle des übergebenen view wird der Code einheitlicher.

    Das Ergebnis ist der folgende Code:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Die Funktionalität bleibt unverändert. Optional können Sie jetzt den Parameter view entfernen und alle Verwendungen von view in dieser Funktion in binding.doneButton ändern.
  1. Für nicknameText ist ein String erforderlich und nicknameEdit.text ist ein Editable. Wenn Sie die Datenbindung verwenden, müssen Sie Editable explizit in String konvertieren.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Sie können die ausgegrauten Importe löschen.
  2. Verwenden Sie apply{}, um die Funktion in Kotlin zu konvertieren.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Erstellen Sie Ihre App und führen Sie sie aus. Sie sollte genauso aussehen und funktionieren wie zuvor.

Mit der Datenbindung können Sie eine Datenklasse direkt für eine Ansicht verfügbar machen. Diese Technik vereinfacht den Code und ist äußerst nützlich für komplexere Fälle.

In diesem Beispiel erstellen Sie anstelle von String-Ressourcen eine Datenklasse für den Namen und den Alias. Sie stellen die Datenklasse über die Datenbindung für die Ansicht zur Verfügung.

Schritt 1: Datenklasse „MyName“ erstellen

  1. Öffnen Sie in Android Studio die Datei MyName.kt im Verzeichnis java. Wenn Sie diese Datei nicht haben, erstellen Sie eine neue Kotlin-Datei und nennen Sie sie MyName.kt.
  2. Definieren Sie eine Datenklasse für den Namen und den Spitznamen. Verwenden Sie leere Strings als Standardwerte.
data class MyName(var name: String = "", var nickname: String = "")

Schritt 2: Daten zum Layout hinzufügen

In der Datei activity_main.xml wird der Name derzeit in einem TextView aus einer String-Ressource festgelegt. Sie müssen den Verweis auf den Namen durch einen Verweis auf Daten in der Datenklasse ersetzen.

  1. Öffnen Sie activity_main.xml auf dem Tab Text.
  2. Fügen Sie oben im Layout zwischen den Tags <layout> und <LinearLayout> ein <data></data>-Tag ein. Hier verbinden Sie die Ansicht mit den Daten.
<data>
  
</data>

Innerhalb der Datentags können Sie benannte Variablen deklarieren, die einen Verweis auf eine Klasse enthalten.

  1. Fügen Sie im Tag <data> ein Tag <variable> hinzu.
  2. Fügen Sie einen name-Parameter hinzu, um der Variablen den Namen "myName" zu geben. Fügen Sie einen type-Parameter hinzu und legen Sie den Typ auf den vollständig qualifizierten Namen der MyName-Datenklasse fest (Paketname + Variablenname).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Anstatt die String-Ressource für den Namen zu verwenden, können Sie jetzt auf die Variable myName verweisen.

  1. Ersetzen Sie android:text="@string/name" durch den folgenden Code.

@={} ist eine Anweisung zum Abrufen der Daten, auf die in den geschweiften Klammern verwiesen wird.

myName verweist auf die zuvor definierte Variable myName, die auf die Datenklasse myName verweist und die Eigenschaft name aus der Klasse abruft.

android:text="@={myName.name}"

Schritt 3: Daten erstellen

Sie haben jetzt einen Verweis auf die Daten in Ihrer Layoutdatei. Als Nächstes erstellen Sie die eigentlichen Daten.

  1. Öffnen Sie die Datei MainActivity.kt.
  2. Erstellen Sie über onCreate() eine private Variable, die gemäß Konvention ebenfalls myName heißt. Weisen Sie der Variablen eine Instanz der Datenklasse MyName zu und übergeben Sie den Namen.
private val myName: MyName = MyName("Aleks Haecky")
  1. Legen Sie in onCreate() den Wert der Variablen myName in der Layoutdatei auf den Wert der Variablen myName fest, die Sie gerade deklariert haben. Sie können nicht direkt in der XML-Datei auf die Variable zugreifen. Sie müssen über das Bindungsobjekt darauf zugreifen.
binding.myName = myName
  1. Möglicherweise wird ein Fehler angezeigt, da Sie das Bindungsobjekt nach Änderungen aktualisieren müssen. Erstellen Sie Ihre App. Der Fehler sollte dann nicht mehr auftreten.

Schritt 4: Datenklasse für den Aliasnamen in der TextView verwenden

Im letzten Schritt verwenden Sie die Datenklasse auch für den Alias in TextView.

  1. Öffnen Sie activity_main.xml.
  2. Fügen Sie in der Textansicht nickname_text eine text-Eigenschaft hinzu. Verweisen Sie wie unten gezeigt auf nickname in der Datenklasse.
android:text="@={myName.nickname}"
  1. Ersetzen Sie in ActivityMain
    nicknameText.text = nicknameEdit.text.toString()
    durch Code, um den Alias in der Variablen myName festzulegen.
myName?.nickname = nicknameEdit.text.toString()

Nachdem der Alias festgelegt wurde, soll die Benutzeroberfläche mit den neuen Daten aktualisiert werden. Dazu müssen Sie alle Bindungsausdrücke ungültig machen, damit sie mit den richtigen Daten neu erstellt werden.

  1. Fügen Sie invalidateAll() hinzu, nachdem Sie den Alias festgelegt haben, damit die Benutzeroberfläche mit dem Wert im aktualisierten Bindungsobjekt aktualisiert wird.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Erstellen und führen Sie Ihre App aus. Sie sollte genau wie zuvor funktionieren.

Android Studio-Projekt: AboutMeDataBinding

So ersetzen Sie Aufrufe von findViewById() durch die Datenbindung:

  1. Aktivieren Sie die Datenbindung im Android-Abschnitt der Datei build.gradle:
    dataBinding { enabled = true }
  2. Verwenden Sie <layout> als Stammansicht in Ihrem XML-Layout.
  3. Bindungsvariable definieren:
    private lateinit var binding: ActivityMainBinding
  4. Erstellen Sie ein Bindungsobjekt in MainActivity und ersetzen Sie setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Ersetzen Sie Aufrufe von findViewById() durch Verweise auf die Ansicht im Bindungsobjekt. Beispiel:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (Im Beispiel wird der Name der Ansicht im CamelCase-Format aus dem id der Ansicht im XML-Code generiert.)

So binden Sie Ansichten an Daten:

  1. Erstellen Sie eine Datenklasse für Ihre Daten.
  2. Fügen Sie dem <layout>-Tag einen <data>-Block hinzu.
  3. Definieren Sie eine <variable> mit einem Namen und einem Typ, der die Datenklasse ist.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. Erstellen Sie in MainActivity eine Variable mit einer Instanz der Datenklasse. Beispiel:
    private val myName: MyName = MyName("Aleks Haecky")
  1. Legen Sie im Bindungsobjekt die Variable auf die Variable fest, die Sie gerade erstellt haben:
    binding.myName = myName
  1. Legen Sie in der XML-Datei den Inhalt der Ansicht auf die Variable fest, die Sie im Block <data> definiert haben. Verwenden Sie die Punktnotation, um auf die Daten in der Datenklasse zuzugreifen.
    android:text="@={myName.name}"

Udacity-Kurs:

Android-Entwicklerdokumentation:

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

Warum sollten explizite und implizite Aufrufe von findViewById() minimiert werden?

  • Bei jedem Aufruf von findViewById() wird die Ansichtshierarchie durchlaufen.
  • findViewById() wird im Haupt- oder UI-Thread ausgeführt.
  • Diese Aufrufe können die Benutzeroberfläche verlangsamen.
  • Ihre App stürzt seltener ab.

Frage 2

Wie würden Sie die Datenbindung beschreiben?

Hier sind einige Beispiele für Aussagen, die Sie über die Datenbindung machen könnten:

  • Die Idee hinter der Datenbindung besteht darin, zur Kompilierzeit ein Objekt zu erstellen, das zwei weit voneinander entfernte Informationen miteinander verbindet/zuordnet/bindet, sodass Sie zur Laufzeit nicht nach den Daten suchen müssen.
  • Das Objekt, das diese Bindungen für Sie verfügbar macht, wird als Bindungsobjekt bezeichnet.
  • Das Bindungsobjekt wird vom Compiler erstellt.

Frage 3

Welche der folgenden Optionen ist KEIN Vorteil der Datenbindung?

  • Der Code ist kürzer, leichter zu lesen und einfacher zu warten.
  • Daten und Ansichten sind klar getrennt.
  • Das Android-System durchläuft die Ansichtshierarchie nur einmal, um jede Ansicht abzurufen.
  • Der Aufruf von findViewById() führt zu einem Compilerfehler.
  • Typsicherheit für den Zugriff auf Ansichten.

Frage 4

Wozu dient das <layout>-Tag?

  • Sie umschließen damit die Stammansicht im Layout.
  • Für alle Ansichten in einem Layout werden Bindungen erstellt.
  • Sie gibt die Ansicht der obersten Ebene in einem XML-Layout an, das die Datenbindung verwendet.
  • Mit dem <data>-Tag innerhalb eines <layout>-Tags können Sie eine Variable an eine Datenklasse binden.

Frage 5

Wie werden gebundene Daten im XML-Layout richtig referenziert?

  • android:text="@={myDataClass.property}"
  • android:text="@={myDataClass}"
  • android:text="@={myDataClass.property.toString()}"
  • android:text="@={myDataClass.bound_data.property}"

Nächste Lektion starten: 3.1: Fragment erstellen

Links zu anderen Codelabs in diesem Kurs finden Sie auf der Landingpage für Android Kotlin Fundamentals-Codelabs.