ה-codelab הזה הוא חלק מהקורס Android Kotlin Fundamentals. כדי להפיק את המרב מהקורס הזה, מומלץ לעבוד על ה-codelabs לפי הסדר. כל ה-codelab של הקורס מפורטים בדף הנחיתה של ה-codelab בנושא יסודות Kotlin ל-Android.
מבוא
ב-codelab הקודם השתמשתם ב-ViewModel באפליקציית GuessTheWord כדי לאפשר לנתוני האפליקציה לשרוד שינויים בהגדרות המכשיר. ב-codelab הזה תלמדו איך לשלב את LiveData עם הנתונים בכיתות ViewModel. LiveData, שהוא אחד מרכיבי הארכיטקטורה של Android, מאפשר ליצור אובייקטים של נתונים ששולחים הודעות לתצוגות כשיש שינויים במסד הנתונים הבסיסי.
כדי להשתמש במחלקה LiveData, מגדירים 'צופים' (לדוגמה, פעילויות או קטעים) שעוקבים אחרי שינויים בנתונים של האפליקציה. LiveData מודע למחזור החיים, ולכן הוא מעדכן רק את רכיבי האפליקציה שנמצאים במצב פעיל במחזור החיים.
מה שכדאי לדעת
- איך ליצור אפליקציות בסיסיות ל-Android ב-Kotlin.
- איך עוברים בין יעדים באפליקציה.
- מחזור החיים של פעילות ושל קטע.
- איך משתמשים באובייקטים מסוג
ViewModelבאפליקציה. - איך יוצרים אובייקטים מסוג
ViewModelבאמצעות הממשק שלViewModelProvider.Factory.
מה תלמדו
- מה הופך אובייקטים של
LiveDataלשימושיים. - איך מוסיפים
LiveDataלנתונים שמאוחסנים ב-ViewModel. - מתי ואיך להשתמש ב-
MutableLiveData. - איך מוסיפים שיטות של תצפית כדי לעקוב אחרי שינויים ב-
LiveData. - איך מבצעים אנקפסולציה של
LiveDataבאמצעות מאפיין גיבוי. - איך מתקשרים בין בקר ממשק משתמש לבין
ViewModelהתואם שלו.
הפעולות שתבצעו:
- משתמשים ב-
LiveDataלמילה ולניקוד באפליקציית GuessTheWord. - מוסיפים משקיפים שישימו לב אם המילה או הניקוד משתנים.
- עדכון תצוגות הטקסט שבהן מוצגים ערכים שהשתנו.
- משתמשים בתבנית
LiveDataobserver כדי להוסיף אירוע של סיום משחק. - מטמיעים את הלחצן הפעלה חוזרת.
בשיעור 5 של Codelab, מפתחים את האפליקציה GuessTheWord, החל מקוד התחלתי. GuessTheWord הוא משחק בסגנון ניחוש מילים לשני שחקנים, שבו השחקנים משתפים פעולה כדי להשיג את הניקוד הגבוה ביותר האפשרי.
השחקן הראשון מסתכל על המילים באפליקציה ומציג כל אחת מהן בתורו, תוך הקפדה לא להראות את המילה לשחקן השני. השחקן השני מנסה לנחש את המילה.
כדי לשחק במשחק, השחקן הראשון פותח את האפליקציה במכשיר ורואה מילה, למשל 'גיטרה', כמו שמוצג בצילום המסך שלמטה.
השחקן הראשון מדגים את המילה, ומשתדל לא לומר אותה.
- כשהשחקן השני מנחש את המילה נכון, השחקן הראשון לוחץ על הלחצן Got It (הבנתי), מה שמגדיל את הספירה באחד ומציג את המילה הבאה.
- אם השחקן השני לא מצליח לנחש את המילה, השחקן הראשון לוחץ על הכפתור דילוג, מה שמקטין את המספר באחד ומדלג למילה הבאה.
- כדי לסיים את המשחק, לוחצים על הלחצן סיום המשחק. (הפונקציונליות הזו לא מופיעה בקוד ההתחלתי של ה-codelab הראשון בסדרה).
ב-codelab הזה, תשפרו את האפליקציה GuessTheWord על ידי הוספת אירוע לסיום המשחק כשמשתמש עובר על כל המילים באפליקציה. תוסיפו גם לחצן Play Again (משחק חוזר) בקטע של הניקוד, כדי שהמשתמש יוכל לשחק שוב.
מסך – כותרת |
מסך המשחק |
מסך הציון |
במשימה הזו תאתרו את קוד ההתחלה של ה-codelab ותפעילו אותו. אתם יכולים להשתמש באפליקציית GuessTheWord שיצרתם ב-codelab הקודם כקוד התחלתי, או להוריד אפליקציית התחלה.
- (אופציונלי) אם אתם לא משתמשים בקוד מה-codelab הקודם, הורידו את קוד ההתחלה ל-codelab הזה. מחלצים את הקוד ופותחים את הפרויקט ב-Android Studio.
- מפעילים את האפליקציה ומשחקים במשחק.
- שימו לב שהמילה הבאה מוצגת בלחיצה על הלחצן דילוג והניקוד יורד באחת, ובלחיצה על הלחצן הבנתי המילה הבאה מוצגת והניקוד עולה באחת. המשחק מסתיים כשלוחצים על הכפתור סיום המשחק.
LiveData הוא מחזיק נתונים שניתן לצפייה בו, שמודע למחזור החיים של האפליקציה. לדוגמה, אפשר להוסיף תגי LiveData סביב הניקוד הנוכחי באפליקציית GuessTheWord. ב-codelab הזה תלמדו על כמה מאפיינים של LiveData:
-
LiveDataהוא אובייקט שניתן לצפייה, כלומר, צופה מקבל הודעה כשהנתונים שמוחזקים באובייקטLiveDataמשתנים. -
LiveDataמחזיק נתונים;LiveDataהוא wrapper שאפשר להשתמש בו עם כל נתון -
LiveDataמודע למחזור החיים, כלומר הוא מעדכן רק צופים שנמצאים במצב פעיל במחזור החיים, כמוSTARTEDאוRESUMED.
במשימה הזו תלמדו איך להוסיף כל סוג נתונים לאובייקטים של LiveData על ידי המרת הנתונים של הניקוד הנוכחי והמילה הנוכחית ב-GameViewModel ל-LiveData. במשימה מאוחרת יותר, תוסיפו משקיף לאובייקטים האלה של LiveData ותלמדו איך לצפות ב-LiveData.
שלב 1: שינוי הניקוד והמילה כדי להשתמש ב-LiveData
- בקטע
screens/game, פותחים את הקובץGameViewModel. - משנים את הסוג של המשתנים
scoreו-wordל-MutableLiveData.MutableLiveDataהואLiveDataשאפשר לשנות את הערך שלו. MutableLiveDataהוא מחלקה גנרית, ולכן צריך לציין את סוג הנתונים שהיא מכילה.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()- ב-
GameViewModel, בתוך הבלוקinit, מאתחלים אתscoreואתword. כדי לשנות את הערך של משתנהLiveData, משתמשים בשיטהsetValue()במשתנה. ב-Kotlin, אפשר לקרוא ל-setValue()באמצעות המאפייןvalue.
init {
word.value = ""
score.value = 0
...
}שלב 2: מעדכנים את ההפניה לאובייקט LiveData
המשתנים score ו-word הם עכשיו מסוג LiveData. בשלב הזה, משנים את ההפניות למשתנים האלה באמצעות המאפיין value.
- ב-
GameViewModel, בשיטהonSkip(), משנים אתscoreל-score.value. שימו לב לשגיאה לגביscoreשאולי הואnull. בשלב הבא תתקנו את השגיאה הזו. - כדי לפתור את השגיאה, מוסיפים
nullcheck toscore.valueב-onSkip(). אחר כך קוראים לפונקציהminus()ב-score, שמבצעת את החיסור עםnull-safety.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}- מעדכנים את השיטה
onCorrect()באותו אופן: מוסיפים בדיקה שלnullלמשתנהscoreומשתמשים בפונקציהplus().
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}- ב-
GameViewModel, בתוך ה-methodnextWord(), משנים את ההפניהwordל-word.value.
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}- ב-
GameFragment, בתוך השיטהupdateWordText(), משנים את ההפניה אלviewModel.wordל-viewModel.word.value.
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}- ב-
GameFragment, בתוך השיטהupdateScoreText(), משנים את ההפניה אלviewModel.scoreל-viewModel.score.value.
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}- ב-
GameFragment, בתוך ה-methodgameFinished(), משנים את ההפניה אלviewModel.scoreל-viewModel.score.value. מוסיפים את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)
}- מוודאים שאין שגיאות בקוד. קומפילציה והפעלה של האפליקציה. הפונקציונליות של האפליקציה צריכה להיות זהה למה שהייתה קודם.
המשימה הזו קשורה קשר הדוק למשימה הקודמת, שבה המרתם את הציון ואת נתוני המילים לאובייקטים מסוג LiveData. במשימה הזו, תצרפו אובייקטים של Observer לאובייקטים של LiveData.
- ב-
GameFragment,בתוך השיטהonCreateView(), מצרפים אובייקטObserverלאובייקטLiveDataשל הניקוד הנוכחי,viewModel.score. משתמשים בשיטהobserve()ומציבים את הקוד אחרי האתחול שלviewModel. שימוש בביטוי למבדה כדי לפשט את הקוד. (ביטוי למדא הוא פונקציה אנונימית שלא מוצהרת, אבל מועברת באופן מיידי כביטוי).
viewModel.score.observe(this, Observer { newScore ->
})צריך לפתור את הבעיה שקשורה לקובץ העזר Observer. כדי לעשות זאת, לוחצים על Observer, מקישים על Alt+Enter (Option+Enter ב-Mac) ומייבאים את androidx.lifecycle.Observer.
- האובייקט observer שיצרתם מקבל אירוע כשהנתונים שמוחזקים באובייקט
LiveDataשנצפה משתנים. בתוך האובייקט observer, מעדכנים את הציוןTextViewעם הציון החדש.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})- מצרפים אובייקט
ObserverלאובייקטLiveDataהנוכחי. מבצעים את הפעולה באותו אופן שבו צירפתם אובייקטObserverלציון הנוכחי.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})כשערך score או word משתנה, הערך של score או word שמוצג במסך מתעדכן אוטומטית.
- ב-
GameFragment, מוחקים את השיטותupdateWordText()ו-updateScoreText()ואת כל ההפניות אליהן. אתם לא צריכים אותם יותר, כי תצוגות הטקסט מתעדכנות על ידיLiveDataשיטות האובזרבר. - מריצים את האפליקציה. אפליקציית המשחק אמורה לפעול בדיוק כמו קודם, אבל עכשיו היא משתמשת ב-
LiveDataוב-LiveDataobservers.
הצפנה היא דרך להגביל את הגישה הישירה לחלק מהשדות של אובייקט. כשמבצעים אנקפסולציה של אובייקט, חושפים קבוצה של מתודות ציבוריות שמשנות את השדות הפנימיים הפרטיים. באמצעות הכמסה, אתם קובעים איך מחלקות אחרות ישנו את השדות הפנימיים האלה.
בקוד הנוכחי, כל מחלקה חיצונית יכולה לשנות את המשתנים score ו-word באמצעות המאפיין value, לדוגמה באמצעות viewModel.score.value. יכול להיות שזה לא משנה באפליקציה שאתם מפתחים ב-codelab הזה, אבל באפליקציה בסביבת הייצור, אתם רוצים שליטה בנתונים באובייקטים ViewModel.
רק ViewModel צריך לערוך את הנתונים באפליקציה. אבל רכיבי בקרה של ממשק המשתמש צריכים לקרוא את הנתונים, ולכן שדות הנתונים לא יכולים להיות פרטיים לחלוטין. כדי להצפין את הנתונים של האפליקציה, משתמשים באובייקטים MutableLiveData ו-LiveData.
MutableLiveData נגד LiveData:
- כמו שאפשר להבין מהשם, אפשר לשנות את הנתונים באובייקט
MutableLiveData. בתוךViewModel, הנתונים צריכים להיות ניתנים לעריכה, ולכן נעשה שימוש ב-MutableLiveData. - אפשר לקרוא את הנתונים באובייקט
LiveData, אבל אי אפשר לשנות אותם. מחוץ ל-ViewModel, הנתונים צריכים להיות ניתנים לקריאה אבל לא לעריכה, ולכן הם צריכים להיות חשופים כ-LiveData.
כדי ליישם את האסטרטגיה הזו, משתמשים במאפיין גיבוי של Kotlin. נכס גיבוי מאפשר להחזיר מ-getter משהו שאינו האובייקט המדויק. במשימה הזו מטמיעים נכס תומך לאובייקטים score ו-word באפליקציה GuessTheWord.
הוספת מאפיין גיבוי לציון ולמילה
- ב-
GameViewModel, מגדירים את האובייקט הנוכחיscoreכ-private. - כדי לפעול לפי המוסכמה למתן שמות שמשמשת במאפייני הגיבוי, משנים את
scoreל-_score. המאפיין_scoreהוא עכשיו הגרסה הניתנת לשינוי של ניקוד המשחק, והוא מיועד לשימוש פנימי. - יוצרים גרסה ציבורית של הסוג
LiveData, שנקראתscore.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>- מוצגת שגיאת אתחול. השגיאה הזו מתרחשת כי בתוך
GameFragment,scoreהוא הפניה ל-LiveData, ול-scoreכבר אין גישה לשיטת ה-setter שלו. מידע נוסף על getters ו-setters ב-Kotlin זמין במאמר Getters and Setters.
כדי לפתור את השגיאה, צריך לבטל את השיטהget()לאובייקטscoreב-GameViewModelולהחזיר את מאפיין הגיבוי,_score.
val score: LiveData<Int>
get() = _score- ב-
GameViewModel, משנים את ההפניות שלscoreלגרסה הפנימית שניתנת לשינוי,_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)
}
...
}- משנים את השם של האובייקט
wordל-_wordומוסיפים לו מאפיין גיבוי, כמו שעשיתם לאובייקט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)
}
}כל הכבוד, ביצעת את האנקפסולציה של האובייקטים LiveData ו-word.score
באפליקציה הנוכחית, המשתמש מועבר למסך התוצאות כשהוא מקיש על הלחצן סיום המשחק. אתם רוצים גם שהאפליקציה תעבור למסך התוצאות אחרי שהשחקנים יסיימו את כל המילים. אחרי שהשחקנים מסיימים עם המילה האחרונה, אתם רוצים שהמשחק יסתיים אוטומטית כדי שהמשתמש לא יצטרך להקיש על הלחצן.
כדי להטמיע את הפונקציונליות הזו, צריך להגדיר אירוע שמופעל ומועבר אל ה-fragment מ-ViewModel אחרי שכל המילים מוצגות. כדי לעשות את זה, משתמשים בתבנית LiveData observer כדי ליצור מודל של אירוע סיום משחק.
דפוס התצפית
תבנית Observer היא תבנית עיצוב תוכנה. הוא מגדיר תקשורת בין אובייקטים: אובייקט שניתן לצפייה (ה'נושא' של הצפייה) ואובייקטים צופים. משתנה ניתן לצפייה הוא אובייקט שמודיע לצופים על השינויים במצב שלו.

