Android Kotlin Fundamentals 05.2: LiveData وLiveData مراقبة

يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات Android Kotlin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورات التدريبية في الصفحة المقصودة لدروس الترميز Android Kotlin Fundamentals.

مقدمة

في الدرس التطبيقي السابق، استخدِمت ViewModel في تطبيق GuessTheWord للسماح لبيانات التطبيق بالوصول إلى التغييرات التي تطرأ على إعدادات الجهاز. في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية دمج LiveData مع البيانات في الصفوف ViewModel. يتيح لك LiveData، وهو أحد مكوّنات بنية Android، إنشاء كائنات البيانات التي تنبّه المشاهدات عند تغيير قاعدة البيانات الأساسية.

لاستخدام فئة LiveData، عليك إعداد "observers" (على سبيل المثال، الأنشطة أو الأجزاء) التي تلاحظ التغييرات في بيانات التطبيق. يُعد LiveData برنامجًا واعيًا لمراحل الحياة، ولذلك يتم تحديث مراقبي مكونات التطبيق فقط الذين تكون في حالة نشاط نشطة.

ما يجب معرفته

  • كيفية إنشاء تطبيقات Android الأساسية في Kotlin.
  • كيفية التنقل بين وجهات تطبيقك.
  • النشاط وجزء مراحل النشاط.
  • كيفية استخدام عناصر ViewModel في تطبيقك.
  • كيفية إنشاء عناصر ViewModel باستخدام واجهة ViewModelProvider.Factory

ما ستتعرَّف عليه

  • العناصر التي تجعل عناصر LiveData مفيدة
  • طريقة إضافة LiveData إلى البيانات المخزّنة في ViewModel
  • وقت الاستخدام وكيفية استخدامه MutableLiveData.
  • كيفية إضافة طرق الملاحظة لمراقبة التغييرات في LiveData.
  • كيفية تضمين LiveData باستخدام خاصية احتياطية.
  • كيفية الاتصال بين وحدة تحكم واجهة المستخدم وViewModel المقابلة لها.

الإجراءات التي ستنفذّها

  • استخدم LiveData للكلمة والنتيجة في تطبيق GuessTheWord.
  • أضِف مراقبين يرصدون تغيّر الكلمة أو النتيجة.
  • عدِّل طرق عرض النص التي تعرض قيمًا تم تغييرها.
  • استخدِم نمط المراقب LiveData لإضافة حدث انتهى من اللعبة.
  • نفِّذ الزر التشغيل مرة أخرى.

في الدرس التطبيقي حول الترميز، يمكنك تطوير تطبيق GuessTheWord الذي يبدأ برمز التفعيل. GuessTheWord هي لعبة من فئة charades ثنائية اللاعبين، حيث يتعاون اللاعبون لتحقيق أعلى نتيجة ممكنة.

ينظر اللاعب الأول إلى الكلمات في التطبيق ويدور كل منها بدوره، مع التأكد من عدم عرض الكلمة على اللاعب الثاني. يحاول اللاعب الثاني تخمين الكلمة.

لتشغيل اللعبة، يفتح المشغِّل الأول التطبيق على الجهاز ويرى كلمة، على سبيل المثال "غيتار&"& كما يظهر في لقطة الشاشة أدناه.

يمارس اللاعب الأول الكلمة، مع الحرص على عدم قول الكلمة نفسها في الواقع.

  • عندما يخمّن اللاعب الثاني الكلمة بشكلٍ صحيح، يضغط اللاعب الأول على الزر حسنًا، ما يؤدي إلى زيادة العدد بمقدار كلمة واحدة وعرض الكلمة التالية.
  • إذا لم يتمكّن المشغّل الثاني من تخمين الكلمة، يضغط اللاعب الأول على الزر التخطّي، ما يؤدي إلى خفض العدد بمقدار كلمة واحدة والانتقال إلى الكلمة التالية.
  • لإنهاء اللعبة، اضغط على زر إنهاء اللعبة. (هذه الوظيفة ليست في رمز بدء الدرس التطبيقي للترميز الأول في السلسلة).

في هذا الدرس التطبيقي حول الترميز، يمكنك تحسين تطبيق GuessTheWord من خلال إضافة حدث لإنهاء اللعبة عند تصفّح المستخدم جميع الكلمات في التطبيق. وإضافةً إلى زر اللعب مرة أخرى في جزء النتيجة، حتى يتمكّن المستخدم من تشغيل اللعبة مرة أخرى.

