Android Kotlin Fundamentals 05.2: LiveData i obserwatorzy LiveData

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ą interfejsu ViewModelProvider.Factory.

Czego się nauczysz

  • Co sprawia, że obiekty LiveData są przydatne.
  • Jak dodać LiveData do danych przechowywanych w narzędziu ViewModel.
  • 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&&quot, 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ą.

  1. (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.
  2. Uruchom aplikację i zagraj w grę.
  3. 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 obiekt LiveData się zmienią.
  • LiveData zawiera dane; kod LiveData 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 lub RESUMED.

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

  1. W pakiecie screens/game otwórz plik GameViewModel.
  2. Zmień typ zmiennych score i word na MutableLiveData.

    MutableLiveData to LiveData, 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>()
  1. W elemencie GameViewModel w bloku init zainicjuj score i word. Aby zmienić wartość zmiennej LiveData, użyj w niej metody setValue(). W Kotlin możesz wywołać setValue() za pomocą właściwości value.
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.

  1. W GameViewModel w metodzie onSkip() zmień score na score.value. Zwróć uwagę na błąd dotyczący elementu score, który prawdopodobnie ma wartość null. Następnie naprawisz ten błąd.
  2. Aby naprawić ten błąd, na koncie onSkip() otwórz sekcję null, sprawdzając test score.value. Następnie wywołaj funkcję minus() w elemencie score, która wykonuje odejmowanie równym null-bezpieczeństwo.
fun onSkip() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.minus(1)
   }
   nextWord()
}
  1. Zaktualizuj metodę onCorrect() w ten sam sposób: dodaj znacznik null do zmiennej score i użyj funkcji plus().
fun onCorrect() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.plus(1)
   }
   nextWord()
}
  1. W metodzie GameViewModel w metodzie nextWord() zmień odwołanie word na word.value.
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       word.value = wordList.removeAt(0)
   }
}
  1. W metodzie GameFragment w metodzie updateWordText() zmień odwołanie na viewModel.word na viewModel.word.value.
/** Methods for updating the UI **/
private fun updateWordText() {
   binding.wordText.text = viewModel.word.value
}
  1. W metodzie GameFragment w metodzie updateScoreText() zmień odwołanie na viewModel.score na viewModel.score.value.
private fun updateScoreText() {
   binding.scoreText.text = viewModel.score.value.toString()
}
  1. W metodzie GameFragment w metodzie gameFinished() zmień odwołanie na viewModel.score na viewModel.score.value. Dodaj wymaganą kontrolę bezpieczeństwa null.
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)
}
  1. 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.

  1. W obiekcie GameFragment, w metodzie onCreateView() dołącz obiekt Observer do obiektu LiveData dla bieżącego wyniku (viewModel.score). Użyj metody observe() i umieść kod po zainicjowaniu elementu viewModel. 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.

  1. Utworzony właśnie przez Ciebie obserwator odbiera zdarzenie, gdy zmienią się dane przechowywane przez zaobserwowany obiekt LiveData. W obserwatorze zaktualizuj wynik TextView, dodając nowy wynik.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Dołącz obiekt Observer do bieżącego obiektu LiveData. Zrób to samo, tak jak dołączono obiekt Observer 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.

  1. W usłudze GameFragment usuń metody updateWordText() i updateScoreText() oraz wszystkie odniesienia do nich. Nie potrzebujesz ich już, ponieważ widoki tekstu są aktualizowane przez metody obserwatorów LiveData.
  2. Uruchom aplikację. Aplikacja powinna działać dokładnie tak jak dotychczas, ale korzysta z LiveData i LiveData 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 sekcji ViewModel dane możesz edytować, więc używasz MutableLiveData.
  • Dane w obiekcie LiveData można odczytać, ale nie można ich zmienić. Poza ViewModel dane powinny być czytelne, ale nie można ich edytować, dlatego powinny być widoczne jako LiveData.

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

  1. W GameViewModel dodaj bieżący obiekt score private.
  2. 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.
  3. Utwórz publiczną wersję typu LiveData o nazwie score.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
  1. Pojawia się błąd inicjowania. Ten błąd występuje, ponieważ element GameFragment score jest odwołaniem do LiveData, a score 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() obiektu score w GameViewModel i zwróć właściwość backendu _score.
val score: LiveData<Int>
   get() = _score
  1. W narzędziu GameViewModel zmień odniesienia elementu score 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)
   }
   ...
}
  1. Zmień nazwę obiektu word na _word i dodaj do niego właściwość backendu, tak jak w przypadku obiektu score.
// 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.

  1. W GameViewModel utwórz obiekt Boolean MutableLiveData o nazwie _eventGameFinish. Ten obiekt będzie zawierać zdarzenie ukończenia gry.
  2. Po zainicjowaniu obiektu _eventGameFinish utwórz i zainicjuj usługę o nazwie eventGameFinish.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
   get() = _eventGameFinish
  1. W GameViewModel dodaj metodę onGameFinish(). W metodzie ustaw zdarzenie zakończone na eventGameFinish na true.