במקרה של LiveData באפליקציה הזו, האובייקט שאפשר לצפות בו (הנושא) הוא אובייקט LiveData, והצופים הם השיטות בבקרי ממשק המשתמש, כמו fragments. שינוי במצב מתרחש בכל פעם שהנתונים שמוקפים בתגי LiveData משתנים. המחלקות LiveData חיוניות לתקשורת מ-ViewModel אל ה-Fragment.
שלב 1: שימוש ב-LiveData כדי לזהות אירוע של סיום משחק
במשימה הזו משתמשים בתבנית LiveData observer כדי ליצור מודל של אירוע סיום משחק.
- ב-
GameViewModel, יוצרים אובייקטBooleanMutableLiveDataבשם_eventGameFinish. האובייקט הזה יכיל את אירוע סיום המשחק. - אחרי שמאתחלים את האובייקט
_eventGameFinish, יוצרים ומאתחלים נכס תומך בשםeventGameFinish.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish- ב-
GameViewModel, מוסיפים אמצעי תשלוםonGameFinish(). בשיטה, מגדירים את האירוע של סיום המשחק,eventGameFinish, לערךtrue.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}- ב-
GameViewModel, בתוך השיטהnextWord(), מסיימים את המשחק אם רשימת המילים ריקה.
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}- ב-
GameFragment, בתוךonCreateView(), אחרי הפעלתviewModel, מצרפים משקיף ל-eventGameFinish. משתמשים בשיטהobserve(). בתוך פונקציית ה-lambda, קוראים לשיטהgameFinished().
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
if (hasFinished) gameFinished()
})- מריצים את האפליקציה, משחקים במשחק ועוברים על כל המילים. האפליקציה עוברת למסך התוצאות באופן אוטומטי, במקום להישאר בקטע של המשחק עד שמקישים על סיום המשחק.
אחרי שרשימת המילים ריקה, הערך שלeventGameFinishמוגדר, שיטת הצפייה המשויכת בקטע של המשחק נקראת, והאפליקציה עוברת לקטע של המסך. - הקוד שהוספת גרם לבעיה במחזור החיים. כדי להבין את הבעיה, בכיתה
GameFragment, מוסיפים הערה לקוד הניווט בשיטהgameFinished(). חשוב להשאיר את ההודעהToastבשיטה.
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)
}
- מריצים את האפליקציה, משחקים במשחק ועוברים על כל המילים. הודעה קצרה שמופיעה בתחתית מסך המשחק ובה כתוב 'המשחק הסתיים' – זהו ההתנהגות הצפויה.
עכשיו מסובבים את המכשיר או את האמולטור. ההודעה הקופצת תוצג שוב. כדאי לסובב את המכשיר עוד כמה פעמים, וכנראה שההודעה הקופצת תופיע בכל פעם. זה באג, כי ההודעה אמורה להופיע רק פעם אחת, כשהמשחק מסתיים. ההודעה הקופצת לא צריכה להופיע בכל פעם שהקטע נוצר מחדש. תפתרו את הבעיה הזו במשימה הבאה.
|
|
שלב 2: מאפסים את האירוע game-finished
בדרך כלל, LiveData מעביר עדכונים לצופים רק כשיש שינויים בנתונים. יוצא מן הכלל הוא שגם משתמשים עם הרשאת צפייה מקבלים עדכונים כשהם משנים את הסטטוס שלהם מ'לא פעיל' ל'פעיל'.
זו הסיבה לכך שההודעה הקופצת על סיום המשחק מופעלת שוב ושוב באפליקציה. כשיוצרים מחדש את קטע המשחק אחרי סיבוב המסך, הוא עובר ממצב לא פעיל למצב פעיל. ה-observer בקטע מתחבר מחדש ל-ViewModel הקיים ומקבל את הנתונים הנוכחיים. השיטה gameFinished() מופעלת מחדש, וההודעה הקופצת מוצגת.
במשימה הזו אתם פותרים את הבעיה ומציגים את ההודעה הקופצת רק פעם אחת, על ידי איפוס הדגל eventGameFinish ב-GameViewModel.
- ב-
GameViewModel, מוסיפים methodonGameFinishComplete()כדי לאפס את אירוע סיום המשחק,_eventGameFinish.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}- ב
GameFragment, בסוףgameFinished(), תתקשר אלonGameFinishComplete()באובייקטviewModel. (בינתיים, משאירים את קוד הניווט ב-gameFinished()כהערה).
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}- מפעילים את האפליקציה ומשחקים במשחק. קוראים את כל המילים, ואז משנים את כיוון המסך של המכשיר. ההודעה הקופצת מוצגת רק פעם אחת.
- ב-
GameFragment, בתוך השיטהgameFinished(), מבטלים את ההערה של קוד הניווט.
כדי לבטל את ההערה ב-Android Studio, בוחרים את השורות שהוגדרו כהערה ומקישים עלControl+/(Command+/ב-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()
}אם מוצגת הנחיה ב-Android Studio, מייבאים את androidx.navigation.fragment.NavHostFragment.findNavController.
- מפעילים את האפליקציה ומשחקים במשחק. מוודאים שהאפליקציה עוברת אוטומטית למסך הניקוד הסופי אחרי שמעבירים את כל המילים.
|
|
מעולה! האפליקציה משתמשת ב-LiveData כדי להפעיל אירוע של סיום המשחק, כדי להעביר מה-GameViewModel אל קטע המשחק שהרשימה של המילים ריקה. לאחר מכן, קטע המשחק עובר לקטע התוצאה.
במשימה הזו, משנים את הציון לאובייקט LiveData ב-ScoreViewModel ומצרפים אליו צופה. המשימה הזו דומה למה שעשית כששילבת את LiveData בGameViewModel.
אתם מבצעים את השינויים האלה ב-ScoreViewModel כדי שהנתונים יהיו מלאים, כך שכל הנתונים באפליקציה ישתמשו ב-LiveData.
- ב-
ScoreViewModel, משנים את סוג המשתנהscoreל-MutableLiveData. משנים את השם שלו בהתאם למוסכמה ל-_scoreומוסיפים נכס תומך.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _score- ב-
ScoreViewModel, בתוך הבלוקinit, מאתחלים את_score. אתם יכולים להסיר את היומן או להשאיר אותו בחסימהinitלפי הצורך.
init {
_score.value = finalScore
}- ב-
ScoreFragment, בתוךonCreateView(), אחרי הפעלתviewModel, מצרפים אובייקט של צופה לציוןLiveData. בתוך ביטוי ה-lambda, מגדירים את ערך הניקוד לתצוגת הטקסט של הניקוד. מסירים את הקוד שמשייך ישירות את תצוגת הטקסט לערך הניקוד מ-ViewModel.
הקוד שצריך להוסיף:
// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})קוד להסרה:
binding.scoreText.text = viewModel.score.toString()כשמופיעה בקשה מ-Android Studio, מייבאים את androidx.lifecycle.Observer.
- מפעילים את האפליקציה ומשחקים במשחק. האפליקציה אמורה לפעול כמו קודם, אבל עכשיו היא משתמשת ב-
LiveDataוב-observer כדי לעדכן את הניקוד.
במשימה הזו מוסיפים לחצן Play Again למסך התוצאה ומטמיעים את ה-click listener שלו באמצעות אירוע LiveData. הלחצן מפעיל אירוע שגורם למעבר ממסך הניקוד למסך המשחק.
קוד ההתחלה של האפליקציה כולל את הלחצן Play Again, אבל הלחצן מוסתר.
- ב-
res/layout/score_fragment.xml, בלחצןplay_again_button, משנים את הערך של מאפייןvisibilityל-visible.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>- ב-
ScoreViewModel, מוסיפים אובייקטLiveDataכדי להחזיקBooleanבשם_eventPlayAgain. האובייקט הזה משמש לשמירת האירועLiveDataכדי לנווט ממסך הניקוד למסך המשחק.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgain- ב-
ScoreViewModel, מגדירים שיטות להגדרה ולאיפוס של האירוע,_eventPlayAgain.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}- ב-
ScoreFragment, מוסיפים משקיף ל-eventPlayAgain. ממקמים את הקוד בסוףonCreateView(), לפני ההצהרהreturn. בתוך ביטוי ה-lambda, חוזרים למסך המשחק ומאפסים אתeventPlayAgain.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
if (playAgain) {
findNavController().navigate(ScoreFragmentDirections.actionRestart())
viewModel.onPlayAgainComplete()
}
})מייבאים את androidx.navigation.fragment.findNavController כשמוצגת הנחיה ב-Android Studio.
- ב-
ScoreFragment, בתוךonCreateView(), מוסיפים מאזין לקליקים ללחצן PlayAgain וקוראים ל-viewModel.onPlayAgain().
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }- מפעילים את האפליקציה ומשחקים במשחק. בסיום המשחק, במסך התוצאות מוצגת התוצאה הסופית והלחצן Play Again (משחק חוזר). מקישים על הלחצן PlayAgain (משחק חוזר), והאפליקציה עוברת למסך המשחק כדי שתוכלו לשחק שוב.

