هذا الدرس التطبيقي حول الترميز هو جزء من دورة "أساسيات Android Kotlin". يمكنك تحقيق أقصى استفادة من هذه الدورة التدريبية إذا اتبعت ترتيب الخطوات في دروس البرمجة. يتم إدراج جميع الدروس التطبيقية حول الترميز الخاصة بالدورة التدريبية في الصفحة المقصودة للدروس التطبيقية حول الترميز في دورة Android Kotlin Fundamentals.
مقدمة
في الدرس العملي السابق، استخدمت ViewModel في تطبيق GuessTheWord للسماح ببقاء بيانات التطبيق محفوظة عند إجراء تغييرات في إعدادات الجهاز. في هذا الدرس العملي، ستتعرّف على كيفية دمج LiveData مع البيانات في فئات ViewModel. تتيح لك LiveData، وهي إحدى مكوّنات بنية Android، إنشاء عناصر بيانات تُعلم طرق العرض عند تغيُّر قاعدة البيانات الأساسية.
لاستخدام الفئة LiveData، عليك إعداد "مراقبين" (مثل الأنشطة أو الأجزاء) يراقبون التغييرات في بيانات التطبيق. LiveData مدرِك لدورة الحياة، لذا لا يحدّث سوى مراقبي مكونات التطبيق الذين يكونون في حالة نشطة من دورة الحياة.
ما يجب معرفته
- كيفية إنشاء تطبيقات Android أساسية في Kotlin
- كيفية التنقّل بين وجهات تطبيقك
- مراحل نشاط التطبيق وأجزائه
- كيفية استخدام عناصر
ViewModelفي تطبيقك - كيفية إنشاء عناصر
ViewModelباستخدام واجهةViewModelProvider.Factory
أهداف الدورة التعليمية
- ما الذي يجعل عناصر
LiveDataمفيدة؟ - كيفية إضافة
LiveDataإلى البيانات المخزّنة فيViewModel - كيفية استخدام
MutableLiveDataوالحالات التي يُستخدَم فيها - كيفية إضافة طرق مراقبة لتتبُّع التغييرات في
LiveData. - كيفية تغليف
LiveDataباستخدام سمة احتياطية - كيفية التواصل بين وحدة التحكّم في واجهة المستخدم و
ViewModelالمقابل لها
الإجراءات التي ستنفذّها
- استخدِم
LiveDataللكلمة والنتيجة في تطبيق GuessTheWord. - أضِف مراقبين يلاحظون متى تتغير الكلمة أو النتيجة.
- تعديل طرق عرض النصوص التي تعرض القيم المتغيرة
- استخدِم نمط
LiveDataالمراقب لإضافة حدث انتهاء اللعبة. - تنفيذ الزر تشغيل مرة أخرى
في دروس الترميز التطبيقية في الدرس 5، ستطوّر تطبيق GuessTheWord، بدءًا من الرموز البرمجية للمبتدئين. GuessTheWord هي لعبة تمثيل لشخصيات من لاعبَين، حيث يتعاون اللاعبان لتحقيق أعلى نتيجة ممكنة.
ينظر اللاعب الأول إلى الكلمات في التطبيق ويمثّل كل كلمة بالتناوب، مع الحرص على عدم إظهار الكلمة للاعب الثاني. يحاول اللاعب الثاني تخمين الكلمة.
لبدء اللعبة، يفتح اللاعب الأول التطبيق على الجهاز ويظهر له كلمة، مثل "غيتار"، كما هو موضّح في لقطة الشاشة أدناه.
يؤدي اللاعب الأول الكلمة، مع الحرص على عدم قول الكلمة نفسها.
- عندما يخمن اللاعب الثاني الكلمة بشكل صحيح، يضغط اللاعب الأول على الزر فهمت، ما يؤدي إلى زيادة العدد بمقدار واحد وعرض الكلمة التالية.
- إذا لم يتمكّن اللاعب الثاني من تخمين الكلمة، ينقر اللاعب الأول على زر تخطّي، ما يؤدي إلى خفض العدد بمقدار واحد والانتقال إلى الكلمة التالية.
- لإنهاء اللعبة، اضغط على الزر إنهاء اللعبة. (لا تتوفّر هذه الوظيفة في الرمز الأولي لأول درس برمجي في السلسلة).
في هذا الدرس العملي، ستعمل على تحسين تطبيق GuessTheWord من خلال إضافة حدث لإنهاء اللعبة عندما يتنقّل المستخدم بين جميع الكلمات في التطبيق. ستضيف أيضًا زر اللعب مرة أخرى في جزء النتيجة، حتى يتمكّن المستخدم من لعب اللعبة مرة أخرى.
شاشة العنوان |
شاشة اللعبة |
شاشة النتيجة |
في هذه المهمة، ستحدّد موقع الرمز الأولي لهذا الدرس التطبيقي حول الترميز وتشغّله. يمكنك استخدام تطبيق GuessTheWord الذي أنشأته في الدرس العملي السابق كرمز أولي، أو يمكنك تنزيل تطبيق أولي.
- (اختياري) إذا كنت لا تستخدم الرمز من الدرس التطبيقي السابق حول الترميز، يمكنك تنزيل رمز البداية لهذا الدرس التطبيقي حول الترميز. فك ضغط الرمز البرمجي، وافتح المشروع في "استوديو Android".
- شغِّل التطبيق واللعبة.
- يُرجى العِلم أنّ الزر تخطّي يعرض الكلمة التالية ويخفض النتيجة بمقدار واحد، بينما يعرض الزر فهمت الكلمة التالية ويزيد النتيجة بمقدار واحد. يؤدي النقر على الزر إنهاء اللعبة إلى إنهاء اللعبة.
LiveData هي فئة حاوية بيانات قابلة للمراقبة ومدرِكة لدورة الحياة. على سبيل المثال، يمكنك تضمين LiveData حول النتيجة الحالية في تطبيق GuessTheWord. في هذا الدرس العملي، ستتعرّف على العديد من خصائص LiveData:
- يمكن مراقبة
LiveData، ما يعني أنّه يتم إرسال إشعار إلى المراقب عند تغيير البيانات التي يحتوي عليها عنصرLiveData. LiveDataتحتفظ بالبيانات، وLiveDataهي أداة تغليف يمكن استخدامها مع أي بيانات-
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إلىscore.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، داخل طريقةnextWord()، غيِّر مرجع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، داخل طريقةgameFinished()، غيِّر المرجع إلى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. استخدِم تعبير lambda لتبسيط الرمز. (تعبير lambda هو دالة مجهولة الاسم لم يتم الإعلان عنها، ولكن يتم تمريرها على الفور كتعبير).
viewModel.score.observe(this, Observer { newScore ->
})حلّ المرجع إلى Observer لإجراء ذلك، انقر على Observer، ثم اضغط على Alt+Enter (Option+Enter على جهاز Mac)، واستورِد androidx.lifecycle.Observer.
- يتلقّى المراقب الذي أنشأته للتو حدثًا عندما تتغيّر البيانات التي يحتوي عليها العنصر
LiveDataالذي تتم مراقبته. داخل المراقب، عدِّل النتيجة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.
التغليف هو طريقة لحظر الوصول المباشر إلى بعض حقول الكائن. عند تغليف عنصر، يمكنك عرض مجموعة من الطرق العامة التي تعدّل الحقول الداخلية الخاصة. باستخدام التغليف، يمكنك التحكّم في كيفية معالجة الفئات الأخرى لهذه الحقول الداخلية.
في الرمز الحالي، يمكن لأي فئة خارجية تعديل المتغيّرَين score وword باستخدام السمة value، مثلاً باستخدام viewModel.score.value. قد لا يكون ذلك مهمًا في التطبيق الذي تعمل على تطويره في هذا الدرس البرمجي، ولكن في تطبيق متاح للجميع، من المهم أن يكون لديك تحكّم في البيانات الموجودة في عناصر ViewModel.
يجب أن يكون ViewModel هو الجهة الوحيدة التي تعدّل البيانات في تطبيقك، ولكن يجب أن تتمكّن عناصر التحكّم في واجهة المستخدم من قراءة البيانات، لذا لا يمكن أن تكون حقول البيانات خاصّة تمامًا. لتغليف بيانات تطبيقك، يمكنك استخدام كلّ من العنصرَين MutableLiveData وLiveData.
MutableLiveData مقارنةً بـ LiveData:
- يمكن تغيير البيانات في كائن
MutableLiveData، كما يشير الاسم. داخلViewModel، يجب أن تكون البيانات قابلة للتعديل، لذا يتم استخدامMutableLiveData. - يمكن قراءة البيانات في عنصر
LiveData، ولكن لا يمكن تغييرها. من خارجViewModel، يجب أن تكون البيانات قابلة للقراءة، ولكن ليس قابلة للتعديل، لذا يجب عرض البيانات على أنّهاLiveData.
لتنفيذ هذه الاستراتيجية، يمكنك استخدام سمة احتياطية في Kotlin. تتيح لك السمة الاحتياطية عرض قيمة من دالة جلب غير الكائن المحدد. في هذه المهمة، ستنفّذ خاصية احتياطية للكائنَين score وword في تطبيق GuessTheWord.
إضافة سمة داعمة إلى النتيجة والكلمة
- في
GameViewModel، اجعل كائنscoreالحاليprivate. - للتوافق مع اصطلاح التسمية المستخدَم في الخصائص الداعمة، غيِّر
scoreإلى_score. أصبحت السمة_scoreالآن هي النسخة القابلة للتغيير من نتيجة المباراة، ويجب استخدامها داخليًا. - أنشئ نسخة علنية من النوع
LiveDataباسمscore.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>- تظهر لك رسالة خطأ في الإعداد. يحدث هذا الخطأ لأنّ
scoreداخلGameFragmentهو مرجعLiveData، ولم يعُد بإمكانscoreالوصول إلى الدالة الضابطة. لمزيد من المعلومات حول دوال الجلب والتعديل في Kotlin، يمكنك الاطّلاع على دوال الجلب والتعديل.
لحلّ الخطأ، عليك إلغاء طريقة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
ينتقِل تطبيقك الحالي إلى شاشة النتائج عندما ينقر المستخدم على الزر إنهاء اللعبة. عليك أيضًا أن تجعل التطبيق ينتقل إلى شاشة النتائج عندما يكمل اللاعبون جميع الكلمات. بعد أن ينتهي اللاعبون من الكلمة الأخيرة، تريد أن تنتهي اللعبة تلقائيًا حتى لا يضطر المستخدم إلى النقر على الزر.
لتنفيذ هذه الوظيفة، تحتاج إلى حدث يتم تشغيله وإرساله إلى الجزء من ViewModel عندما يتم عرض جميع الكلمات. لإجراء ذلك، يمكنك استخدام نمط المراقب LiveData لإنشاء نموذج لحدث انتهاء اللعبة.
نمط المراقب
نمط المراقب هو نمط تصميم برامج. تحدّد هذه السمة التواصل بين الكائنات: كائن قابل للمراقبة (وهو "موضوع" المراقبة) ومراقبون. الكائن القابل للملاحظة هو كائن يُعلم المراقبين بالتغييرات في حالته.

