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
android
przed nawiasem zamykającym dodaj sekcjędataBinding
i ustaw wartośćenabled
natrue
.
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, kliknijActivityMainBinding
i naciśnijAlt+Enter
(Option+Enter
na Macu), aby zaimportować brakujące zajęcia. (Więcej skrótów klawiszowych znajdziesz w artykule Skróty klawiszowe).
Instrukcjaimport
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 klasyDataBindingUtil
, aby powiązać układactivity_main
z 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.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_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
editText
inicknameTextView
wraz z wywołaniami funkcjifindViewById()
. Spowoduje to błędy. - Napraw błędy, pobierając widoki
nicknameText
,nicknameEdit
idoneButton
z obiektubinding
zamiast z usuniętych zmiennych. - Zastąp
view.visibility
tekstembinding.doneButton.visibility
. Użycie wartościbinding.doneButton
zamiast przekazanej wartościview
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 parametruview
, aby w tej funkcji używać parametrubinding.doneButton
.
nicknameText
wymagaString
, anicknameEdit.text
toEditable
. W przypadku wiązania danych konieczne jest jawne przekształcenieEditable
naString
.
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
java
otwó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.xml
na 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 parametrtype
i 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 myName
zmiennej.
- 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ść zmiennejmyName
w 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_text
dodaj właściwośćtext
. Odwołaj się donickname
w klasie danych, jak pokazano poniżej.
android:text="@={myName.nickname}"
- W
ActivityMain
zastą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.doneBu
tton
(W tym przykładzie nazwa widoku jest generowana w formacie camel case na podstawieid
widoku 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
MainActivity
utwó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.