כל הכבוד! שינית את הארכיטקטורה של האפליקציה כדי להשתמש באובייקטים של LiveData ב-ViewModel, וצירפת משתנים מסוג Observer לאובייקטים של LiveData. LiveData מודיע לאובייקטים של הצופה כשהערך שמוחזק על ידי LiveData משתנה.
פרויקט Android Studio: GuessTheWord
LiveData
-
LiveDataהוא מחזיק נתונים שניתן לצפייה בו, מודע למחזור החיים ואחד מרכיבי הארכיטקטורה של Android. - אתם יכולים להשתמש ב-
LiveDataכדי לאפשר לממשק המשתמש להתעדכן באופן אוטומטי כשהנתונים מתעדכנים. - אפשר לצפות ב-
LiveData, כלומר אפשר להודיע לצופה כמו פעילות או קטע כשהנתונים שמוחזקים על ידי אובייקטLiveDataמשתנים. -
LiveDataמחזיק נתונים; הוא עוטף נתונים שאפשר להשתמש בהם עם כל נתון. -
LiveDataמודע למחזור החיים, כלומר הוא מעדכן רק צופים שנמצאים במצב פעיל במחזור החיים, כמוSTARTEDאוRESUMED.
כדי להוסיף LiveData
- משנים את הסוג של משתני הנתונים ב-
ViewModelל-LiveDataאו ל-MutableLiveData.
MutableLiveData הוא אובייקט LiveData שאפשר לשנות את הערך שלו. MutableLiveData הוא מחלקה גנרית, ולכן צריך לציין את סוג הנתונים שהיא מכילה.
- כדי לשנות את ערך הנתונים שמוחזק על ידי
LiveData, משתמשים בשיטהsetValue()במשתנהLiveData.
כדי להשתמש ב-LiveData
- אפשר לערוך את
LiveDataבתוךViewModel. מחוץ ל-ViewModel, צריך להיות אפשר לקרוא אתLiveData. אפשר להטמיע את זה באמצעות נכס גיבוי של Kotlin. - מאפיין גיבוי ב-Kotlin מאפשר להחזיר מ-getter משהו שאינו האובייקט המדויק.
- כדי להוסיף את
LiveData, משתמשים ב-privateMutableLiveDataבתוךViewModelומחזירים מאפיין גיבוי שלLiveDataמחוץ ל-ViewModel.
LiveData שניתן לצפייה
-
LiveDataפועל לפי תבנית observer. האובייקטLiveDataהוא ה-observable, וה-observers הם השיטות בבקרי ממשק המשתמש, כמו fragments. בכל פעם שהנתונים שמוקפים בתגיLiveDataמשתנים, שיטות האובייקט המתבונן בבקרי ממשק המשתמש מקבלות הודעה. - כדי להפוך את
LiveDataלניתן לצפייה, צריך לצרף אובייקט צופה להפניהLiveDataבצופים (כמו פעילויות וקטעים) באמצעות השיטהobserve(). - אפשר להשתמש בדפוס התצפית
LiveDataהזה כדי לתקשר מה-ViewModelלבקרי ממשק המשתמש.
קורס ב-Udacity:
מסמכי תיעוד למפתחי Android:
אחר:
- Backing property ב-Kotlin
בקטע הזה מפורטות אפשרויות למשימות ביתיות לתלמידים שעובדים על ה-Codelab הזה כחלק מקורס בהנחיית מדריך. המורה צריך:
- אם צריך, מקצים שיעורי בית.
- להסביר לתלמידים איך להגיש מטלות.
- בודקים את שיעורי הבית.
אנשי ההוראה יכולים להשתמש בהצעות האלה כמה שרוצים, ומומלץ להם להקצות כל שיעורי בית אחרים שהם חושבים שמתאימים.
אם אתם עובדים על ה-codelab הזה לבד, אתם יכולים להשתמש במשימות האלה כדי לבדוק את הידע שלכם.
עונים על השאלות הבאות
שאלה 1
איך אפשר להצפין את LiveData שמאוחסן ב-ViewModel כך שאובייקטים חיצוניים יוכלו לקרוא את הנתונים בלי אפשרות לעדכן אותם?
- באובייקט
ViewModel, משנים את סוג הנתונים של הנתונים ל-privateLiveData. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגMutableLiveData. - באובייקט
ViewModel, משנים את סוג הנתונים של הנתונים ל-privateMutableLiveData. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגLiveData. - בתוך בקר ממשק המשתמש, משנים את סוג הנתונים של הנתונים ל-
privateMutableLiveData. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגLiveData. - באובייקט
ViewModel, משנים את סוג הנתונים של הנתונים ל-LiveData. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגLiveData.
שאלה 2
LiveData מעדכן בקר ממשק משתמש (כמו fragment) אם בקר ממשק המשתמש נמצא באחד מהמצבים הבאים:
- המשכת הפעולה של
- ברקע
- בהשהיה
- נעצר
שאלה 3
בLiveData תבנית Observer, מהו הפריט שניתן לצפייה (מה נצפה)?
- שיטת הצופה
- הנתונים באובייקט
LiveData - הבקר של ממשק המשתמש
- האובייקט
ViewModel
עוברים לשיעור הבא:
קישורים ל-codelabs אחרים בקורס הזה מופיעים בדף הנחיתה של ה-codelabs בנושא יסודות Android Kotlin.




