Android Kotlin Fundamentals 02.4: podstawy wiązania danych

Te ćwiczenia są częścią kursu Android Kotlin Fundamentals. Skorzystaj z tego kursu, jeśli będziesz wykonywać kolejno kilka ćwiczeń z programowania. Wszystkie ćwiczenia z kursu są wymienione na stronie docelowej ćwiczeń z programowania na temat Kotlin.

Wprowadzenie

W poprzednich ćwiczeniach z programowania na ten kurs wykorzystywana była funkcja findViewById() do sprawdzania odwołań do widoków. Gdy aplikacja ma złożone hierarchie widoków danych, findViewById() jest kosztowna i spowalnia ją, ponieważ Android przechodzi przez hierarchię widoków, zaczynając od poziomu głównego, aż znajdzie odpowiedni widok. Na szczęście jest lepszy sposób.

Do ustawiania danych w widokach służą zasoby ciągu znaków i dane z aktywności. Lepszy byłby widok danych, gdyby wiedział o nich dane. Na szczęście jest to możliwe.

Z tego ćwiczenia dowiesz się, jak używać wiązania danych, aby wyeliminować potrzebę stosowania funkcji findViewById(). Dowiedz się też, jak korzystać z powiązań danych, by uzyskać dostęp do danych bezpośrednio z poziomu widoku danych.

Co musisz wiedzieć

Pamiętaj:

  • Czym jest aktywność i jak ją skonfigurować w narzędziu onCreate().
  • Tworzenie widoku tekstowego i ustawianie tekstu, który się w nim wyświetla.
  • Używanie operatora findViewById() do generowania odwołań do widoku.
  • Tworzenie i edytowanie podstawowego układu XML dla widoku.

Czego się nauczysz

  • Jak używać biblioteki wiązania danych, aby wyeliminować nieskuteczne wywołania usługi findViewById().
  • Jak uzyskać dostęp do danych aplikacji bezpośrednio z pliku XML.

Jakie zadania wykonasz:

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

W tym ćwiczeniu zaczniesz od aplikacji O mnie, by zmienić jej działanie na wiązanie danych. Gdy skończysz, aplikacja będzie wyglądać dokładnie tak samo.

Oto informacje o aplikacji OMem:

  • Gdy użytkownik otworzy aplikację, wyświetli się nazwa, pole do wpisania pseudonimu, przycisk Gotowe, obraz gwiazdki i element, który można przewijać.
  • Użytkownik może wpisać pseudonim i kliknąć przycisk Gotowe. Pole i przycisk, które można edytować, zostaną zastąpione widokiem tekstowym zawierającym wpisany pseudonim.


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

Kod podany w poprzednich ćwiczeniach z programowania korzysta z funkcji findViewById() do uzyskiwania odniesień do widoków.

Za każdym razem, gdy użyjesz widoku findViewById(), aby wyszukać widok, który został utworzony lub utworzony ponownie, Android w czasie rzeczywistym przegląda hierarchię widoku, aby ją znaleźć. Jeśli Twoja aplikacja ma tylko kilka wyświetleń, nie stanowi to problemu. Jednak aplikacje w wersji produkcyjnej mogą mieć dziesiątki wyświetleń w układzie i nawet w przypadku najlepszego projektu będą zagnieżdżone widoki.

Pomyśl o układzie liniowym zawierającym widok przewijania zawierający widok tekstowy. W przypadku dużej lub szczegółowej hierarchii widoków znalezienie widoku może potrwać wystarczająco długo, aby spowolnić działanie aplikacji u użytkownika. Buforowanie widoków w zmiennych może pomóc, ale nadal musisz zainicjować zmienną dla każdego widoku w każdej przestrzeni nazw. To przekłada się też na liczbę wyświetleń i wiele aktywności.

Jednym z rozwiązań jest utworzenie obiektu zawierającego odwołanie do każdego widoku. Ten obiekt, nazywany obiektem Binding, może być używany przez całą aplikację. Ta technika nazywa się powiązaniem danych. Po utworzeniu obiektu wiązania na potrzeby aplikacji możesz uzyskać dostęp do widoków i innych danych za pomocą obiektu wiązania, bez konieczności poruszania się po hierarchii widoku i wyszukiwania danych.