شاشة العنوان

شاشة اللعبة

شاشة النتائج

في هذه المَهمّة، يمكنك تحديد مكان رمز تفعيل هذا الدرس التطبيقي وتشغيله. يمكنك استخدام تطبيق GuessTheWord الذي أنشأته في الدرس التطبيقي السابق كرمز للمبتدئين، أو يمكنك تنزيل تطبيق للمبتدئين.

  1. (اختياري) في حال عدم استخدام الرمز من الدرس التطبيقي السابق، يُرجى تنزيل رمز التفعيل لهذا الدرس التطبيقي. فكّ ضغط الرمز وافتح المشروع في Android Studio.
  2. شغِّل التطبيق وشغِّل اللعبة.
  3. لاحظ أن الزر تخط يعرض الكلمة التالية ويقلل النتيجة بمقدار كلمة واحدة، ويعرض الزر حسنًا الكلمة التالية ويزيد النتيجة بمقدار كلمة رئيسية واحدة. يؤدي النقر على زر إنهاء اللعبة إلى إنهاء اللعبة.

LiveData هي فئة حامل بيانات يمكن ملاحظتها، تعيّن دورة الحياة. على سبيل المثال، يمكنك التفاف LiveData حول النتيجة الحالية في تطبيق GuessTheWord. في هذا الدرس التطبيقي حول الترميز، ستتعرّف على العديد من خصائص LiveData:

  • يمكن ملاحظة LiveData، ما يعني أنه يتم إشعار المراقب عند تغيير البيانات التي يحتفظ بها عنصر LiveData.
  • LiveData يحتفظ بالبيانات، وLiveData برنامج تضمين يمكن استخدامه مع أي بيانات
  • يعمل LiveData على مراحل النشاط، ما يعني أنّه لا يُحدِّث سوى المراقبين الذين يكونون في حالة دورة حياة نشطة، مثل STARTED أو RESUMED.

في هذه المهمة، ستتعرّف على كيفية لف أي نوع من أنواع البيانات في عناصر LiveData عن طريق تحويل النتيجة الحالية وبيانات الكلمة الحالية في GameViewModel إلى LiveData. في مهمة لاحقة، يمكنك إضافة مراقب إلى كائنات LiveData هذه والتعرّف على كيفية رصد LiveData.

الخطوة 1: تغيير النتيجة والكلمة لاستخدام LiveData

  1. ضمن حزمة screens/game، افتح ملف GameViewModel.
  2. غيِّر نوع المتغيّرَين score وword إلى MutableLiveData.

    MutableLiveData عبارة عن LiveData يمكن تغيير قيمته. MutableLiveData هي فئة عامة، لذا عليك تحديد نوع البيانات التي تحتفظ بها.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
  1. في GameViewModel، داخل كتلة init، عليك إعداد score وword. لتغيير قيمة متغير LiveData، يمكنك استخدام طريقة setValue() على المتغير. وفي لغة Kotlin، يمكنك الاتصال بالرقم setValue() باستخدام السمة value.
init {

   word.value = ""
   score.value = 0
  ...
}

الخطوة 2: تعديل مرجع كائن LiveData

أصبح المتغيران score وword الآن من النوع LiveData. في هذه الخطوة، يمكنك تغيير الإشارات إلى هذه المتغيرات باستخدام السمة value.

  1. في GameViewModel، باستخدام الطريقة onSkip()، غيِّر score إلى score.value. يمكنك ملاحظة الخطأ الذي قد يكون score يتضمن null. أصلِح هذا الخطأ بعد ذلك.
  2. لإصلاح الخطأ، أضف فحص null إلى score.value في onSkip(). بعد ذلك، تستدعي الدالة minus() على score، التي تجري عملية الجمع باستخدام null-safety.
fun onSkip() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.minus(1)
   }
   nextWord()
}
  1. عدِّل طريقة onCorrect() بالطريقة نفسها: أضِف فحص null إلى المتغيّر score واستخدِم الدالة plus().
fun onCorrect() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.plus(1)
   }
   nextWord()
}
  1. في GameViewModel، داخل طريقة nextWord()، غيِّر مرجع word إلى word.value.
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       word.value = wordList.removeAt(0)
   }
}
  1. في GameFragment، داخل طريقة updateWordText()، غيِّر المرجع إلى viewModel.word إلى viewModel.word.value.
