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.
- Jeśli nie masz aplikacji AboutMe z poprzednich zajęć, pobierz kod AboutMeDataBinding-Starter z GitHub. Otwórz go w Android Studio.
- Otwórz plik
build.gradle (Module: app). - W sekcji
androidprzed nawiasem zamykającym dodaj sekcjędataBindingi ustaw wartośćenablednatrue.
dataBinding {
enabled = true
}- 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).
- 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.
- Otwórz plik
activity_main.xml. - Przejdź na kartę Tekst.
- Dodaj tag
<layout></layout>jako najbardziej zewnętrzny tag wokół tagu<LinearLayout>.
<layout>
<LinearLayout ... >
...
</LinearLayout>
</layout>
- 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.
- 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">
- 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:
- Otwórz plik
MainActivity.kt. - Przed
onCreate()na najwyższym poziomie utwórz zmienną dla obiektu wiązania. Zmienna ta jest zwykle nazywanabinding.
Typbinding, czyli klasaActivityMainBinding, jest tworzony przez kompilator specjalnie na potrzeby tego głównego działania. Nazwa pochodzi od nazwy pliku układu, czyliactivity_main + Binding.
private lateinit var binding: ActivityMainBinding- Jeśli Android Studio wyświetli odpowiedni komunikat, zaimportuj
ActivityMainBinding. Jeśli nie pojawi się prośba, kliknijActivityMainBindingi naciśnijAlt+Enter(Option+Enterna Macu), aby zaimportować brakujące zajęcia. (Więcej skrótów klawiszowych znajdziesz w artykule Skróty klawiszowe).
Instrukcjaimportpowinna wyglądać podobnie do tej poniżej.
import com.example.android.aboutme.databinding.ActivityMainBindingNastępnie zastąp bieżącą funkcję setContentView() instrukcją, która wykonuje te czynności:
- Tworzy obiekt powiązania.
- Używa funkcji
setContentView()z klasyDataBindingUtil, aby powiązać układactivity_mainz elementemMainActivity. Ta funkcjasetContentView()zajmuje się też konfiguracją powiązań danych w przypadku widoków.
- W pliku
onCreate()zastąp wywołaniesetContentView()tym wierszem kodu:
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
- Importuj
DataBindingUtil.
import androidx.databinding.DataBindingUtilKrok 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_text w nicknameText.
- W
onCreate()zastąp kod, który używafindViewById()do znalezieniadone_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ęć wonCreate()powinien wyglądać tak.
binding.doneButton.setOnClickListener {
addNickname(it)
}- Zrób to samo w przypadku wszystkich wywołań funkcji
findViewById()w funkcjiaddNickname().
Zastąp wszystkie wystąpienia ciągufindViewById<View>(R.id.id_view)ciągiembinding.idView. Zrób to w ten sposób:
- Usuń definicje zmiennych
editTextinicknameTextViewwraz z wywołaniami funkcjifindViewById(). Spowoduje to błędy. - Napraw błędy, pobierając widoki
nicknameText,nicknameEditidoneButtonz obiektubindingzamiast z usuniętych zmiennych. - Zastąp
view.visibilitytekstembinding.doneButton.visibility. Użycie wartościbinding.doneButtonzamiast przekazanej wartościviewsprawia, ż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
viewi zaktualizować wszystkie użycia parametruview, aby w tej funkcji używać parametrubinding.doneButton.
nicknameTextwymagaString, anicknameEdit.texttoEditable. W przypadku wiązania danych konieczne jest jawne przekształcenieEditablenaString.
binding.nicknameText.text = binding.nicknameEdit.text.toString()- Możesz usunąć zacienione importy.
- 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
}- 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
- W Android Studio w katalogu
javaotwórz plikMyName.kt. Jeśli nie masz tego pliku, utwórz nowy plik Kotlin i nadaj mu nazwęMyName.kt. - 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.
- Otwórz
activity_main.xmlna karcie Tekst. - U góry układu, między tagami
<layout>i<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.
- W tagu
<data>dodaj tag<variable>. - Dodaj parametr
name, aby nadać zmiennej nazwę"myName". Dodaj parametrtypei ustaw typ na w pełni kwalifikowaną nazwę klasy danychMyName(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.
- 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.
- Otwórz plik
MainActivity.kt. - Nad
onCreate()utwórz zmienną prywatną, która zgodnie z konwencją również będzie się nazywaćmyName. Przypisz zmiennej instancję klasy danychMyName, przekazując nazwę.
private val myName: MyName = MyName("Aleks Haecky")- W
onCreate()ustaw wartość zmiennejmyNamew pliku układu na wartość zmiennejmyName, 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- 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.
- Otwórz pokój
activity_main.xml. - W widoku tekstowym
nickname_textdodaj właściwośćtext. Odwołaj się donicknamew klasie danych, jak pokazano poniżej.
android:text="@={myName.nickname}"- W
ActivityMainzastąpnicknameText.text = nicknameEdit.text.toString()
kodem, aby ustawić pseudonim w zmiennejmyName.
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.
- 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()
...
}- 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:
- Włącz powiązanie danych w sekcji android pliku
build.gradle:dataBinding { enabled = true } - Użyj
<layout>jako widoku głównego w układzie XML. - Zdefiniuj zmienną wiązania:
private lateinit var binding: ActivityMainBinding - Utwórz obiekt wiązania w usłudze
MainActivity, zastępującsetContentView:binding = DataBindingUtil.setContentView(this, R.layout.activity_main) - 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 podstawieidwidoku w XML).
Instrukcje dotyczące wiązania widoków z danymi:
- Utwórz klasę danych dla swoich danych.
- Dodaj blok
<data>w tagu<layout>. - Zdefiniuj
<variable>z nazwą i typem, który jest klasą danych.
<data>
<variable
name="myName"
type="com.example.android.aboutme.MyName" />
</data>- W
MainActivityutwórz zmienną z instancją klasy danych. Na przykład:private val myName: MyName = MyName("Aleks Haecky")
- W obiekcie powiązania ustaw zmienną na utworzoną zmienną:
binding.myName = myName
- 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:
- Biblioteka powiązań danych
- Wygenerowane klasy powiązań
- Pierwsze kroki z powiązaniem danych
- Układy i wyrażenia wiążące
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ę:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.