Powiązanie danych ma te zalety:

  • Kod jest krótszy, łatwiejszy do odczytania i łatwiejszy w utrzymaniu niż kod korzystający z tagu findByView().
  • Dane i widoki danych są wyraźnie oddzielone. Korzyści wynikające z powiązania danych stają się coraz ważniejsze w dalszej części tego kursu.
  • System Android przegląda wyniki tylko raz, aby pobrać każdy widok. Dzieje się tak podczas uruchamiania aplikacji, a nie w czasie, gdy użytkownik wchodzi w interakcję z aplikacją.
  • Otrzymujesz bezpieczeństwo typu podczas uzyskiwania dostępu do widoków. (Bezpieczeństwo typu oznacza, że kompilator weryfikuje typy podczas kompilowania, a jeśli spróbujesz przypisać do niego nieprawidłowy typ, wyświetli się błąd).

W tym zadaniu konfigurujesz wiązanie danych i używasz wiązania danych, aby zastąpić wywołania findViewById() przez wywołania obiektu powiązania.

Krok 1. Włącz wiązanie danych

Aby użyć wiązania danych, musisz włączyć wiązanie danych w pliku Gradle, ponieważ nie jest ono włączone domyślnie. 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 poprzedniego ćwiczenia z programowania, pobierz z GitHuba kod AboutMeDataBinding-Starter. Otwórz ją w Android Studio.
  2. Otwórz plik build.gradle (Module: app).
  3. W sekcji android przed nawiasem zamykającym dodaj sekcję dataBinding i ustaw enabled na true.
dataBinding {
    enabled = true
}
  1. Gdy pojawi się taka prośba, zsynchronizuj projekt. Jeśli nie pojawi się prośba, wybierz File > Sync Project with Gradle Files.
  2. Możesz uruchomić aplikację, ale nie zobaczysz żadnych zmian.

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

Aby tworzyć powiązania danych, musisz pakować układ XML tagiem <layout>. Dzięki temu klasa główna nie jest już grupą widoku danych, a zamiast nich zawiera układ i widoki danych. Będzie on wówczas wiedział o układzie i widokach, które się w nim znajdują.

  1. Otwórz plik activity_main.xml.
  2. Otwórz kartę Tekst.
  3. Dodaj tag <layout></layout> jako najbardziej zewnętrzny tag wokół obiektu <LinearLayout>.
<layout>
   <LinearLayout ... >
   ...
   </LinearLayout>
</layout>
  1. Wybierz Kod > Zmień format kodu, aby naprawić wcięcie kodu.

    Deklaracje przestrzeni nazw muszą znajdować się w tagu zewnętrznym.
  1. Wytnij deklaracje przestrzeni nazw z <LinearLayout> i wklej je w tagu <layout>. Tag otwierający <layout> powinien wyglądać tak 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. Przygotuj i uruchom aplikację, aby sprawdzić, czy wszystko zostało poprawnie ustawione.

Krok 3. Utwórz obiekt wiązania w głównej aktywności

Dodaj odniesienie do obiektu powiązania do głównej aktywności, aby móc go używać do uzyskania dostępu do widoków:

  1. Otwórz plik MainActivity.kt.
  2. Przed onCreate() utwórz na najwyższym poziomie zmienną dla obiektu wiązania. Ta zmienna nosi zwykle nazwę binding.

    Typ binding, klasa ActivityMainBinding, jest tworzony przez kompilator specjalnie dla tej głównej aktywności. Nazwa pochodzi od nazwy pliku układu, czyli „activity_main + Binding”.
private lateinit var binding: ActivityMainBinding
  1. Jeśli pojawi się prośba w Android Studio, zaimportuj ActivityMainBinding. Jeśli nie pojawi się taka prośba, kliknij ActivityMainBinding i naciśnij Alt+Enter (Option+Enter na Macu), by zaimportować tę brakujące zajęcia. (więcej informacji o skrótach klawiszowych znajdziesz w sekcji Skróty klawiszowe).

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

Następnie zastąp obecną funkcję setContentView() instrukcją, która wykonuje tę czynność:

  • Utworzy obiekt powiązania.
  • Używa funkcji setContentView() z klasy DataBindingUtil do powiązania układu activity_main z MainActivity. Ta funkcja setContentView() obsługuje też niektóre konfiguracje wiązania danych dla widoków.
  1. W 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. Zastąp wszystkie wywołania funkcji „ViewByById()” za pomocą obiektu wiązania