/** Methods for updating the UI **/
private fun updateWordText() {
   binding.wordText.text = viewModel.word.value
}
  1. في GameFragment، داخل طريقة updateScoreText()، غيّر الإشارة إلى viewModel.score إلى viewModel.score.value.
private fun updateScoreText() {
   binding.scoreText.text = viewModel.score.value.toString()
}
  1. في 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)
}
  1. تأكّد من عدم وجود أخطاء في الرمز. اجمع تطبيقك وشغّله. يجب أن تكون وظيفة التطبيق هي نفسها كما كانت من قبل.

ترتبط هذه المهمة ارتباطًا وثيقًا بالمهمة السابقة، حيث حوَّلت بيانات النتيجة والكلمة إلى LiveData عناصر. في هذه المهمة، يمكنك إرفاق كائنات Observer إلى كائنات LiveData هذه.

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

  1. يتلقى المُلاحظ الذي أنشأته للتو حدثًا عندما تتغير البيانات التي يحافظ عليها العنصر LiveData الملاحَظ. داخل المراقب، عدِّل النتيجة TextView باستخدام النتيجة الجديدة.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. يمكنك إرفاق كائن Observer إلى الكلمة الحالية LiveData. نفِّذ الإجراء بالطريقة نفسها التي أرفقت بها عنصر Observer بالنتيجة الحالية.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})

عندما تتغير قيمة score أو word، يتم تعديل score أو word المعروضة على الشاشة الآن تلقائيًا.

  1. في GameFragment، عليك حذف الطُرق updateWordText() وupdateScoreText() وجميع الإشارات إليها. لم تعد بحاجة إليها، لأنه يتم تعديل طرق عرض النص باستخدام طرق المراقب LiveData.
  2. شغِّل تطبيقك. يجب أن يعمل تطبيق اللعبة تمامًا كما كان الحال من قبل، ولكنه الآن يستخدم مراقبي LiveData وLiveData.

الإغلاق هو طريقة لتقييد الوصول المباشر إلى بعض حقول العنصر. عند تضمين عنصر، يتم عرض مجموعة من الطرق العامة التي تعمل على تعديل الحقول الداخلية الخاصة. وباستخدام هذه الطريقة، يمكنك التحكّم في كيفية تعامل الصفوف الأخرى مع هذه الحقول الداخلية.

في رمزك الحالي، يمكن لأي فئة خارجية تعديل المتغيّرَين score وword باستخدام السمة value، على سبيل المثال باستخدام viewModel.score.value. قد لا يهم في التطبيق الذي تطوّره في هذا الدرس التطبيقي، ولكن في تطبيق إنتاج، تحتاج إلى التحكّم في البيانات في كائنات ViewModel.

يجب أن يعدّل ViewModel البيانات في تطبيقك فقط، لكن وحدات التحكّم في واجهة المستخدم تحتاج إلى قراءة البيانات، لذا لا يمكن أن تكون حقول البيانات خاصة تمامًا. لتغليف بيانات تطبيقك، يمكنك استخدام عناصر MutableLiveData وLiveData.

MutableLiveData في مقابل LiveData:

  • يمكن تغيير البيانات في عنصر MutableLiveData، كما يشير الاسم ضمنًا. داخل ViewModel، يجب أن تكون البيانات قابلة للتعديل، لذلك تستخدم MutableLiveData.
  • يمكن قراءة البيانات في عنصر LiveData، ولكن لا يمكن تغييرها. من خارج ViewModel، يجب أن تكون البيانات قابلة للقراءة، ولكن غير قابلة للتعديل، لذلك يجب أن يتم عرض البيانات كـ LiveData.

لتنفيذ هذه الاستراتيجية، يمكنك استخدام موقع إلكتروني متوافق مع لغة Kotlin. تتيح لك الخاصية الاحتياطية إرجاع عنصر من إرجاع غير الكائن الدقيق. في هذه المهمة، ننفِّذ خاصية احتياطية للكائنات score وword في تطبيق GuessTheWord.

