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 poprzednim laboratorium kodowania w aplikacji GuessTheWord użyto ViewModel
, aby dane aplikacji przetrwały zmiany konfiguracji urządzenia. Z tego modułu dowiesz się, jak zintegrować LiveData
z danymi w klasach ViewModel
. LiveData
, który jest jednym z komponentów architektury Androida, umożliwia tworzenie obiektów danych, które powiadamiają widoki o zmianach w bazie danych.
Aby użyć klasy LiveData
, musisz skonfigurować „obserwatorów” (np. działania lub fragmenty), którzy będą śledzić zmiany w danych aplikacji. LiveData
jest świadomy cyklu życia, więc aktualizuje tylko obserwatorów komponentów aplikacji, którzy są w aktywnym stanie cyklu życia.
Co warto wiedzieć
- Jak tworzyć podstawowe aplikacje na Androida w języku Kotlin.
- Jak poruszać się między miejscami docelowymi w aplikacji.
- Cykl życia działania i fragmentu.
- Jak używać obiektów
ViewModel
w aplikacji. - Jak tworzyć obiekty
ViewModel
za pomocą interfejsuViewModelProvider.Factory
.
Czego się nauczysz
- Co sprawia, że
LiveData
obiektów jest przydatnych. - Jak dodać
LiveData
do danych przechowywanych wViewModel
. - Kiedy i jak używać
MutableLiveData
. - Jak dodać metody obserwatora, aby obserwować zmiany w
LiveData.
- Jak enkapsulować
LiveData
za pomocą właściwości pomocniczej. - Jak komunikować się między kontrolerem interfejsu a odpowiadającym mu elementem
ViewModel
.
Jakie zadania wykonasz
- Użyj
LiveData
dla słowa i wyniku w aplikacji GuessTheWord. - Dodaj obserwatorów, którzy będą reagować na zmianę słowa lub wyniku.
- Aktualizuj widoki tekstu, które wyświetlają zmienione wartości.
- Użyj wzorca
LiveData
obserwatora, aby dodać zdarzenie zakończenia gry. - Zaimplementuj przycisk Zagraj jeszcze raz.
W ćwiczeniach z programowania w lekcji 5 tworzysz aplikację GuessTheWord, zaczynając od kodu początkowego. GuessTheWord to gra w kalambury dla 2 osób, w której gracze współpracują, aby uzyskać jak najwyższy wynik.
Pierwszy gracz patrzy na słowa w aplikacji i kolejno odgrywa każde z nich, uważając, aby nie pokazać słowa drugiemu graczowi. Drugi gracz próbuje odgadnąć słowo.
Aby rozpocząć grę, pierwszy gracz otwiera aplikację na urządzeniu i widzi słowo, np. „gitara”, jak pokazano na zrzucie ekranu poniżej.
Pierwszy gracz odgrywa słowo, uważając, aby go nie wypowiedzieć.
- Gdy drugi gracz odgadnie słowo, pierwszy gracz naciśnie przycisk Got It (Mam to), co zwiększy liczbę o 1 i wyświetli kolejne słowo.
- Jeśli drugi gracz nie odgadnie słowa, pierwszy gracz naciśnie przycisk Pomiń, co zmniejszy liczbę o 1 i spowoduje przejście do następnego słowa.
- Aby zakończyć grę, naciśnij przycisk Zakończ grę. (Ta funkcja nie jest dostępna w kodzie początkowym pierwszego laboratorium w tej serii).
W tym laboratorium nauczysz się ulepszać aplikację GuessTheWord, dodając zdarzenie, które kończy grę, gdy użytkownik przejdzie przez wszystkie słowa w aplikacji. Dodasz też przycisk Zagraj ponownie w fragmencie wyniku, aby użytkownik mógł ponownie zagrać w grę.
Ekran tytułowy | Ekran gry | Ekran wyników |
W tym zadaniu znajdziesz i uruchomisz kod początkowy do tego ćwiczenia z programowania. Możesz użyć aplikacji GuessTheWord utworzonej w poprzednim laboratorium, lub pobrać aplikację startową.
- (Opcjonalnie) Jeśli nie używasz kodu z poprzednich ćwiczeń z programowania, pobierz kod początkowy do tych ćwiczeń. Rozpakuj kod i otwórz projekt w Android Studio.
- Uruchom aplikację i zagraj w grę.
- Zauważ, że przycisk Pomiń wyświetla następne słowo i zmniejsza wynik o 1, a przycisk Rozumiem wyświetla następne słowo i zwiększa wynik o 1. Przycisk Zakończ grę kończy grę.
LiveData
to klasa przechowująca dane, które można obserwować, i która uwzględnia cykl życia. Możesz na przykład umieścić element LiveData
wokół aktualnego wyniku w aplikacji GuessTheWord. W tym laboratorium dowiesz się więcej o kilku cechach elementu LiveData
:
LiveData
jest obserwowalny, co oznacza, że obserwator otrzymuje powiadomienie, gdy dane przechowywane przez obiektLiveData
ulegną zmianie.LiveData
przechowuje dane;LiveData
to otoczka, której można używać z dowolnymi danymi.LiveData
uwzględnia cykl życia, co oznacza, że aktualizuje tylko obserwatorów, którzy są w aktywnym stanie cyklu życia, np.STARTED
lubRESUMED
.
W tym zadaniu dowiesz się, jak opakować dowolny typ danych w obiekty LiveData
, przekształcając bieżące dane o wyniku i bieżące dane o słowie w GameViewModel
na LiveData
. W późniejszym zadaniu dodasz do tych obiektów LiveData
obserwatora i dowiesz się, jak obserwować LiveData
.
Krok 1. Zmień wynik i słowo, aby używać LiveData
- W pakiecie
screens/game
otwórz plikGameViewModel
. - Zmień typ zmiennych
score
iword
naMutableLiveData
.MutableLiveData
toLiveData
, którego wartość można zmienić.MutableLiveData
to klasa ogólna, więc musisz określić typ danych, które zawiera.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
- W pliku
GameViewModel
w blokuinit
zainicjuj zmiennescore
iword
. Aby zmienić wartość zmiennejLiveData
, użyj metodysetValue()
. W języku Kotlin możesz wywołać funkcjęsetValue()
za pomocą właściwościvalue
.
init {
word.value = ""
score.value = 0
...
}
Krok 2. Zaktualizuj odwołanie do obiektu LiveData
Zmienne score
i word
mają teraz typ LiveData
. W tym kroku zmienisz odwołania do tych zmiennych, używając właściwości value
.
- W
GameViewModel
w metodzieonSkip()
zmieńscore
nascore.value
. Zwróć uwagę na błąd dotyczący tego, żescore
może byćnull
. Ten błąd naprawisz w następnym kroku. - Aby rozwiązać ten problem, dodaj znak
null
doscore.value
wonSkip()
. Następnie wywołaj funkcjęminus()
nascore
, która wykonuje odejmowanie znull
-bezpieczeństwem.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}
- Zaktualizuj metodę
onCorrect()
w ten sam sposób: dodaj sprawdzenienull
do zmiennejscore
i użyj funkcjiplus()
.
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}
- W
GameViewModel
w metodzienextWord()
zmień odwołanieword
naword
.
value
.
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}
- W
GameFragment
w metodzieupdateWordText()
zmień odwołanie doviewModel
.word
naviewModel
.
word
.
value.
.
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}
- W funkcji
GameFragment
w metodzieupdateScoreText()
zmień odwołanie doviewModel
.score
naviewModel
.
score
.
value.
.
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}
- W pliku
GameFragment
w metodziegameFinished()
zmień odwołanie doviewModel
.score
naviewModel
.
score
.
value
. Dodaj wymaganenull
-safety check.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
NavHostFragment.findNavController(this).navigate(action)
}
- Sprawdź, czy w kodzie nie ma błędów. Skompiluj i uruchom aplikację. Jej funkcje powinny być takie same jak wcześniej.
To zadanie jest ściśle powiązane z poprzednim, w którym przekonwertowano dane dotyczące wyników i słów na obiekty LiveData
. W tym zadaniu do obiektów LiveData
dołączysz obiekty Observer
.
- W
GameFragment,
w metodzieonCreateView()
dołącz obiektObserver
do obiektuLiveData
dla bieżącego wyniku,viewModel.score
. Użyj metodyobserve()
i umieść kod po inicjowaniuviewModel
. Użyj wyrażenia lambda, aby uprościć kod. (Wyrażenie lambda to funkcja anonimowa, która nie jest deklarowana, ale jest przekazywana natychmiast jako wyrażenie).
viewModel.score.observe(this, Observer { newScore ->
})
Rozwiąż problem z odwołaniem do Observer
. Aby to zrobić, kliknij Observer
, naciśnij Alt+Enter
(Option+Enter
na komputerze Mac) i zaimportuj androidx.lifecycle.Observer
.
- Obserwator, którego właśnie utworzono, otrzymuje zdarzenie, gdy zmienią się dane przechowywane przez obserwowany obiekt
LiveData
. W obserwatorze zaktualizuj wynikTextView
, wpisując nowy wynik.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Dołącz obiekt
Observer
do obiektuLiveData
bieżącego słowa. Zrób to w taki sam sposób, w jaki dołączyłeś(-aś) obiektObserver
do bieżącego wyniku.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
Gdy zmieni się wartość score
lub word
, wyświetlane na ekranie score
lub word
zostaną automatycznie zaktualizowane.
- W
GameFragment
usuń metodyupdateWordText()
iupdateScoreText()
oraz wszystkie odwołania do nich. Nie są już potrzebne, ponieważ widoki tekstu są aktualizowane przez metody obserwatoraLiveData
. - Uruchom aplikację. Gra powinna działać tak samo jak wcześniej, ale teraz korzysta z obserwatorów
LiveData
iLiveData
.
Hermetyzacja to sposób na ograniczenie bezpośredniego dostępu do niektórych pól obiektu. Gdy enkapsulujesz obiekt, udostępniasz zestaw publicznych metod, które modyfikują prywatne pola wewnętrzne. Dzięki hermetyzacji możesz kontrolować, jak inne klasy manipulują tymi polami wewnętrznymi.
W bieżącym kodzie każda klasa zewnętrzna może modyfikować zmienne score
i word
za pomocą właściwości value
, np. używając viewModel.score.value
. W aplikacji, którą tworzysz w tym laboratorium, może to nie mieć znaczenia, ale w aplikacji produkcyjnej chcesz mieć kontrolę nad danymi w obiektach ViewModel
.
Tylko ViewModel
powinna edytować dane w aplikacji. Kontrolery interfejsu użytkownika muszą jednak odczytywać dane, więc pola danych nie mogą być całkowicie prywatne. Do hermetyzacji danych aplikacji używasz obiektów MutableLiveData
i LiveData
.
MutableLiveData
– LiveData
:
- Dane w obiekcie
MutableLiveData
można zmieniać, jak sama nazwa wskazuje. WewnątrzViewModel
dane powinny być edytowalne, więc używa sięMutableLiveData
. - Dane w obiekcie
LiveData
można odczytywać, ale nie można ich zmieniać. Dane spozaViewModel
powinny być dostępne do odczytu, ale nie do edycji, dlatego powinny być udostępniane jakoLiveData
.
Aby zrealizować tę strategię, użyj właściwości pomocniczej w języku Kotlin. Właściwość pomocnicza umożliwia zwrócenie z funkcji pobierającej czegoś innego niż dokładny obiekt. W tym zadaniu zaimplementujesz właściwość pomocniczą dla obiektów score
i word
w aplikacji GuessTheWord.
Dodawanie właściwości pomocniczej do wyniku i słowa
- W
GameViewModel
ustaw bieżący obiektscore
jakoprivate
. - Aby zachować konwencję nazewnictwa stosowaną we właściwościach pomocniczych, zmień
score
na_score
. Właściwość_score
jest teraz wersją wyniku gry, którą można zmieniać i która jest używana wewnętrznie. - Utwórz publiczną wersję typu
LiveData
o nazwiescore
.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
- Wyświetla się błąd inicjowania. Ten błąd występuje, ponieważ wewnątrz
GameFragment
elementscore
jest odwołaniem doLiveData
, ascore
nie ma już dostępu do jego funkcji ustawiającej. Więcej informacji o getterach i setterach w Kotlinie znajdziesz w artykule Getters and Setters (Gettery i settery).
Aby rozwiązać ten problem, zastąp metodęget()
dla obiektuscore
wGameViewModel
i zwróć właściwość pomocniczą_score
.
val score: LiveData<Int>
get() = _score
- W
GameViewModel
zmień odwołania doscore
na jego wewnętrzną wersję modyfikowalną, czyli_score
.
init {
...
_score.value = 0
...
}
...
fun onSkip() {
if (!wordList.isEmpty()) {
_score.value = (score.value)?.minus(1)
}
...
}
fun onCorrect() {
if (!wordList.isEmpty()) {
_score.value = (score.value)?.plus(1)
}
...
}
- Zmień nazwę obiektu
word
na_word
i dodaj do niego właściwość pomocniczą, tak jak w przypadku obiektuscore
.
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
get() = _word
...
init {
_word.value = ""
...
}
...
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
_word.value = wordList.removeAt(0)
}
}
Świetna robota! Udało Ci się zamknąć w ramce LiveData
obiekty word
i score
.
Obecna aplikacja przechodzi do ekranu z wynikami, gdy użytkownik kliknie przycisk Zakończ grę. Chcesz też, aby po przejściu przez wszystkie słowa aplikacja przenosiła graczy na ekran z wynikami. Po tym, jak gracze skończą z ostatnim słowem, gra powinna zakończyć się automatycznie, aby użytkownik nie musiał klikać przycisku.
Aby wdrożyć tę funkcję, musisz wywołać zdarzenie i przekazać je do fragmentu z ViewModel
, gdy wyświetlą się wszystkie słowa. W tym celu użyj wzorca obserwatora LiveData
, aby modelować zdarzenie zakończenia gry.
Wzorzec obserwatora
Wzorzec obserwatora to wzorzec projektowy oprogramowania. Określa komunikację między obiektami: obserwowanym (czyli „obiektem” obserwacji) i obserwatorami. Obserwowany obiekt to obiekt, który powiadamia obserwatorów o zmianach w swoim stanie.
W przypadku LiveData
w tej aplikacji obserwowanym obiektem (podmiotem) jest obiekt LiveData
, a obserwatorami są metody w kontrolerach interfejsu, np. fragmenty. Zmiana stanu następuje za każdym razem, gdy zmieniają się dane zawarte w LiveData
. Klasy LiveData
są kluczowe w komunikacji z ViewModel
do fragmentu.
Krok 1. Użyj LiveData do wykrywania zdarzenia zakończenia gry
W tym zadaniu użyjesz wzorca obserwatora LiveData
do modelowania zdarzenia zakończenia gry.
- W
GameViewModel
utwórz obiektBoolean
MutableLiveData
o nazwie_eventGameFinish
. Ten obiekt będzie zawierać zdarzenie zakończenia gry. - Po zainicjowaniu obiektu
_eventGameFinish
utwórz i zainicjuj właściwość pomocniczą o nazwieeventGameFinish
.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish
- W
GameViewModel
dodaj metodęonGameFinish()
. W metodzie ustaw zdarzenie zakończenia gry,eventGameFinish
, natrue
.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}
- W
GameViewModel
w metodzienextWord()
zakończ grę, jeśli lista słów jest pusta.
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}
- W
GameFragment
wonCreateView()
po zainicjowaniuviewModel
dołącz obserwatora doeventGameFinish
. używaj metodyobserve()
. W funkcji lambda wywołaj metodęgameFinished()
.
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
if (hasFinished) gameFinished()
})
- Uruchom aplikację, zagraj w grę i przejdź przez wszystkie słowa. Aplikacja automatycznie przechodzi do ekranu z wynikami, zamiast pozostawać w fragmencie gry, dopóki nie klikniesz Zakończ grę.
Gdy lista słów jest pusta, ustawiana jest wartośćeventGameFinish
, wywoływana jest powiązana metoda obserwatora we fragmencie gry, a aplikacja przechodzi do fragmentu ekranu. - Dodany kod spowodował problem z cyklem życia. Aby zrozumieć problem, w klasie
GameFragment
zmień w komentarz kod nawigacji w metodziegameFinished()
. Pamiętaj, aby w metodzie zachować komunikatToast
.
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
// val action = GameFragmentDirections.actionGameToScore()
// action.score = viewModel.score.value?:0
// NavHostFragment.findNavController(this).navigate(action)
}
- Uruchom aplikację, zagraj w grę i przejdź przez wszystkie słowa. U dołu ekranu gry na krótko pojawi się komunikat „Gra właśnie się skończyła”, co jest oczekiwanym zachowaniem.
Teraz obróć urządzenie lub emulator. Toast wyświetli się ponownie. Obróć urządzenie jeszcze kilka razy, a powiadomienie prawdopodobnie będzie się wyświetlać za każdym razem. To błąd, ponieważ powiadomienie powinno wyświetlać się tylko raz, po zakończeniu gry. Toast nie powinien być wyświetlany za każdym razem, gdy fragment jest ponownie tworzony. Rozwiążesz ten problem w kolejnym zadaniu.
Krok 2. Zresetuj zdarzenie zakończenia gry
Zwykle LiveData
dostarcza aktualizacje do obserwatorów tylko wtedy, gdy dane się zmienią. Wyjątkiem od tego zachowania jest to, że obserwatorzy otrzymują też aktualizacje, gdy zmieniają stan z nieaktywnego na aktywny.
Dlatego w aplikacji wielokrotnie pojawia się komunikat o zakończeniu gry. Gdy po obróceniu ekranu fragment gry zostanie utworzony ponownie, przechodzi ze stanu nieaktywnego do aktywnego. Obserwator we fragmencie zostanie ponownie połączony z istniejącym ViewModel
i otrzyma aktualne dane. Metoda gameFinished()
zostanie ponownie wywołana i wyświetli się komunikat.
W tym zadaniu rozwiążesz ten problem i wyświetlisz komunikat tylko raz, resetując flagę eventGameFinish
w GameViewModel
.
- W
GameViewModel
dodaj metodęonGameFinishComplete()
, aby zresetować zdarzenie zakończenia gry,_eventGameFinish
.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- W pliku
GameFragment
na końcu plikugameFinished()
wywołaj funkcjęonGameFinishComplete()
na obiekcieviewModel
. (Na razie pozostaw kod nawigacji wgameFinished()
jako komentarz).
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
- Uruchom aplikację i zagraj w grę. Przejdź przez wszystkie słowa, a potem zmień orientację ekranu urządzenia. Komunikat wyświetla się tylko raz.
- W
GameFragment
w metodziegameFinished()
odkomentuj kod nawigacji.
Aby odkomentować kod w Android Studio, wybierz odkomentowane wiersze i naciśnijControl+/
(Command+/
na komputerze Mac).
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score.value?:0
findNavController(this).navigate(action)
viewModel.onGameFinishComplete()
}
Jeśli Android Studio wyświetli odpowiedni komunikat, zaimportuj androidx.navigation.fragment.NavHostFragment.findNavController
.
- Uruchom aplikację i zagraj w grę. Sprawdź, czy po przejściu przez wszystkie słowa aplikacja automatycznie przechodzi do ekranu z wynikiem końcowym.
Doskonale! Aplikacja używa LiveData
, aby wywołać zdarzenie zakończenia gry i przekazać z GameViewModel
do fragmentu gry informację, że lista słów jest pusta. Fragment gry przechodzi do fragmentu wyniku.
W tym zadaniu zmienisz wynik na obiekt LiveData
w pliku ScoreViewModel
i dołączysz do niego obserwatora. To zadanie jest podobne do tego, które wykonano podczas dodawania LiveData
do GameViewModel
.
Wprowadzasz te zmiany w ScoreViewModel
, aby zapewnić kompletność danych i aby wszystkie dane w aplikacji korzystały z LiveData
.
- W polu
ScoreViewModel
zmień typ zmiennejscore
naMutableLiveData
. Zmień jego nazwę zgodnie z konwencją na_score
i dodaj właściwość pomocniczą.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
- W
ScoreViewModel
, w blokuinit
, zainicjuj_score
. Możesz usunąć lub pozostawić log w blokuinit
.
init {
_score.value = finalScore
}
- W
ScoreFragment
wonCreateView()
po zainicjowaniuviewModel
dołącz obserwatora obiektu wynikuLiveData
. W wyrażeniu lambda ustaw wartość wyniku w widoku tekstu wyniku. Usuń zViewModel
kod, który bezpośrednio przypisuje widokowi tekstu wartość wyniku.
Kod do dodania:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
Kod do usunięcia:
binding.scoreText.text = viewModel.score.toString()
Gdy Android Studio wyświetli odpowiedni komunikat, zaimportuj plik androidx.lifecycle.Observer
.
- Uruchom aplikację i zagraj w grę. Aplikacja powinna działać jak wcześniej, ale teraz do aktualizowania wyniku używa
LiveData
i obserwatora.
W tym zadaniu dodasz do ekranu z wynikami przycisk Zagraj jeszcze raz i zastosujesz detektor kliknięć za pomocą zdarzenia LiveData
. Przycisk wywołuje zdarzenie, które powoduje przejście z ekranu wyniku do ekranu gry.
Kod początkowy aplikacji zawiera przycisk Zagraj ponownie, ale jest on ukryty.
- W
res/layout/score_fragment.xml
w przypadku przyciskuplay_again_button
zmień wartość atrybutuvisibility
navisible
.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>
- W sekcji
ScoreViewModel
dodaj obiektLiveData
, który będzie zawierać elementBoolean
o nazwie_eventPlayAgain
. Ten obiekt służy do zapisywania zdarzeniaLiveData
, aby przejść z ekranu z wynikami do ekranu gry.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain
- W
ScoreViewModel
zdefiniuj metody ustawiania i resetowania zdarzenia_eventPlayAgain
.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}
- W
ScoreFragment
dodaj obserwatora doeventPlayAgain
. Umieść kod na końcuonCreateView()
, przed instrukcjąreturn
. W wyrażeniu lambda wróć do ekranu gry i zresetuj wartośćeventPlayAgain
.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})
Po wyświetleniu prośby w Android Studio zaimportuj androidx.navigation.fragment.findNavController
.
- W
ScoreFragment
wonCreateView()
dodaj odbiornik kliknięć do przycisku PlayAgain i wywołaj funkcjęviewModel
.onPlayAgain()
.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Uruchom aplikację i zagraj w grę. Po zakończeniu gry na ekranie wyników pojawi się ostateczny wynik i przycisk Zagraj ponownie. Kliknij przycisk PlayAgain, a aplikacja przeniesie Cię na ekran gry, aby umożliwić Ci ponowne rozpoczęcie rozgrywki.
Dobra robota! Zmieniasz architekturę aplikacji, aby używać LiveData
obiektów w ViewModel
, i dołączasz obserwatorów do obiektów LiveData
. LiveData
powiadamia obiekty obserwujące, gdy wartość przechowywana przez LiveData
ulegnie zmianie.
Projekt Android Studio: GuessTheWord
LiveData
LiveData
to klasa przechowująca dane, które można obserwować, i która jest powiązana z cyklem życia. Jest to jeden z komponentów architektury Androida.- Możesz użyć
LiveData
, aby włączyć automatyczne aktualizowanie interfejsu użytkownika po zmianie danych. LiveData
jest obserwowalny, co oznacza, że obserwator, np. aktywność lub fragment, może otrzymywać powiadomienia o zmianach danych przechowywanych przez obiektLiveData
.LiveData
przechowuje dane; jest to kontener, którego można używać z dowolnymi danymi.LiveData
uwzględnia cykl życia, co oznacza, że aktualizuje tylko obserwatorów, którzy są w aktywnym stanie cyklu życia, np.STARTED
lubRESUMED
.
Aby dodać LiveData
- Zmień typ zmiennych danych w
ViewModel
naLiveData
lubMutableLiveData
.
MutableLiveData
to obiekt LiveData
, którego wartość można zmienić. MutableLiveData
to klasa ogólna, więc musisz określić typ danych, które zawiera.
- Aby zmienić wartość danych przechowywanych przez zmienną
LiveData
, użyj metodysetValue()
w zmiennejLiveData
.
Enkapsulacja LiveData
- Wartość
LiveData
w obrębie elementuViewModel
powinna być edytowalna. PozaViewModel
elementLiveData
powinien być czytelny. Można to zaimplementować za pomocą właściwości pomocniczej w języku Kotlin. - Właściwość pomocnicza w Kotlinie umożliwia zwracanie z metody pobierającej czegoś innego niż dokładny obiekt.
- Aby zamknąć element
LiveData
, użyjprivate
MutableLiveData
wewnątrz elementuViewModel
i zwróć właściwośćLiveData
backing poza elementemViewModel
.
Obserwowalne obiekty LiveData
LiveData
jest zgodny ze wzorcem obserwatora. „Obserwowany” to obiektLiveData
, a obserwatorami są metody w kontrolerach interfejsu, np. fragmenty. Gdy dane zawarte wLiveData
ulegną zmianie, metody obserwatora w kontrolerach interfejsu użytkownika otrzymają powiadomienie.- Aby obiekt
LiveData
był obserwowalny, dołącz do niego obiekt obserwatora w obserwatorach (np. w aktywnościach i fragmentach) za pomocą metodyobserve()
.LiveData
- Ten wzorzec
LiveData
obserwatora może być używany do komunikacji z kontrolerami interfejsu.ViewModel
Kurs Udacity:
Dokumentacja dla deweloperów aplikacji na Androida:
Inne:
- Właściwość pomocnicza w języku Kotlin
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
Jak zamknąć LiveData
w ViewModel
, aby obiekty zewnętrzne mogły odczytywać dane bez możliwości ich aktualizacji?
- W obiekcie
ViewModel
zmień typ danych naprivate
LiveData
. Użyj właściwości pomocniczej, aby udostępnić dane tylko do odczytu typuMutableLiveData
. - W obiekcie
ViewModel
zmień typ danych naprivate
MutableLiveData
. Użyj właściwości pomocniczej, aby udostępnić dane tylko do odczytu typuLiveData
. - W kontrolerze interfejsu zmień typ danych na
private
MutableLiveData
. Użyj właściwości pomocniczej, aby udostępnić dane tylko do odczytu typuLiveData
. - W obiekcie
ViewModel
zmień typ danych naLiveData
. Użyj właściwości pomocniczej, aby udostępnić dane tylko do odczytu typuLiveData
.
Pytanie 2
LiveData
aktualizuje kontroler interfejsu (np. fragment), jeśli jest on w jednym z tych stanów:
- Wznowiono
- W tle
- Wstrzymano
- Zatrzymano
Pytanie 3
W LiveData
wzorcu obserwatora co jest obserwowanym elementem (co jest obserwowane)?
- Metoda obserwacyjna
- Dane w obiekcie
LiveData
- Kontroler interfejsu
- Obiekt
ViewModel
Rozpocznij kolejną lekcję:
Linki do innych ćwiczeń z tego kursu znajdziesz na stronie docelowej ćwiczeń z podstaw języka Kotlin na Androidzie.