Android Kotlin Fundamentals 02.4: Data-binding basics

Ten moduł Codelab jest częścią kursu Android Kotlin Fundamentals. Najwięcej korzyści przyniesie Ci ukończenie wszystkich ćwiczeń w kolejności. Wszystkie ćwiczenia z tego kursu znajdziesz na stronie docelowej kursu Android Kotlin Fundamentals.

Wprowadzenie

W poprzednich ćwiczeniach z tego kursu do uzyskiwania odwołań do widoków używaliśmy funkcji findViewById(). Gdy aplikacja ma złożone hierarchie widoków, funkcja findViewById() jest kosztowna i spowalnia działanie aplikacji, ponieważ Android przechodzi przez hierarchię widoków, zaczynając od elementu głównego, aż znajdzie żądany widok. Na szczęście istnieje lepszy sposób.

Aby ustawić dane w widokach, używasz zasobów tekstowych i ustawiasz dane z aktywności. Byłoby bardziej wydajne, gdyby widok znał dane. Na szczęście jest to możliwe.

Z tego ćwiczenia w Codelabs dowiesz się, jak używać powiązania danych, aby wyeliminować konieczność stosowania findViewById(). Dowiesz się też, jak używać powiązania danych, aby uzyskiwać dostęp do danych bezpośrednio z widoku.

Co warto wiedzieć

Musisz znać:

  • Co to jest aktywność i jak skonfigurować aktywność z układem w onCreate().
  • tworzenie widoku tekstu i ustawianie tekstu, który ma być w nim wyświetlany;
  • Używanie findViewById() do uzyskiwania odniesienia do widoku.
  • Tworzenie i edytowanie podstawowego układu XML widoku.

Czego się nauczysz

  • Jak używać biblioteki powiązań danych, aby eliminować nieefektywne wywołania funkcji findViewById().
  • Jak uzyskać dostęp do danych aplikacji bezpośrednio z pliku XML.

Jakie zadania wykonasz

  • Zmodyfikuj aplikację, aby zamiast findViewById() używała powiązania danych i miała dostęp do danych bezpośrednio z plików XML układu.

W tym ćwiczeniu z programowania zaczniesz od aplikacji AboutMe i zmienisz ją tak, aby korzystała z powiązywania danych. Po zakończeniu pracy aplikacja będzie wyglądać dokładnie tak samo.

Oto co robi aplikacja AboutMe:

  • Gdy użytkownik otworzy aplikację, wyświetli się nazwa, pole do wpisania pseudonimu, przycisk Gotowe, obraz gwiazdy i tekst z możliwością przewijania.
  • Użytkownik może wpisać pseudonim i kliknąć przycisk Gotowe. Pole edycji i przycisk zostaną zastąpione widokiem tekstu, w którym będzie widoczny wpisany pseudonim.


Możesz użyć kodu utworzonego w poprzednich ćwiczeniach lub pobrać kod AboutMeDataBinding-Starter z GitHuba.

Kod napisany w poprzednich ćwiczeniach z programowania używa funkcji findViewById() do uzyskiwania odwołań do widoków.

Za każdym razem, gdy używasz funkcji findViewById() do wyszukiwania widoku po jego utworzeniu lub ponownym utworzeniu, system Android w czasie działania przechodzi przez hierarchię widoków, aby go znaleźć. Jeśli Twoja aplikacja ma tylko kilka wyświetleń, nie stanowi to problemu. Jednak aplikacje produkcyjne mogą mieć w układzie dziesiątki widoków, a nawet przy najlepszym projekcie będą zawierać widoki zagnieżdżone.

Wyobraź sobie układ liniowy zawierający widok przewijania, który zawiera widok tekstu. W przypadku dużej lub złożonej hierarchii widoków znalezienie widoku może zająć tyle czasu, że użytkownik zauważy spowolnienie działania aplikacji. Zapisywanie widoków w zmiennych może pomóc, ale nadal musisz zainicjować zmienną dla każdego widoku w każdej przestrzeni nazw. Przy dużej liczbie wyświetleń i różnych aktywnościach może to być spora kwota.

