ה-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. - מוסיפים משקיפים שישימו לב אם המילה או הניקוד משתנים.
- עדכון תצוגות הטקסט שבהן מוצגים ערכים שהשתנו.
- משתמשים בתבנית
LiveData
observer כדי להוסיף אירוע של סיום משחק. - מטמיעים את הלחצן הפעלה חוזרת.
בשיעור 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
. בשלב הבא תתקנו את השגיאה הזו. - כדי לפתור את השגיאה, מוסיפים
null
check 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
וב-LiveData
observers.
הצפנה היא דרך להגביל את הגישה הישירה לחלק מהשדות של אובייקט. כשמבצעים אנקפסולציה של אובייקט, חושפים קבוצה של מתודות ציבוריות שמשנות את השדות הפנימיים הפרטיים. באמצעות הכמסה, אתם קובעים איך מחלקות אחרות ישנו את השדות הפנימיים האלה.
בקוד הנוכחי, כל מחלקה חיצונית יכולה לשנות את המשתנים 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
, יוצרים אובייקטBoolean
MutableLiveData
בשם_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
, משתמשים ב-private
MutableLiveData
בתוך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
, משנים את סוג הנתונים של הנתונים ל-private
LiveData
. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגMutableLiveData
. - באובייקט
ViewModel
, משנים את סוג הנתונים של הנתונים ל-private
MutableLiveData
. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגLiveData
. - בתוך בקר ממשק המשתמש, משנים את סוג הנתונים של הנתונים ל-
private
MutableLiveData
. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגLiveData
. - באובייקט
ViewModel
, משנים את סוג הנתונים של הנתונים ל-LiveData
. משתמשים בנכס תומך כדי לחשוף נתונים לקריאה בלבד מהסוגLiveData
.
שאלה 2
LiveData
מעדכן בקר ממשק משתמש (כמו fragment) אם בקר ממשק המשתמש נמצא באחד מהמצבים הבאים:
- המשכת הפעולה של
- ברקע
- בהשהיה
- נעצר
שאלה 3
בLiveData
תבנית Observer, מהו הפריט שניתן לצפייה (מה נצפה)?
- שיטת הצופה
- הנתונים באובייקט
LiveData
- הבקר של ממשק המשתמש
- האובייקט
ViewModel
עוברים לשיעור הבא:
קישורים ל-codelabs אחרים בקורס הזה מופיעים בדף הנחיתה של ה-codelabs בנושא יסודות Android Kotlin.