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