في حالة LiveData في هذا التطبيق، يكون العنصر القابل للملاحظة (الموضوع) هو العنصر LiveData، وتكون العناصر المراقِبة هي الطرق في وحدات التحكّم في واجهة المستخدم، مثل الأجزاء. يحدث تغيير في الحالة كلما تغيّرت البيانات المضمّنة في LiveData. تُعدّ فئات LiveData ضرورية للتواصل من ViewModel إلى الجزء.
الخطوة 1: استخدام LiveData لرصد حدث انتهاء اللعبة
في هذه المهمة، ستستخدم LiveData نمط المراقب لنمذجة حدث انتهاء اللعبة.
- في
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: إعادة ضبط حدث انتهاء اللعبة
عادةً، لا يرسل LiveData تحديثات إلى المراقبين إلا عند تغيُّر البيانات. ويستثنى من هذا السلوك أنّ المراقبين يتلقّون أيضًا إشعارات عندما تتغيّر حالة المراقب من غير نشط إلى نشط.
لهذا السبب يتم تشغيل إشعار "اكتملت اللعبة" بشكل متكرّر في تطبيقك. وعند إعادة إنشاء جزء اللعبة بعد تدوير الشاشة، ينتقل من حالة غير نشطة إلى حالة نشطة. يتم إعادة ربط المراقب في الجزء بـ ViewModel الحالي ويتلقّى البيانات الحالية. يتم إعادة تشغيل الطريقة gameFinished()، ويظهر الإشعار.
في هذه المهمة، عليك حلّ هذه المشكلة وعرض الإشعار مرة واحدة فقط، وذلك عن طريق إعادة ضبط العلامة eventGameFinish في GameViewModel.
- في
GameViewModel، أضِف طريقةonGameFinishComplete()لإعادة ضبط حدث انتهاء اللعبة،_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"، حدِّد الأسطر التي تم التعليق عليها واضغط على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" ذلك، استورِد 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ومراقبًا لتعديل النتيجة.
في هذه المهمة، ستضيف زر اللعب مرة أخرى إلى شاشة النتيجة وتنفّذ أداة معالجة النقرات باستخدام حدث LiveData. يؤدي الزر إلى تشغيل حدث للانتقال من شاشة النتيجة إلى شاشة اللعبة.
يتضمّن الرمز البرمجي الأولي للتطبيق الزر تشغيل مرة أخرى، ولكنّه مخفي.
- في
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() }- شغِّل تطبيقك والعب اللعبة. عند انتهاء المباراة، تعرض شاشة النتائج النتيجة النهائية والزر لعب مجددًا. انقر على الزر PlayAgain، وسينتقِل التطبيق إلى شاشة اللعبة لتتمكّن من لعبها مرة أخرى.