إضافة خاصية احتياطية للنتيجة والكلمة

  1. في GameViewModel، اجعل العنصر score الحالي private.
  2. لاتّباع اصطلاح التسمية المستخدَم في الخصائص الاحتياطية، غيِّر score إلى _score. أصبحت السمة _score الآن الإصدار القابل للتغيير لنتيجة اللعبة، ويجب استخدامها داخليًا.
  3. أنشئ نسخة عامة من النوع LiveData، تُسمى score.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
  1. حدث خطأ في الإعداد. حدث هذا الخطأ لأنّ القيمة score هي مرجع LiveData داخل GameFragment، ولم يعُد بإمكان score الوصول إلى العنصر الذي تم ضبطه. للتعرُّف على المزيد من المعلومات عن أدوات الحصول على الأحرف وإعدادها بلغة Kotlin، يُرجى الاطّلاع على Getter and setter.

    لحل الخطأ، يمكنك إلغاء طريقة get() لكائن score في GameViewModel وعرض خاصية الاحتفاظ بنسخة احتياطية، _score.
val score: LiveData<Int>
   get() = _score
  1. في 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)
   }
   ...
}
  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 لوضع نموذج لحدث تم إكماله في اللعبة.

نمط أداة الملاحظة

نمط الخادم هو نمط تصميم البرامج. وهي تحدد الاتصال بين العناصر: ملاحظة يمكن ملاحظتها ("&&;;subject" الملاحظة) والملاحظون. الكائن القابل للملاحظة هو كائن يُعلم المراقبين بالتغييرات في حالته.

في حال استخدام LiveData في هذا التطبيق، يكون العنصر القابل للملاحظة (الموضوع) هو العنصر LiveData، ويمكن استخدام عناصر الرصد في وحدات التحكُّم في واجهة المستخدم، مثل الأجزاء. يحدث تغيير في الحالة كلما تغيّرت البيانات داخل ملف LiveData. تكون الصفوف LiveData مهمة في التواصل من ViewModel إلى الجزء.

الخطوة 1: استخدام LiveData لرصد حدث انتهى من اللعبة

في هذه المَهمّة، يمكنك استخدام نمط المراقب LiveData لتقديم نموذج لحدث تمّ إكماله في اللعبة.

  1. في GameViewModel، أنشِئ عنصر Boolean MutableLiveData باسم _eventGameFinish. وسيحتفظ هذا العنصر بالحدث الذي أنهيته اللعبة.
  2. بعد إعداد الكائن _eventGameFinish، يمكنك إنشاء خاصية احتياطية وضبط إعداداتها باسم eventGameFinish.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
   get() = _eventGameFinish
  1. في GameViewModel، أضِف طريقة onGameFinish(). في الطريقة، اضبُط الحدث المنتهية اللعبة، eventGameFinish، على true.
/** Method for the game completed event **/
fun onGameFinish() {
   _eventGameFinish.value = true
}
  1. في GameViewModel، داخل طريقة nextWord()، يمكنك إنهاء اللعبة إذا كانت قائمة الكلمات فارغة.
private fun nextWord() {
   if (wordList.isEmpty()) {
       onGameFinish()
   } else {
       //Select and remove a _word from the list
       _word.value = wordList.removeAt(0)
   }
}
  1. في GameFragment، داخل onCreateView()، بعد إعداد viewModel، وصِّل مراقبًا بـ eventGameFinish. استخدام طريقة observe() داخل دالة lambda، يجب استدعاء الطريقة gameFinished().
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
   if (hasFinished) gameFinished()
})
  1. يمكنك تشغيل تطبيقك وتشغيل اللعبة ومتابعة جميع الكلمات. ينتقل التطبيق إلى شاشة النتائج تلقائيًا، بدلاً من البقاء في جزء اللعبة حتى تنقر على إنهاء اللعبة.

    بعد ترك قائمة الكلمات فارغة، يتم ضبط eventGameFinish، ويتم استدعاء طريقة أداة المراقبة المرتبطة في جزء اللعبة، وينتقل التطبيق إلى جزء الشاشة.
  2. تسبب الرمز الذي أضفته في حدوث مشكلة في مراحل النشاط. لفهم المشكلة، عليك تعليق رمز التنقّل في طريقة gameFinished() في صف GameFragment. تأكَّد من إبقاء رسالة 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)
   }
  1. يمكنك تشغيل تطبيقك وتشغيل اللعبة ومتابعة جميع الكلمات. ستظهر رسالة قصيرة تفيد بأنّ "اللعبة قد انتهت" وتظهر قريبًا في أسفل شاشة اللعبة، وهذا هو السلوك المتوقّع.