Jednym z rozwiązań jest utworzenie obiektu, który zawiera odwołanie do każdego widoku. Ten obiekt, zwany obiektem Binding, może być używany przez całą aplikację. Ta technika nosi nazwę wiązania danych. Gdy dla aplikacji zostanie utworzony obiekt powiązania, możesz uzyskiwać dostęp do widoków i innych danych za jego pomocą bez konieczności przechodzenia przez hierarchię widoków ani wyszukiwania danych.

Wiązanie danych ma te zalety:

  • Kod jest krótszy, łatwiejszy do odczytania i utrzymania niż kod, który używa findByView().
  • Dane i widoki są wyraźnie rozdzielone. Ta zaleta wiązania danych będzie miała coraz większe znaczenie w dalszej części tego kursu.
  • System Android przechodzi przez hierarchię widoków tylko raz, aby uzyskać każdy widok. Dzieje się to podczas uruchamiania aplikacji, a nie w czasie działania, gdy użytkownik wchodzi z nią w interakcję.
  • Uzyskasz bezpieczeństwo typów podczas uzyskiwania dostępu do widoków. (Bezpieczeństwo typów oznacza, że kompilator weryfikuje typy podczas kompilacji i wyświetla błąd, jeśli spróbujesz przypisać do zmiennej nieprawidłowy typ).

W tym zadaniu skonfigurujesz powiązanie danych i użyjesz go do zastąpienia wywołań funkcji findViewById() wywołaniami obiektu powiązania.

Krok 1. Włącz wiązanie danych

Aby używać powiązania danych, musisz włączyć je w pliku Gradle, ponieważ nie jest ono domyślnie włączone. Dzieje się tak, ponieważ wiązanie danych wydłuża czas kompilacji i może wpływać na czas uruchamiania aplikacji.

  1. Jeśli nie masz aplikacji AboutMe z poprzednich zajęć, pobierz kod AboutMeDataBinding-Starter z GitHub. Otwórz go w Android Studio.
  2. Otwórz plik build.gradle (Module: app).
  3. W sekcji android przed nawiasem zamykającym dodaj sekcję dataBinding i ustaw wartość enabled na true.
dataBinding {
    enabled = true
}
  1. Gdy pojawi się prośba, zsynchronizuj projekt. Jeśli nie pojawi się odpowiedni komunikat, wybierz File > Sync Project with Gradle Files (Plik > Synchronizuj projekt z plikami Gradle).
  2. Możesz uruchomić aplikację, ale nie zobaczysz żadnych zmian.

Krok 2. Zmień plik układu, aby można go było używać z powiązaniem danych

Aby korzystać z wiązania danych, musisz umieścić układ XML w tagu <layout>. Dzięki temu klasa główna nie będzie już grupą widoków, ale układem zawierającym grupy widoków i widoki. Obiekt powiązania może wtedy uzyskać informacje o układzie i widokach w nim.

  1. Otwórz plik activity_main.xml.
  2. Przejdź na kartę Tekst.
  3. Dodaj tag <layout></layout> jako najbardziej zewnętrzny tag wokół tagu <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Aby poprawić wcięcia w kodzie, wybierz Code > Reformat code (Kod > Zmień format kodu).

    Deklaracje przestrzeni nazw w przypadku układu muszą znajdować się w najbardziej zewnętrznym tagu.
  1. Wytnij deklaracje przestrzeni nazw z tagu <LinearLayout> i wklej je do tagu <layout>. Tag otwierający <layout> powinien wyglądać jak poniżej, a tag <LinearLayout> powinien zawierać tylko właściwości widoku.
<layout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
  1. Zbuduj i uruchom aplikację, aby sprawdzić, czy wszystko zostało wykonane prawidłowo.

Krok 3. Utwórz obiekt powiązania w głównym działaniu

Dodaj odwołanie do obiektu powiązania w głównym działaniu, aby móc go używać do uzyskiwania dostępu do widoków:

  1. Otwórz plik MainActivity.kt.
  2. Przed onCreate() na najwyższym poziomie utwórz zmienną dla obiektu wiązania. Zmienna ta jest zwykle nazywana binding.

    Typ binding, czyli klasa ActivityMainBinding, jest tworzony przez kompilator specjalnie na potrzeby tego głównego działania. Nazwa pochodzi od nazwy pliku układu, czyli activity_main + Binding.