Możesz teraz zastąpić wszystkie wywołania findViewById() przez odwołania do widoków znajdujących się w obiekcie wiązania. Podczas tworzenia obiektu wiązania kompilator generuje nazwy widoków w obiekcie wiązania na podstawie identyfikatorów widoków w układzie, przekształcając je w wielkość liter wielbłąda. Przykład: done_button ma postać doneButton w wiążącym obiekcie, nickname_edit zmienia się w nicknameEdit, a nickname_textnicknameText.

  1. W sekcji onCreate() zastąp kod korzystający z tagu findViewById(), aby znaleźć element done_button kodem, który odwołuje się do przycisku w obiekcie wiązania.

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

    Ukończony kod ustawiający detektor 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 elementu findViewById<View>(R.id.id_view) binding.idView. Aby to zrobić:
  • Usuń definicje zmiennych editText i nicknameTextView wraz z ich wywołaniami findViewById(). Pojawią się błędy.
  • Napraw błędy, pobierając widoki nicknameText, nicknameEdit i doneButton z obiektu binding zamiast (usuniętych) zmiennych.
  • Zastąp view.visibility wartością binding.doneButton.visibility. Użycie kodu binding.doneButton zamiast przekazanego kodu view zapewnia spójność kodu.

    Uzyskasz taki kod:
binding.nicknameText.text = binding.nicknameEdit.text
binding.nicknameEdit.visibility = View.GONE
binding.doneButton.visibility = View.GONE
binding.nicknameText.visibility = View.VISIBLE
  • Nie ma zmian w działaniu tej funkcji. Możesz też wyeliminować parametr view i zaktualizować wszystkie zastosowania parametru view, by binding.doneButton korzystały z tej funkcji.
  1. nicknameText wymaga String, a nicknameEdit.text to Editable. Przy wiązaniu danych musisz wyraźnie przekonwertować Editable na String.
binding.nicknameText.text = binding.nicknameEdit.text.toString()
  1. Możesz usunąć wyszarzone operacje importowania.
  2. Połącz funkcję z parametrem apply{}.
binding.apply {
   nicknameText.text = nicknameEdit.text.toString()
   nicknameEdit.visibility = View.GONE
   doneButton.visibility = View.GONE
   nicknameText.visibility = View.VISIBLE
}
  1. Utwórz i uruchom swoją aplikację...powinna wyglądać i działać tak samo jak dotychczas.

Dzięki wiązaniu danych możesz udostępniać klasę danych bezpośrednio w widoku danych. Ta metoda upraszcza kod i jest niezwykle przydatna do obsługi bardziej złożonych przypadków.

W tym przykładzie zamiast ustawiać nazwę i pseudonim za pomocą zasobów ciągu znaków, utworzysz klasę danych dla nazwy i pseudonimu. Klasę danych udostępniasz w widoku danych za pomocą wią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 o nazwie MyName.kt.
  2. Określ klasę danych dla nazwy i pseudonimu. Użyj pustych ciągó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 ustawiana w TextView z zasobu ciągu znaków. Musisz zastąpić odwołanie do nazwy odwołaniem do danych w klasie danych.

  1. Otwórz kartę activity_main.xml na karcie Tekst.
  2. U góry układu wstaw tag <data></data>, między tagami <layout> i <LinearLayout>. Tutaj połączysz widok danych.
<data>
  
</data>

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

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

Teraz zamiast używać ciągu znaków jako nazwy możesz wskazać zmienną myName.

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

@={} to dyrektywa służąca do pobierania danych, które znajdują się między nawiasami klamrowymi.

myName odwołuje się do zdefiniowanej wcześniej 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 dane.

  1. Otwórz plik MainActivity.kt.
  2. Powyżej onCreate() utwórz zmienną prywatną zwaną też myName zgodnie z konwencją. Przypisz zmienną do wystąpienia klasy danych MyName, przekazując nazwę.
private val myName: MyName = MyName("Aleks Haecky")
  1. W onCreate() ustaw zmienną myName w pliku układu na wartość zadeklarowanej przed chwilą zmiennej myName. Nie można uzyskać dostępu bezpośrednio do tej zmiennej w pliku XML. Musisz uzyskać do niego dostęp za pomocą obiektu wiązania.
binding.myName = myName
  1. Może to powodować błąd, ponieważ po wprowadzeniu zmian musisz odświeżyć obiekt powiązania. Utwórz aplikację, a błąd powinien zniknąć.

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

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

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

Po ustawieniu pseudonimu chcesz, aby kod odświeżał interfejs użytkownika za pomocą nowych danych. Aby to zrobić, unieważnij wszystkie wyrażenia wiążące się z odniesieniem do prawidłowych danych.

  1. Dodaj invalidateAll() po ustawieniu pseudonimu, aby interfejs użytkownika został odświeżony przy użyciu wartości ze zaktualizowanego obiektu powiązania.