والآن، حرِّك الجهاز أو المحاكي. يتم عرض الخبز المحمّص مرة أخرى. يُرجى تدوير الجهاز عدة مرات، وسترى على الأرجح الخبز المحمّل في كل مرة. هذا خلل، لأن الخبز المحمص يجب عرضه مرة واحدة فقط، عند انتهاء اللعبة. يجب ألا يظهر الخبز المحمص في كل مرة تتم فيها إعادة إنشاء الجزء. يمكنك حل هذه المشكلة في المهمة التالية.

الخطوة 2: إعادة ضبط الحدث الذي انتهت فيه اللعبة

عادةً ما يرسل LiveData تحديثات للمراقبين فقط عند تغيير البيانات. ويُستثنى من هذا السلوك أن المُشاهدين يتلقّون أيضًا آخر الأخبار عندما تتغيّر حالة المُشاهد من حالة نشطة إلى غير نشطة.

ولهذا السبب، يتم تشغيل الخبز المحمّل الذي انتهت فيه اللعبة بشكل متكرّر في تطبيقك. وعندما تتم إعادة إنشاء جزء اللعبة بعد تدوير الشاشة، ينتقل من حالة غير نشطة إلى حالة نشطة. تتم إعادة ربط المراقب في الجزء بـ ViewModel الحالي ويتلقى البيانات الحالية. تتم إعادة تفعيل طريقة gameFinished()، ويتم عرض الخبز المحمّص.

في هذه المهمة، يمكنك حل هذه المشكلة وعرض الخبز المحمّص مرة واحدة فقط، من خلال إعادة ضبط العلامة eventGameFinish في GameViewModel.

  1. في GameViewModel، أضِف طريقة onGameFinishComplete() لإعادة ضبط حدث انتهاء اللعبة، _eventGameFinish.
/** Method for the game completed event **/

fun onGameFinishComplete() {
   _eventGameFinish.value = false
}
  1. في نهاية gameFinished()، في GameFragment، يمكنك استدعاء onGameFinishComplete() في الكائن viewModel. (اترك رمز التنقل في gameFinished() معلقًا الآن.)
private fun gameFinished() {
   ...
   viewModel.onGameFinishComplete()
}
  1. شغِّل التطبيق وشغِّل اللعبة. يمكنك الاطّلاع على جميع الكلمات، ثم تغيير اتجاه شاشة الجهاز. يتم عرض الخبز المحمّل مرة واحدة فقط.
  2. في 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()
}

يمكنك استيراد androidx.navigation.fragment.NavHostFragment.findNavController إذا طُلب منك ذلك من خلال "استوديو Android".

  1. شغِّل التطبيق وشغِّل اللعبة. تأكّد من أن التطبيق ينتقل تلقائيًا إلى شاشة النتيجة النهائية بعد الاطّلاع على جميع الكلمات.

إجابتك صحيحة. يستخدم تطبيقك LiveData لتشغيل حدث منتهية اللعبة للتواصل من جزء GameViewModel إلى جزء اللعبة الذي يشير إلى أن قائمة الكلمات فارغة. ينتقل جزء اللعبة بعد ذلك إلى جزء النتيجة.

في هذه المهمة، يمكنك تغيير النتيجة إلى عنصر LiveData في ScoreViewModel وإرفاق مراقب بها. تشبه هذه المهمة ما فعلته عند إضافة LiveData إلى GameViewModel.

يمكنك إجراء هذه التغييرات على ScoreViewModel للاكتمال، حتى تستخدم جميع البيانات في تطبيقك LiveData.

  1. في ScoreViewModel، غيِّر نوع المتغيّر score إلى MutableLiveData. عليك إعادة تسميتها من خلال اصطلاح إلى _score وإضافة خاصية الاحتفاظ بنسخة احتياطية.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
   get() = _score
  1. في ScoreViewModel، داخل كتلة init، عليك إعداد _score. يمكنك إزالة السجلّ أو تركه في كتلة init كما تشاء.
init {
   _score.value = finalScore
}
  1. في 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"، استورِد androidx.lifecycle.Observer.

  1. شغِّل تطبيقك وشغِّل اللعبة. من المفترض أن يعمل التطبيق كما في السابق، ولكنه الآن يستخدم LiveData ومُراقبًا لتعديل النتيجة.