/** Method for the game completed event **/
fun onGameFinish() {
   _eventGameFinish.value = true
}
  1. W metodzie GameViewModel w metodzie nextWord() 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)
   }
}
  1. W GameFragment w elemencie onCreateView() po zainicjowaniu viewModel dołącz obserwatora do eventGameFinish. Użyj metody observe(). W funkcji lambda wywołaj metodę gameFinished().
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
   if (hasFinished) gameFinished()
})
  1. 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.
  2. 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 metodzie gameFinished(). Pamiętaj o przekazywaniu wiadomości Toast 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)
   }
  1. 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.

  1. 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
}
  1. W GameFragment na końcu gameFinished() wywołaj onGameFinishComplete() dla obiektu viewModel. (Na razie pozostaw komentarz gameFinished() w nawigacji).
private fun gameFinished() {
   ...
   viewModel.onGameFinishComplete()
}
  1. Uruchom aplikację i zagraj w grę. Przeczytaj wszystkie słowa i zmień orientację ekranu urządzenia. Tost pojawia się tylko raz.
  2. W metodzie GameFragment w metodzie gameFinished() usuń komentarz z kodu nawigacyjnego.

    Aby usunąć komentarz w Android Studio, zaznacz wiersze i naciśnij Control+/ (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.

  1. 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.

  1. W ScoreViewModel zmień typ zmiennej score na MutableLiveData. 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
  1. W elemencie ScoreViewModel w bloku init zainicjuj _score. W dowolnym momencie możesz usunąć dziennik w bloku init lub go pozostawić.
init {
   _score.value = finalScore
}
  1. W ScoreFragment w elemencie onCreateView() po zainicjowaniu viewModel dołącz obserwatora obiektu wyniku LiveData. W wyrażeniu lambda ustaw wartość wyniku na widok tekstu wyniku. Usuń kod, który bezpośrednio przypisuje widok tekstowy z wartością wyniku z ViewModel.

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.

  1. 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.

  1. W res/layout/score_fragment.xml dla przycisku play_again_button zmień wartość atrybutu visibility na visible.
<Button
   android:id="@+id/play_again_button"
...
   android:visibility="visible"
 />
  1. W ScoreViewModel dodaj obiekt LiveData o nazwie Boolean 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
  1. W ScoreViewModel zdefiniuj metody ustawiania i resetowania zdarzenia _eventPlayAgain.
fun onPlayAgain() {
   _eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
   _eventPlayAgain.value = false
}
  1. W ScoreFragment dodaj obserwatora dla elementu eventPlayAgain. Kod należy umieścić na końcu kodu onCreateView(), przed instrukcją return. W wyrażeniu lambda wróć do ekranu gry i zresetuj eventPlayAgain.
// 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.

  1. W ScoreFragment w obrębie tagu onCreateView() dodaj detektor kliknięć do przycisku PlayAgain i wywołuj funkcję viewModel.onPlayAgain().
binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. 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ć LiveDataobiektó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 obiekt LiveData 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 lub RESUMED.

Aby dodać funkcję LiveData

  • Zmień typ zmiennych danych w systemie ViewModel na LiveData lub MutableLiveData.

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 metody setValue() w zmiennej LiveData.

Do hermetyzacji LiveData

  • Element LiveData wewnątrz ViewModel powinien być edytowalny. Poza ViewModel 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żyj private MutableLiveData w obrębie ViewModel i zwróć właściwość tworzenia zasobu LiveData poza element ViewModel.

Obserwowane dane LiveLive

  • LiveData jest zgodny ze wzorcem obserwatora. „Dostrzegalny&quot” to obiekt LiveData, a obserwatorzy to metody w kontrolerach interfejsu takie jak fragmenty. Po zmianie danych opakowanych w LiveData metody obserwatora w kontrolerach interfejsu są powiadamiane.
  • Aby element LiveData był obserwowalny, dołącz obiekt obserwatora do odwołania do LiveData w obserwatorach (takich jak aktywności i fragmenty), korzystając z metody observe().
  • Ten wzorzec obserwatora LiveData może służyć do komunikacji z kontrolera interfejsu ViewModel.

Kurs Udacity:

Dokumentacja dla programistów Androida:

Inne:

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 na private LiveData. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typu MutableLiveData.
  • W obiekcie ViewModel zmień typ danych na private MutableLiveData. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typu LiveData.
  • W kontrolerze interfejsu zmień typ danych na private MutableLiveData. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typu LiveData.
  • W obiekcie ViewModel zmień typ danych na LiveData. Używaj usługi zapasowej, aby wyświetlać dane tylko do odczytu typu LiveData.

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ę: 5.3: wiązanie danych z obiektami ViewModel i LiveData

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