binding.apply {
   myName?.nickname = nicknameEdit.text.toString()
   invalidateAll()
   ...
}
  1. Utwórz i uruchom swoją aplikację. Powinna ona działać dokładnie tak samo jak dotychczas.

Projekt w Android Studio: AboutMeDataBinding

Instrukcja zastępowania połączeń do findViewById():

  1. Włącz wiązanie danych w sekcji Androida 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 elemencie MainActivity, zastępując setContentView:
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
  5. Zamień wywołania findViewById() na odwołania do widoku w obiekcie wiązania. Przykład:
    findViewById<Button>(R.id.done_button) ⇒ binding.doneButton
    (w tym przykładzie nazwa widoku jest tworzona z użyciem wielbłąda na podstawie wartości id w widoku danych).

Aby powiązać widoki danych z danymi:

  1. Utwórz klasę danych dla swoich danych.
  2. Dodaj blok <data> wewnątrz tagu <layout>.
  3. Określ <variable> za pomocą nazwy i typu, który jest klasą danych.
<data>
   <variable
       name="myName"
       type="com.example.android.aboutme.MyName" />
</data>
  1. W MainActivity utwórz zmienną z instancją klasy danych. Na przykład:
    private val myName: MyName = MyName("Aleks Haecky")
  1. W obiekcie wiązania ustaw zmienną, która właśnie została utworzona:
    binding.myName = myName
  1. W pliku XML ustaw zawartość widoku na zmienną określoną w bloku <data>. Aby uzyskać dostęp do danych w klasie danych, użyj kropki.
    android:text="@={myName.name}"

Kurs Udacity:

Dokumentacja dla programistów Androida:

Ta sekcja zawiera listę możliwych zadań domowych dla uczniów, którzy pracują w ramach tego ćwiczenia w ramach kursu prowadzonego przez nauczyciela. To nauczyciel może wykonać te czynności:

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

Nauczyciele mogą wykorzystać te sugestie tak długo, jak chcą lub chcą, i mogą przypisać dowolne zadanie domowe.

Jeśli samodzielnie wykonujesz te ćwiczenia z programowania, możesz sprawdzić swoją wiedzę w tych zadaniach domowych.

Odpowiedz na te pytania

Pytanie 1

Dlaczego chcesz ograniczyć liczbę bezpośrednich i pośrednich wywołań funkcji findViewById()?

  • Przy każdym wywołaniu findViewById() następuje hierarchia widoku.
  • findViewById() działa w głównym wątku lub interfejsie użytkownika.
  • Te wywołania mogą spowolnić interfejs użytkownika.
  • Jest mniej prawdopodobne, że aplikacja ulegnie awarii.

Pytanie 2

Jak możesz opisać wiązanie danych?

Oto kilka uwag o wiązaniu danych:

  • Najważniejszym założeniem wiązania danych jest utworzenie obiektu, który łączy, odnajduje i łączy dwa odległe informacje w czasie kompilacji, dzięki czemu nie trzeba szukać tych danych w czasie działania.
  • Obiekt, który wyświetla Ci te powiązania, jest nazywany obiektem wiązania.
  • Obiekt powiązania jest tworzony przez kompilator.

Pytanie 3

Która z poniższych przyczyn NIE jest korzyścią z powiązania danych?

  • Kod jest krótszy, łatwiejszy do odczytania i łatwiejszy w utrzymaniu.
  • Dane i widoki danych są wyraźnie oddzielone.
  • Aby uzyskać każdy widok, system Android przechodzi tylko przez hierarchię widoków.
  • Wywołanie findViewById() powoduje błąd kompilatora.
  • Zadbaj o bezpieczeństwo, gdy chcesz uzyskać dostęp do widoków.

Pytanie 4

Jaka jest funkcja tagu <layout>?

  • który otacza widok główny w układzie.
  • Powiązania są tworzone dla wszystkich widoków układu.
  • Wskazuje widok najwyższego poziomu w układzie XML, w którym używane jest wiązanie danych.
  • Możesz użyć tagu <data> w obrębie znaczników <layout>, aby powiązać zmienną z klasą danych.

Pytanie 5

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

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

Rozpocznij następną lekcję: 3.1 Tworzenie fragmentu

Linki do innych ćwiczeń z programowania w tym kursie znajdziesz na stronie docelowej z ćwiczeniami z podstaw Androida Kotlin.