private lateinit var binding: ActivityMainBinding
  1. Jeśli Android Studio wyświetli odpowiedni komunikat, zaimportuj ActivityMainBinding. Jeśli nie pojawi się prośba, kliknij ActivityMainBinding i naciśnij Alt+Enter (Option+Enter na Macu), aby zaimportować brakujące zajęcia. (Więcej skrótów klawiszowych znajdziesz w artykule Skróty klawiszowe).

    Instrukcja import powinna wyglądać podobnie do tej poniżej.
import com.example.android.aboutme.databinding.ActivityMainBinding

Następnie zastąp bieżącą funkcję setContentView() instrukcją, która wykonuje te czynności:

  • Tworzy obiekt powiązania.
  • Używa funkcji setContentView() z klasy DataBindingUtil, aby powiązać układ activity_main z elementem MainActivity. Ta funkcja setContentView() zajmuje się też konfiguracją powiązań danych w przypadku widoków.
  1. W pliku onCreate() zastąp wywołanie setContentView() tym wierszem kodu:
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  1. Importuj DataBindingUtil.
import androidx.databinding.DataBindingUtil

Krok 4. Użyj obiektu wiązania, aby zastąpić wszystkie wywołania findViewById()

Możesz teraz zastąpić wszystkie wywołania findViewById() odwołaniami do widoków znajdujących się w obiekcie powiązania. Gdy obiekt powiązania zostanie utworzony, kompilator wygeneruje nazwy widoków w obiekcie powiązania na podstawie identyfikatorów widoków w układzie, przekształcając je na format camel case. Na przykład done_button w obiekcie wiązania to doneButton, nickname_edit zmienia się w nicknameEdit, a nickname_textnicknameText.

  1. onCreate() zastąp kod, który używa findViewById() do znalezienia done_button, kodem odwołującym się do przycisku w obiekcie powiązania.

    Zastąp ten kod: findViewById<Button>(R.id.done_button)
    tym kodem: binding.doneButton

    Gotowy kod do ustawienia odbiornika kliknięć w onCreate() powinien wyglądać tak.
binding.doneButton.setOnClickListener {
   addNickname(it)
}
  1. Zrób to samo w przypadku wszystkich wywołań funkcji findViewById() w funkcji addNickname().
    Zastąp wszystkie wystąpienia ciągu findViewById<View>(R.id.id_view) ciągiem binding.idView. Zrób to w ten sposób:
  • Usuń definicje zmiennych editTextnicknameTextView wraz z wywołaniami funkcji findViewById(). Spowoduje to błędy.
  • Napraw błędy, pobierając widoki nicknameText, nicknameEditdoneButton z obiektu binding zamiast z usuniętych zmiennych.
  • Zastąp view.visibility tekstem binding.doneButton.visibility. Użycie wartości binding.doneButton zamiast przekazanej wartości view sprawia, że kod jest bardziej spójny.

    Wynikiem jest ten kod:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Funkcjonalność pozostaje bez zmian. Opcjonalnie możesz teraz usunąć parametr view i zaktualizować wszystkie użycia parametru view, aby w tej funkcji używać parametru binding.doneButton.
  1. nicknameText wymaga String, a nicknameEdit.text to Editable. W przypadku wiązania danych konieczne jest jawne przekształcenie Editable na String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Możesz usunąć zacienione importy.
  2. Zmień funkcję na kod w języku Kotlin za pomocą apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Zbuduj i uruchom aplikację. Powinna wyglądać i działać dokładnie tak samo jak wcześniej.

Możesz skorzystać z powiązania danych, aby udostępnić klasę danych bezpośrednio w widoku. Ta technika upraszcza kod i jest niezwykle przydatna w bardziej złożonych przypadkach.

W tym przykładzie zamiast ustawiać imię i pseudonim za pomocą zasobów ciągów znaków utworzysz klasę danych dla imienia i pseudonimu. Klasę danych udostępniasz widokowi za pomocą powiązania danych.