عمل رائع لقد غيّرت بنية تطبيقك لاستخدام عناصر LiveData في ViewModel، وربطت أدوات مراقبة بعناصر LiveData. LiveData تُرسِل إشعارات إلى الكائنات المراقِبة عندما تتغيّر القيمة التي تحتفظ بها LiveData.
مشروع "استوديو Android": 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 عرض قيمة من دالة الجلب تختلف عن الكائن المحدد.
- لتضمين
LiveData، استخدِمprivateMutableLiveDataداخلViewModelوأدرِج سمةLiveDataخلفية خارجViewModel.
LiveData قابلة للتتبّع
- يتبع
LiveDataنمط المراقب. إنّ "العنصر القابل للمراقبة" هو الكائنLiveData، والمراقبون هم الطرق في وحدات التحكّم في واجهة المستخدم، مثل الأجزاء. عندما تتغيّر البيانات المضمّنة فيLiveData، يتم إرسال إشعار إلى طرق المراقبة في أدوات التحكّم في واجهة المستخدم. - لجعل
LiveDataقابلاً للمراقبة، أضِف عنصر مراقبة إلى مرجعLiveDataفي عناصر المراقبة (مثل الأنشطة واللقطات) باستخدام الطريقةobserve(). - يمكن استخدام نمط المراقبة
LiveDataهذا للتواصل منViewModelإلى أدوات التحكّم في واجهة المستخدم.
دورة Udacity التدريبية:
مستندات مطوّري تطبيقات Android:
غير ذلك:
- الخاصية الاحتياطية في Kotlin
يسرد هذا القسم مهامًا منزلية محتملة للطلاب الذين يعملون على هذا الدرس التطبيقي العملي كجزء من دورة تدريبية يقودها مدرّب. على المعلّم تنفيذ ما يلي:
- حدِّد واجبًا منزليًا إذا لزم الأمر.
- توضيح كيفية إرسال الواجبات المنزلية للطلاب
- وضع درجات للواجبات المنزلية
يمكن للمدرّبين استخدام هذه الاقتراحات بالقدر الذي يريدونه، ويجب ألا يترددوا في تكليف الطلاب بأي واجبات منزلية أخرى يرونها مناسبة.
إذا كنت تعمل على هذا الدرس العملي بنفسك، يمكنك استخدام مهام الواجب المنزلي هذه لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 1
كيف يمكنك تغليف LiveData المخزّنة في ViewModel حتى تتمكّن الكائنات الخارجية من قراءة البيانات بدون إمكانية تعديلها؟
- داخل العنصر
ViewModel، غيِّر نوع البيانات إلىprivateLiveData. استخدِم خاصية داعمة لعرض بيانات للقراءة فقط من النوعMutableLiveData. - داخل العنصر
ViewModel، غيِّر نوع البيانات إلىprivateMutableLiveData. استخدِم خاصية داعمة لعرض بيانات للقراءة فقط من النوعLiveData. - داخل وحدة التحكّم في واجهة المستخدم، غيِّر نوع البيانات إلى
privateMutableLiveData. استخدِم خاصية داعمة لعرض بيانات للقراءة فقط من النوعLiveData. - داخل العنصر
ViewModel، غيِّر نوع البيانات إلىLiveData. استخدِم خاصية داعمة لعرض بيانات للقراءة فقط من النوعLiveData.
السؤال 2
تعدِّل السمة LiveData وحدة التحكّم في واجهة المستخدم (مثل جزء) إذا كانت وحدة التحكّم في واجهة المستخدم في أيّ من الحالات التالية؟
- تم الاستئناف
- في الخلفية
- متوقف مؤقتًا
- متوقفة
السؤال 3
في نمط المراقب LiveData، ما هو العنصر القابل للمراقبة (ما تتم مراقبته)؟
- طريقة المراقبة
- البيانات في عنصر
LiveData - وحدة التحكّم في واجهة المستخدم
- العنصر
ViewModel
ابدأ الدرس التالي:
للحصول على روابط تؤدي إلى دروس تطبيقية أخرى في هذه الدورة التدريبية، اطّلِع على الصفحة المقصودة الخاصة بالدروس التطبيقية حول أساسيات Android Kotlin.