في هذه المَهمّة، يمكنك إضافة الزرّ تشغيل مجددًا إلى شاشة النتيجة وتنفيذ مستمع النقر الخاص به باستخدام حدث LiveData. سيؤدي الزر إلى تشغيل حدث للانتقال من شاشة النتائج إلى شاشة اللعبة.

يحتوي رمز إجراء التفعيل الخاص بالتطبيق على الزر تشغيل مجددًا، ولكن الزر مخفي.

  1. في res/layout/score_fragment.xml، بالنسبة إلى الزر play_again_button، غيِّر قيمة السمة visibility إلى visible.
<Button
   android:id="@+id/play_again_button"
...
   android:visibility="visible"
 />
  1. في ScoreViewModel، أضِف كائن LiveData لتثبيت Boolean باسم _eventPlayAgain. يُستخدم هذا الكائن لحفظ حدث LiveData للتنقل من شاشة النتائج إلى شاشة اللعبة.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
   get() = _eventPlayAgain
  1. في ScoreViewModel، حدِّد طرقًا لإعداد الحدث وإعادة ضبطه، _eventPlayAgain.
fun onPlayAgain() {
   _eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
   _eventPlayAgain.value = false
}
  1. في 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"

  1. ضمن ScoreFragment، داخل onCreateView()، أضِف أداة معالجة نقرة على الزر PlayAgain واتصل بالرقم viewModel.onPlayAgain().
binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. شغِّل تطبيقك وشغِّل اللعبة. عند انتهاء المباراة، تعرض شاشة النتائج النتيجة النهائية والزر تشغيل مرة أخرى. انقر على الزر PlayAgain وانتقِل التطبيق إلى شاشة اللعبة حتى تتمكّن من تشغيل اللعبة مرة أخرى.

عمل جيد لقد غيّرت بنية تطبيقك لاستخدام LiveDataعناصر في ViewModel، وأرفقت مراقبين بكائنات LiveData. LiveData تُبلِغ كائنات المُراقب عندما تتغير القيمة التي تحتفظ بها LiveData.

مشروع Android Studio: GuessTheWord

بيانات مباشرة

  • 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 في المراقبات (مثل الأنشطة والأجزاء) باستخدام طريقة observe().
  • يمكن استخدام نمط أداة المراقبة LiveData هذا للتواصل من ViewModel إلى وحدات التحكُّم في واجهة المستخدم.

دورة Udacity:

مستندات مطوّر برامج Android:

غير ذلك:

يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:

  • يمكنك تخصيص واجب منزلي إذا لزم الأمر.
  • التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
  • وضع درجات للواجبات المنزلية.

ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.

إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.

الإجابة عن هذه الأسئلة

السؤال 1

كيف يتم حصر LiveData المخزّن في ViewModel بحيث تتمكن العناصر الخارجية من قراءة البيانات بدون القدرة على تحديثها؟

  • داخل الكائن ViewModel، يمكنك تغيير نوع البيانات إلى private LiveData. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوع MutableLiveData.
  • داخل الكائن ViewModel، يمكنك تغيير نوع البيانات إلى private MutableLiveData. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوع LiveData.
  • ضمن وحدة التحكُّم في واجهة المستخدم، غيِّر نوع البيانات إلى private MutableLiveData. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوع LiveData.
  • داخل الكائن ViewModel، يمكنك تغيير نوع البيانات إلى LiveData. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوع LiveData.

السؤال 2

يحدّث LiveData وحدة التحكم في واجهة المستخدم (مثل جزء) إذا كانت وحدة التحكم في واجهة المستخدم بأي من الحالات التالية؟

  • تم الاستئناف
  • في الخلفية
  • متوقفة مؤقتًا
  • موقوف

السؤال 3

في نمط أداة الملاحظة LiveData، ما هو العنصر القابل للملاحظة (ما يُلاحظه)؟

  • طريقة أداة الملاحظة
  • البيانات في عنصر LiveData
  • وحدة التحكُّم في واجهة المستخدم
  • الكائن ViewModel

بدء الدرس التالي: 5.3: ربط البيانات بنموذج المشاهدة وLiveData

وللحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، يُرجى الاطّلاع على الصفحة المقصودة لتطبيق الدروس التطبيقية حول الترميز Kotlin Fundamentals.