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 poprzednim ćwiczeniu z programowania użyto ViewModel
w aplikacji GuessTheWord, aby przetrwać zmiany konfiguracji urządzenia w przypadku danych z tej aplikacji. Z tego modułu ćwiczeń dowiesz się, jak zintegrować LiveData
z danymi z klasy ViewModel
. LiveData
, jeden z komponentów architektury Androida, umożliwia tworzenie obiektów danych, które powiadamiają o zmianach w bazie danych.
Aby użyć klasy LiveData
, konfigurujesz serwery &serwery (np. aktywności lub fragmenty), które śledzą zmiany w danych aplikacji. LiveData
ma znaczenie w cyklu życia, więc aktualizuje tylko obserwatorów komponentu aplikacji, które są w aktywnym stanie cyklu.
Co musisz wiedzieć
- Jak tworzyć podstawowe aplikacje na Androida w Kotlinie.
- Poruszanie się między miejscami docelowymi aplikacji.
- Cykl życia aktywności i fragmentu.
- Jak używać obiektów
ViewModel
w aplikacji - Jak tworzyć obiekty
ViewModel
za pomocą interfejsuViewModelProvider.Factory
.
Czego się nauczysz
- Co sprawia, że obiekty
LiveData
są przydatne. - Jak dodać
LiveData
do danych przechowywanych w narzędziuViewModel
. - Kiedy i jak korzystać z
MutableLiveData
. - Dodawanie metod obserwacji w celu obserwowania zmian w narzędziu
LiveData.
- Sposób hermetyzacji obiektu
LiveData
za pomocą właściwości zapasowej. - Sposób komunikacji między kontrolerem interfejsu a powiązanym z nim urządzeniem
ViewModel
.
Jakie zadania wykonasz:
- Użyj słowa
LiveData
i wyniku w aplikacji GuessTheWord. - Dodaj obserwatorów, którzy zaobserwują zmianę słowa lub wyniku.
- Zaktualizuj widoki tekstowe, w których wyświetlane są zmienione wartości.
- Użyj wzoru obserwatora
LiveData
, aby dodać zdarzenie ukończenia gry. - Zaimplementuj przycisk Odtwórz ponownie.
W ćwiczeniach z lekcji na lekcji 5 tworzysz aplikację GuessTheWord, zaczynając od kodu początkowego. GuessTheWord to 2-osobowa gra w stylu charades, w której gracze współpracują, aby osiągnąć najwyższy możliwy wynik.
Pierwszy gracz sprawdza słowa w aplikacji i wykonuje po kolei każdy z nich, starając się nie wyświetlać słów drugiemu graczowi. Drugi gracz próbuje odgadnąć słowo.
Aby zagrać w grę, pierwszy gracz otworzy aplikację na urządzeniu i zobaczy słowo, na przykład "gitara&", jak na zrzucie ekranu poniżej.
Gracz wypowiada słowo, pamiętając, aby nie mówić samego słowa.
- Gdy drugi gracz odgadnie słowo poprawnie, pierwszy naciśnij przycisk OK, co zwiększa liczbę o jeden i wyświetla następne słowo.
- Jeśli drugi gracz nie odgadnie słowa, pierwszy z nich naciśnie przycisk Pomiń, co zmniejszy liczbę o jeden i przeskoczy do następnego.
- Aby zakończyć grę, naciśnij przycisk Zakończ grę. (Ta funkcja nie jest w kodzie początkowym pierwszego modułu ćwiczeń z serii).
Ten ćwiczenie z ćwiczenia z gadżetu pomoże Ci ulepszyć aplikację GuessTheWord. Aby to zrobić, dodaj zdarzenie, aby zakończyć grę, gdy użytkownik przewinie przez wszystkie słowa w aplikacji. Dodajesz też przycisk Zagraj ponownie we fragmencie wyniku, aby użytkownik mógł ponownie zagrać w grę.
Ekran tytułowy | Ekran gry | Ekran wyniku |
W tym zadaniu znajdziesz i uruchomisz kod startowy tego ćwiczenia z programowania. Możesz użyć aplikacji GuessTheWord, która została opracowana w ramach poprzedniego ćwiczenia z programowania, lub pobrać aplikację inicjowaną.
- (Opcjonalnie) Jeśli nie używasz kodu z poprzedniego ćwiczenia z programowaniem, pobierz kod startowy tego ćwiczenia. Rozpakuj kod i otwórz projekt w Android Studio.
- Uruchom aplikację i zagraj w grę.
- Pamiętaj, że przycisk Pomiń wyświetla następne słowo i obniża wynik o jeden, a przycisk OK pokazuje kolejne słowo i zwiększa wynik o jeden. Przycisk Zakończ grę kończy grę.
LiveData
jest obserwowalną klasą posiadacza danych, która uwzględnia cykl życia. Na przykład możesz opakować LiveData
wokół bieżącego wyniku w aplikacji GuessTheWord. Podczas tego ćwiczenia z programowania poznasz kilka cech LiveData
:
LiveData
można obserwować, co oznacza, że obserwator otrzymuje powiadomienie, gdy dane przechowywane przez obiektLiveData
się zmienią.LiveData
zawiera dane; kodLiveData
to kod, którego można używać z dowolnymi danymiLiveData
jest zależny od cyklu życia, co oznacza, że aktualizuje tylko obserwatorów, którzy są w stanie aktywności, np.STARTED
lubRESUMED
.
Z tego zadania dowiesz się, jak pakować dowolny typ danych do obiektów LiveData
przez przekonwertowanie bieżącego wyniku i bieżących danych słów z GameViewModel
na LiveData
. W późniejszym zadaniu dodasz obserwatora do tych obiektów LiveData
i dowiesz się, jak obserwować LiveData
.
Krok 1. Zmień wynik i słowo, aby korzystać z LiveData
- W pakiecie
screens/game
otwórz plikGameViewModel
. - Zmień typ zmiennych
score
iword
naMutableLiveData
.MutableLiveData
toLiveData
, której wartość można zmienić.MutableLiveData
to klasa ogólna, musisz więc określić typ jej danych.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
- W elemencie
GameViewModel
w blokuinit
zainicjujscore
iword
. Aby zmienić wartość zmiennejLiveData
, użyj w niej metodysetValue()
. W Kotlin możesz wywołaćsetValue()
za pomocą właściwościvalue
.
init {
word.value = ""
score.value = 0
...
}
Krok 2. Zaktualizuj odniesienie do obiektu LiveData
Zmienne score
i word
są teraz typu LiveData
. W tym kroku zmienisz odwołania do tych zmiennych za pomocą właściwości value
.
- W
GameViewModel
w metodzieonSkip()
zmieńscore
nascore.value
. Zwróć uwagę na błąd dotyczący elementuscore
, który prawdopodobnie ma wartośćnull
. Następnie naprawisz ten błąd. - Aby naprawić ten błąd, na koncie
onSkip()
otwórz sekcjęnull
, sprawdzając testscore.value
. Następnie wywołaj funkcjęminus()
w elemenciescore
, która wykonuje odejmowanie równymnull
-bezpieczeństwo.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}
- Zaktualizuj metodę
onCorrect()
w ten sam sposób: dodaj znaczniknull
do zmiennejscore
i użyj funkcjiplus()
.
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}
- W metodzie
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 metodzie
GameFragment
w metodzieupdateWordText()
zmień odwołanie naviewModel
.word
naviewModel
.
word
.
value.
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}
- W metodzie
GameFragment
w metodzieupdateScoreText()
zmień odwołanie naviewModel
.score
naviewModel
.
score
.
value.
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}
- W metodzie
GameFragment
w metodziegameFinished()
zmień odwołanie naviewModel
.score
naviewModel
.
score
.
value
. Dodaj wymaganą kontrolę bezpieczeństwanull
.
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)
}
- Upewnij się, że kod nie zawiera błędów. Skompiluj i uruchom aplikację. Funkcjonalność aplikacji powinna pozostać taka sama jak wcześniej.
To zadanie jest ściśle powiązane z poprzednim zadaniem, w którym dane o wyniku i słowa zostały przekształcone w obiekty LiveData
. W tym zadaniu dołączysz obiekty Observer
do tych obiektów LiveData
.
- W obiekcie
GameFragment,
w metodzieonCreateView()
dołącz obiektObserver
do obiektuLiveData
dla bieżącego wyniku (viewModel.score
). Użyj metodyobserve()
i umieść kod po zainicjowaniu elementuviewModel
. Użyj wyrażenia lambda, aby uprościć kod. Wyrażenie lambda to funkcja anonimowa, która nie jest zadeklarowana, ale jest przekazywana natychmiast jako wyrażenie.
viewModel.score.observe(this, Observer { newScore ->
})
Rozwiąż problem z plikiem referencyjnym Observer
. Aby to zrobić, kliknij Observer
, naciśnij Alt+Enter
(Option+Enter
na Macu) i zaimportuj androidx.lifecycle.Observer
.
- Utworzony właśnie przez Ciebie obserwator odbiera zdarzenie, gdy zmienią się dane przechowywane przez zaobserwowany obiekt
LiveData
. W obserwatorze zaktualizuj wynikTextView
, dodając nowy wynik.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})
- Dołącz obiekt
Observer
do bieżącego obiektuLiveData
. Zrób to samo, tak jak dołączono obiektObserver
do bieżącego wyniku.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})
Gdy wartość score
lub word
ulegnie zmianie, score
lub word
na ekranie zostaną automatycznie zaktualizowane.
- W usłudze
GameFragment
usuń metodyupdateWordText()
iupdateScoreText()
oraz wszystkie odniesienia do nich. Nie potrzebujesz ich już, ponieważ widoki tekstu są aktualizowane przez metody obserwatorówLiveData
. - Uruchom aplikację. Aplikacja powinna działać dokładnie tak jak dotychczas, ale korzysta z
LiveData
iLiveData
obserwatorów.
Maskowanie to sposób na ograniczenie bezpośredniego dostępu do niektórych pól obiektu. Gdy zamkniesz obiekt, możesz udostępnić zestaw publicznych metod, które modyfikują prywatne pola wewnętrzne. Enklawa herbaciana pozwala kontrolować sposób, w jaki inne klasy traktują te pola wewnętrzne.
W obecnym kodzie wszystkie klasy zewnętrzne mogą modyfikować zmienne score
i word
za pomocą właściwości value
, np. viewModel.score.value
. Może to nie mieć znaczenia w przypadku aplikacji, którą tworzysz w ramach tego ćwiczenia z programowania, ale w aplikacji produkcyjnej chcesz mieć kontrolę nad danymi w obiektach ViewModel
.
Tylko ViewModel
może edytować dane w Twojej aplikacji. Kontrolery interfejsu użytkownika muszą jednak odczytywać te dane, więc pola te nie mogą być całkowicie prywatne. Aby hermetyzować dane aplikacji, użyj obiektów MutableLiveData
i LiveData
.
MutableLiveData
a LiveData
:
- Możesz zmienić dane w obiekcie
MutableLiveData
, jak wskazuje na to nazwa. W sekcjiViewModel
dane możesz edytować, więc używaszMutableLiveData
. - Dane w obiekcie
LiveData
można odczytać, ale nie można ich zmienić. PozaViewModel
dane powinny być czytelne, ale nie można ich edytować, dlatego powinny być widoczne jakoLiveData
.
Aby zastosować tę strategię, korzystasz z usługi backendu Kotlin. Właściwość wewnętrzna umożliwia zwracanie czegoś innego niż obiekt dokładny. W tym zadaniu implementujesz właściwości backendu dla obiektów score
i word
w aplikacji GuessTheWord.
Dodaj właściwość uzupełniającą, aby otrzymać ocenę i słowo
- W
GameViewModel
dodaj bieżący obiektscore
private
. - Aby zachować zgodność z konwencją nazewnictwa używaną we właściwościach kopii zapasowej, zmień
score
na_score
. Właściwość_score
jest teraz zmodyfikowaną wersją wyniku gry do użycia wewnętrznie. - Utwórz publiczną wersję typu
LiveData
o nazwiescore
.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
- Pojawia się błąd inicjowania. Ten błąd występuje, ponieważ element
GameFragment
score
jest odwołaniem doLiveData
, ascore
nie ma już dostępu do jego ustawienia. Aby dowiedzieć się więcej o elementach pobierających i ustawiających w Kotlinie, przeczytaj artykuł Getters i Setery.
Aby naprawić ten błąd, zastąp metodęget()
obiektuscore
wGameViewModel
i zwróć właściwość backendu_score
.
val score: LiveData<Int>
get() = _score
- W narzędziu
GameViewModel
zmień odniesienia elementuscore
na jego wewnętrzną zmienną_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ść backendu, 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ę uchronić LiveData
obiekty word
i score
.
Gdy użytkownik kliknie przycisk Zakończ grę, otworzy się ekran bieżącej aplikacji. Aplikacja powinna też otwierać ekran z wynikami, gdy gracz przewija kolejne słowa. Gdy gracze kończą ostatnią grę, chcesz, aby gra kończyła się automatycznie, żeby użytkownik nie musiał klikać przycisku.
Aby zaimplementować tę funkcję, musisz wywołać zdarzenie i przekazać je do fragmentu z elementu ViewModel
, gdy wszystkie słowa zostaną wyświetlone. Aby to zrobić, musisz użyć wzoru obserwatora LiveData
, aby utworzyć model zdarzenia zakończonego w grze.
Wzorzec obserwatora
Wzorzec serwera to wzorzec oprogramowania. Wskazuje on komunikację między obiektami: obserwowalny ("subject" obserwacja) i obserwatorów. Obserwowalny obiekt to powiadomienie, które informuje obserwatorów o zmianach stanu.
W przypadku LiveData
w tej aplikacji obserwowanym obiektem jest obiekt LiveData
, a obserwatorzy to metody w kontrolerach interfejsu, takie jak fragmenty. Zmiana stanu następuje za każdym razem, gdy dane pakowane do LiveData
są zmieniane. Klasy LiveData
mają kluczowe znaczenie w komunikacji między fragmentami ViewModel
.
Krok 1. Użyj LiveData, aby wykryć zdarzenie dotyczące gry
W tym zadaniu używasz wzoru obserwatora LiveData
do modelowania zdarzenia zakończonego w grze.
- W
GameViewModel
utwórz obiektBoolean
MutableLiveData
o nazwie_eventGameFinish
. Ten obiekt będzie zawierać zdarzenie ukończenia gry. - Po zainicjowaniu obiektu
_eventGameFinish
utwórz i zainicjuj usługę 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ńczone naeventGameFinish
natrue
.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}
- W metodzie
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
w elemencieonCreateView()
po zainicjowaniuviewModel
dołącz obserwatora doeventGameFinish
. Użyj 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 przejrzyj wszystkie słowa. Aplikacja automatycznie wyświetli się na ekranie z wynikiem wyszukiwania. Nie pozostaniesz we fragmencie gry, dopóki nie klikniesz Zakończ grę.
Jeśli lista słów jest pusta,eventGameFinish
jest ustawiana jako powiązana metoda obserwatora we fragmencie gry, a aplikacja przechodzi do fragmentu ekranu. - W podanym kodzie wystąpił problem z cyklem życia. Aby dowiedzieć się, na czym polega problem, w klasie
GameFragment
dodaj komentarz dotyczący kodu nawigacyjnego w metodziegameFinished()
. Pamiętaj o przekazywaniu wiadomościToast
w metodzie.
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 przejrzyj wszystkie słowa. Na dole ekranu pojawi się komunikat „"Gra właśnie się zakończyła”. To prawidłowy proces.
Teraz obróć urządzenie lub emulator. Tost znowu się wyświetla. Obróć urządzenie jeszcze kilka razy i zapewne zobaczysz komunikat za każdym razem. To błąd, ponieważ tost pojawia się tylko raz po zakończeniu gry. Tost powinien być wyświetlany przy każdym odtworzeniu fragmentu. Ten problem zostanie rozwiązany w następnym zadaniu.
Krok 2. Zresetuj wydarzenie zakończone w grze
Zazwyczaj LiveData
aktualizuje powiadomienia tylko dla zmian danych. Wyjątek stanowi sytuacja, gdy obserwatorzy otrzymują powiadomienia o zmianie stanu z nieaktywnego na aktywny.
Tost pojawiający się w aplikacji wielokrotnie uruchamia się w grze. Po odtworzeniu ekranu fragment gry jest ponownie odtwarzany, z nieaktywnego do aktywnego. Obserwator we fragmencie jest ponownie połączony z dotychczasowym elementem ViewModel
i otrzymuje bieżące dane. Ponownie zostanie wywołana metoda gameFinished()
, a na ekranie pojawi się tost.
W tym zadaniu rozwiązujesz problem i wyświetlasz tost tylko raz, resetując flagę eventGameFinish
w GameViewModel
.
- W narzędziu
GameViewModel
dodaj metodęonGameFinishComplete()
, by zresetować zdarzenie ukończenia gry_eventGameFinish
.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- W
GameFragment
na końcugameFinished()
wywołajonGameFinishComplete()
dla obiektuviewModel
. (Na razie pozostaw komentarzgameFinished()
w nawigacji).
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}
- Uruchom aplikację i zagraj w grę. Przeczytaj wszystkie słowa i zmień orientację ekranu urządzenia. Tost pojawia się tylko raz.
- W metodzie
GameFragment
w metodziegameFinished()
usuń komentarz z kodu nawigacyjnego.
Aby usunąć komentarz w Android Studio, zaznacz wiersze i naciśnijControl+/
(Command+/
na Macu).
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 pojawi się prośba w Android Studio, zaimportuj androidx.navigation.fragment.NavHostFragment.findNavController
.
- Uruchom aplikację i zagraj w grę. Upewnij się, że po automatycznym przejrzeniu wszystkich słów aplikacja automatycznie przejdzie do ekranu z ostatecznym wynikiem.
Brawo! Aplikacja używa wartości LiveData
, aby wywołać zdarzenie ukończenia gry i poinformować ją ze fragmentu GameViewModel
, że lista słów jest pusta. Następnie fragment kodu gry przejdzie do fragmentu z wynikiem.
W tym zadaniu zmienisz wynik na obiekt LiveData
w elemencie ScoreViewModel
i dołączysz do niego obserwatora. To zadanie jest podobne do wykonanego przez Ciebie dodania elementu LiveData
do folderu GameViewModel
.
Aby zapewnić kompletność, wprowadzasz te zmiany w ScoreViewModel
, aby wszystkie dane w aplikacji używały wartości LiveData
.
- W
ScoreViewModel
zmień typ zmiennejscore
naMutableLiveData
. Zmień nazwę zgodnie z konwencją na_score
i dodaj właściwość podkładową.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score
- W elemencie
ScoreViewModel
w blokuinit
zainicjuj_score
. W dowolnym momencie możesz usunąć dziennik w blokuinit
lub go pozostawić.
init {
_score.value = finalScore
}
- W
ScoreFragment
w elemencieonCreateView()
po zainicjowaniuviewModel
dołącz obserwatora obiektu wynikuLiveData
. W wyrażeniu lambda ustaw wartość wyniku na widok tekstu wyniku. Usuń kod, który bezpośrednio przypisuje widok tekstowy z wartością wyniku zViewModel
.
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 pojawi się prośba w Android Studio, zaimportuj androidx.lifecycle.Observer
.
- Uruchom aplikację i zagraj w grę. Aplikacja powinna działać jak dotychczas, ale do aktualizacji wyniku używa teraz
LiveData
i obserwatora.
W tym zadaniu dodajesz przycisk Odtwórz ponownie na ekranie wyniku i implementujesz detektor kliknięć za pomocą zdarzenia LiveData
. Przycisk uruchamia zdarzenie, które umożliwia przejście z ekranu wyników na ekran gry.
Kod początkowy aplikacji zawiera przycisk Odtwórz ponownie, ale jest on ukryty.
- W
res/layout/score_fragment.xml
dla przyciskuplay_again_button
zmień wartość atrybutuvisibility
navisible
.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>
- W
ScoreViewModel
dodaj obiektLiveData
o nazwieBoolean
o nazwie_eventPlayAgain
. Ten obiekt służy do zapisywania zdarzeńLiveData
w celu poruszania się między ekranem wyników a ekranem 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 dla elementueventPlayAgain
. Kod należy umieścić na końcu koduonCreateView()
, przed instrukcjąreturn
. W wyrażeniu lambda wróć do ekranu gry i zresetujeventPlayAgain
.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})
Gdy pojawi się prośba w Android Studio, zaimportuj androidx.navigation.fragment.findNavController
.
- W
ScoreFragment
w obrębie taguonCreateView()
dodaj detektor kliknięć do przycisku PlayAgain i wywołuj funkcjęviewModel
.onPlayAgain()
.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- Uruchom aplikację i zagraj w grę. Po zakończeniu gry na ekranie wyniku zobaczysz końcowy wynik i przycisk Zagraj ponownie. Kliknij przycisk PlayAgain, a aplikacja przejdzie na ekran gry, aby można było w nią zagrać.
Dobra robota! Zmieniłeś architekturę aplikacji tak, aby używać LiveData
obiektów w obiekcie ViewModel
. Dołączono obserwatorów do obiektów LiveData
. LiveData
powiadamia o obiektach obserwacji, gdy zmieni się wartość przechowywana przez LiveData
.
Projekt na Android Studio: GuessTheWord
Dane Live Data
LiveData
jest obserwowalną klasą danych, która jest zależny od cyklu życia i należy do jednego z komponentów architektury Androida.- Za pomocą
LiveData
możesz włączyć automatyczne aktualizowanie interfejsu. LiveData
można obserwować, co oznacza, że po zmianie danych przechowywanych przez obiektLiveData
można powiadomić o tym obserwatora, np. aktywności lub fragment.LiveData
zawiera dane; to kod, którego można używać z dowolnymi danymi.LiveData
jest zależny od cyklu życia, co oznacza, że aktualizuje tylko obserwatorów, którzy są w stanie aktywności, np.STARTED
lubRESUMED
.
Aby dodać funkcję LiveData
- Zmień typ zmiennych danych w systemie
ViewModel
naLiveData
lubMutableLiveData
.
MutableLiveData
to obiekt LiveData
, którego wartość można zmienić. MutableLiveData
to klasa ogólna, musisz więc określić typ jej danych.
- Aby zmienić wartość danych przechowywanych w nagłówku
LiveData
, użyj metodysetValue()
w zmiennejLiveData
.
Do hermetyzacji LiveData
- Element
LiveData
wewnątrzViewModel
powinien być edytowalny. PozaViewModel
właściwośćLiveData
powinna być czytelna. Możesz to zrobić za pomocą usługi zapasowej Kotlin. - Właściwość kotlin pozwala uzyskać coś innego niż obiekt dokładny.
- Aby zamknąć atrybut
LiveData
, użyjprivate
MutableLiveData
w obrębieViewModel
i zwróć właściwość tworzenia zasobuLiveData
poza elementViewModel
.
Obserwowane dane LiveLive
LiveData
jest zgodny ze wzorcem obserwatora. „Dostrzegalny"” to obiektLiveData
, a obserwatorzy to metody w kontrolerach interfejsu takie jak fragmenty. Po zmianie danych opakowanych wLiveData
metody obserwatora w kontrolerach interfejsu są powiadamiane.- Aby element
LiveData
był obserwowalny, dołącz obiekt obserwatora do odwołania doLiveData
w obserwatorach (takich jak aktywności i fragmenty), korzystając z metodyobserve()
. - Ten wzorzec obserwatora
LiveData
może służyć do komunikacji z kontrolera interfejsuViewModel
.
Kurs Udacity:
Dokumentacja dla programistów Androida:
- Omówienie modelu Model
- Omówienie danych na żywo
MutableLiveData
- Przewodnik po architekturze aplikacji
Inne:
- Zaplecze w Kotlinie
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
Jak zamknąć plik LiveData
zapisany w systemie ViewModel
, by obiekty zewnętrzne mogły odczytywać dane bez możliwości ich aktualizacji?
- W obiekcie
ViewModel
zmień typ danych naprivate
LiveData
. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typuMutableLiveData
. - W obiekcie
ViewModel
zmień typ danych naprivate
MutableLiveData
. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typuLiveData
. - W kontrolerze interfejsu zmień typ danych na
private
MutableLiveData
. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typuLiveData
. - W obiekcie
ViewModel
zmień typ danych naLiveData
. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typuLiveData
.
Pytanie 2
W którym z tych stanów LiveData
kontroler interfejsu (np. fragment) aktualizuje kontroler?
- Wznowiono
- W tle
- Wstrzymano
- Zatrzymano
Pytanie 3
Który element obserwowany (LiveData
) jest obserwowany?
- Metoda obserwacji
- Dane w obiekcie
LiveData
- Kontroler interfejsu
- Obiekt
ViewModel
Rozpocznij następną lekcję:
Linki do innych ćwiczeń z programowania w tym kursie znajdziesz na stronie docelowej z ćwiczeniami z podstaw Androida Kotlin.