Krok 1. Utwórz klasę danych MyName

  1. W Android Studio w katalogu java otwórz plik MyName.kt. Jeśli nie masz tego pliku, utwórz nowy plik Kotlin i nadaj mu nazwę MyName.kt.
  2. Zdefiniuj klasę danych dla imienia i pseudonimu. Używaj pustych ciągów znaków jako wartości domyślnych.
data class MyName(var name: String = "", var nickname: String = "")

Krok 2. Dodaj dane do układu

W pliku activity_main.xml nazwa jest obecnie ustawiona w TextView z zasobu ciągu tekstowego. Musisz zastąpić odwołanie do nazwy odwołaniem do danych w klasie danych.

  1. Otwórz activity_main.xml na karcie Tekst.
  2. U góry układu, między tagami <layout><LinearLayout>, wstaw tag <data></data>. W tym miejscu połączysz widok z danymi.
<data>
  
</data>

W tagach danych możesz zadeklarować nazwane zmienne, które zawierają odwołanie do klasy.

  1. W tagu <data> dodaj tag <variable>.
  2. Dodaj parametr name, aby nadać zmiennej nazwę "myName". Dodaj parametr type i ustaw typ na w pełni kwalifikowaną nazwę klasy danych MyName (nazwa pakietu + nazwa zmiennej).
<variable
       name="myName"
       type="com.example.android.aboutme.MyName" />

Teraz zamiast używać zasobu ciągu tekstowego dla nazwy możesz odwoływać się do myNamezmiennej.

  1. Zastąp android:text="@string/name" kodem poniżej.

@={} to dyrektywa pobierania danych, do których odwołuje się tekst w nawiasach klamrowych.

myName odwołuje się do wcześniej zdefiniowanej zmiennej myName, która wskazuje klasę danych myName i pobiera z niej właściwość name.

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

Krok 3. Utwórz dane

Masz teraz odwołanie do danych w pliku układu. Następnie utwórz rzeczywiste dane.

  1. Otwórz plik MainActivity.kt.
  2. Nad onCreate() utwórz zmienną prywatną, która zgodnie z konwencją również będzie się nazywać myName. Przypisz zmiennej instancję klasy danych MyName, przekazując nazwę.
private val myName: MyName = MyName("Aleks Haecky")
  1. onCreate() ustaw wartość zmiennej myName w pliku układu na wartość zmiennej myName, którą właśnie zadeklarowano. Nie możesz uzyskać bezpośredniego dostępu do zmiennej w pliku XML. Musisz uzyskać do niego dostęp za pomocą obiektu wiążącego.
binding.myName = myName
  1. Może to spowodować błąd, ponieważ po wprowadzeniu zmian musisz odświeżyć obiekt powiązania. Skompiluj aplikację, a błąd powinien zniknąć.

Krok 4. Użyj klasy danych dla pseudonimu w obiekcie TextView

Ostatnim krokiem jest użycie klasy danych w przypadku pseudonimu w TextView.

  1. Otwórz pokój activity_main.xml.
  2. W widoku tekstowym nickname_text dodaj właściwość text. Odwołaj się do nickname w klasie danych, jak pokazano poniżej.
android:text="@={myName.nickname}"
  1. ActivityMain zastąp
    nicknameText.text = nicknameEdit.text.toString()
    kodem, aby ustawić pseudonim w zmiennej myName.
myName?.nickname = nicknameEdit.text.toString()

Po ustawieniu pseudonimu chcesz, aby kod odświeżył interfejs użytkownika z nowymi danymi. Aby to zrobić, musisz unieważnić wszystkie wyrażenia wiążące, aby zostały ponownie utworzone z prawidłowymi danymi.

  1. Po ustawieniu pseudonimu dodaj invalidateAll(), aby odświeżyć interfejs z wartością w zaktualizowanym obiekcie powiązania.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Zbuduj i uruchom aplikację. Powinna działać tak samo jak wcześniej.

Projekt Android Studio: AboutMeDataBinding

Aby zastąpić wywołania funkcji findViewById() za pomocą powiązania danych:

  1. Włącz powiązanie danych w sekcji android pliku build.gradle:
    dataBinding { enabled = true }
  2. Użyj <layout> jako widoku głównego w układzie XML.
  3. Zdefiniuj zmienną wiązania:
    private lateinit var binding: ActivityMainBinding
  4. Utwórz obiekt wiązania w usłudze MainActivity, zastępując setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Zastąp wywołania funkcji findViewById() odwołaniami do widoku w obiekcie powiązania. Przykład:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (W tym przykładzie nazwa widoku jest generowana w formacie camel case na podstawie id widoku w XML).

Instrukcje dotyczące wiązania widoków z danymi:

  1. Utwórz klasę danych dla swoich danych.
  2. Dodaj blok <data> w tagu <layout>.
  3. Zdefiniuj <variable> z nazwą i typem, który jest klasą danych.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. MainActivity utwórz zmienną z instancją klasy danych. Na przykład:
    private val myName: MyName = MyName("Aleks Haecky")
  1. W obiekcie powiązania ustaw zmienną na utworzoną zmienną:
    binding.myName = myName
  1. W pliku XML ustaw zawartość widoku na zmienną zdefiniowaną w bloku <data>. Aby uzyskać dostęp do danych w klasie danych, użyj notacji z kropką.
    android:text="@={myName.name}"

Kurs Udacity:

Dokumentacja dla deweloperów aplikacji na Androida:

W tej sekcji znajdziesz listę możliwych zadań domowych dla uczniów, którzy wykonują ten moduł w ramach kursu prowadzonego przez instruktora. Nauczyciel musi:

  • W razie potrzeby przypisz pracę domową.
  • Poinformuj uczniów, jak przesyłać projekty.
  • Oceń zadania domowe.

Instruktorzy mogą korzystać z tych sugestii w dowolnym zakresie i mogą zadawać inne zadania domowe, które uznają za odpowiednie.

Jeśli wykonujesz ten kurs samodzielnie, możesz użyć tych zadań domowych, aby sprawdzić swoją wiedzę.

Odpowiedz na te pytania

Pytanie 1

Dlaczego chcesz zminimalizować jawne i niejawne wywołania funkcji findViewById()?

  • Za każdym razem, gdy wywoływana jest funkcja findViewById(), przechodzi ona przez hierarchię widoków.
  • findViewById() działa w wątku głównym lub wątku interfejsu.
  • Te wywołania mogą spowolnić interfejs.
  • Aplikacja będzie mniej podatna na awarie.

Pytanie 2

Jak opiszesz wiązanie danych?

Oto kilka przykładów, co możesz powiedzieć o wiązaniu danych:

  • Główną ideą powiązania danych jest utworzenie obiektu, który łączy/mapuje/wiąże ze sobą 2 odległe informacje w czasie kompilacji, dzięki czemu nie musisz szukać danych w czasie działania programu.
  • Obiekt, który udostępnia te powiązania, nazywa się obiektem powiązania.
  • Obiekt powiązania jest tworzony przez kompilator.

Pytanie 3

Która z tych opcji NIE jest zaletą wiązania danych?

  • Kod jest krótszy, łatwiejszy do odczytania i utrzymania.
  • Dane i widoki są wyraźnie rozdzielone.
  • System Android przechodzi przez hierarchię widoków tylko raz, aby uzyskać każdy widok.
  • Wywołanie funkcji findViewById() powoduje błąd kompilatora.
  • Bezpieczeństwo typów podczas uzyskiwania dostępu do widoków.

Pytanie 4

Jaką funkcję pełni tag <layout>?

  • Umieść go w widoku głównym w układzie.
  • Powiązania są tworzone dla wszystkich widoków w układzie.
  • Określa widok najwyższego poziomu w układzie XML, który korzysta z powiązania danych.
  • Możesz użyć tagu <data> wewnątrz tagu <layout>, aby powiązać zmienną z klasą danych.

Pytanie 5

Który sposób odwoływania się do powiązanych danych w układzie XML jest prawidłowy?

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

Rozpocznij kolejną lekcję: 3.1. Tworzenie fragmentu

Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.