يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات 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 الذي أنشأته في الدرس التطبيقي السابق كرمز للمبتدئين، أو يمكنك تنزيل تطبيق للمبتدئين.
- (اختياري) في حال عدم استخدام الرمز من الدرس التطبيقي السابق، يُرجى تنزيل رمز التفعيل لهذا الدرس التطبيقي. فكّ ضغط الرمز وافتح المشروع في Android Studio.
- شغِّل التطبيق وشغِّل اللعبة.
- لاحظ أن الزر تخط يعرض الكلمة التالية ويقلل النتيجة بمقدار كلمة واحدة، ويعرض الزر حسنًا الكلمة التالية ويزيد النتيجة بمقدار كلمة رئيسية واحدة. يؤدي النقر على زر إنهاء اللعبة إلى إنهاء اللعبة.
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
هي مرجعLiveData
داخلGameFragment
، ولم يعُد بإمكانscore
الوصول إلى العنصر الذي تم ضبطه. للتعرُّف على المزيد من المعلومات عن أدوات الحصول على الأحرف وإعدادها بلغة Kotlin، يُرجى الاطّلاع على Getter and setter.
لحل الخطأ، يمكنك إلغاء طريقة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
لوضع نموذج لحدث تم إكماله في اللعبة.
نمط أداة الملاحظة
نمط الخادم هو نمط تصميم البرامج. وهي تحدد الاتصال بين العناصر: ملاحظة يمكن ملاحظتها ("&&;;subject" الملاحظة) والملاحظون. الكائن القابل للملاحظة هو كائن يُعلم المراقبين بالتغييرات في حالته.
في حال استخدام 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
، ويتم استدعاء طريقة أداة المراقبة المرتبطة في جزء اللعبة، وينتقل التطبيق إلى جزء الشاشة. - تسبب الرمز الذي أضفته في حدوث مشكلة في مراحل النشاط. لفهم المشكلة، عليك تعليق رمز التنقّل في طريقة
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)
}
- يمكنك تشغيل تطبيقك وتشغيل اللعبة ومتابعة جميع الكلمات. ستظهر رسالة قصيرة تفيد بأنّ "اللعبة قد انتهت" وتظهر قريبًا في أسفل شاشة اللعبة، وهذا هو السلوك المتوقّع.
والآن، حرِّك الجهاز أو المحاكي. يتم عرض الخبز المحمّص مرة أخرى. يُرجى تدوير الجهاز عدة مرات، وسترى على الأرجح الخبز المحمّل في كل مرة. هذا خلل، لأن الخبز المحمص يجب عرضه مرة واحدة فقط، عند انتهاء اللعبة. يجب ألا يظهر الخبز المحمص في كل مرة تتم فيها إعادة إنشاء الجزء. يمكنك حل هذه المشكلة في المهمة التالية.
الخطوة 2: إعادة ضبط الحدث الذي انتهت فيه اللعبة
عادةً ما يرسل LiveData
تحديثات للمراقبين فقط عند تغيير البيانات. ويُستثنى من هذا السلوك أن المُشاهدين يتلقّون أيضًا آخر الأخبار عندما تتغيّر حالة المُشاهد من حالة نشطة إلى غير نشطة.
ولهذا السبب، يتم تشغيل الخبز المحمّل الذي انتهت فيه اللعبة بشكل متكرّر في تطبيقك. وعندما تتم إعادة إنشاء جزء اللعبة بعد تدوير الشاشة، ينتقل من حالة غير نشطة إلى حالة نشطة. تتم إعادة ربط المراقب في الجزء بـ ViewModel
الحالي ويتلقى البيانات الحالية. تتم إعادة تفعيل طريقة gameFinished()
، ويتم عرض الخبز المحمّص.
في هذه المهمة، يمكنك حل هذه المشكلة وعرض الخبز المحمّص مرة واحدة فقط، من خلال إعادة ضبط العلامة eventGameFinish
في GameViewModel
.
- في
GameViewModel
، أضِف طريقةonGameFinishComplete()
لإعادة ضبط حدث انتهاء اللعبة،_eventGameFinish
.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}
- في نهاية
gameFinished()
، فيGameFragment
، يمكنك استدعاء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()
}
يمكنك استيراد androidx.navigation.fragment.NavHostFragment.findNavController
إذا طُلب منك ذلك من خلال "استوديو Android".
- شغِّل التطبيق وشغِّل اللعبة. تأكّد من أن التطبيق ينتقل تلقائيًا إلى شاشة النتيجة النهائية بعد الاطّلاع على جميع الكلمات.
إجابتك صحيحة. يستخدم تطبيقك 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"، استورِد 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"
- ضمن
ScoreFragment
، داخلonCreateView()
، أضِف أداة معالجة نقرة على الزر PlayAgain واتصل بالرقمviewModel
.onPlayAgain()
.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }
- شغِّل تطبيقك وشغِّل اللعبة. عند انتهاء المباراة، تعرض شاشة النتائج النتيجة النهائية والزر تشغيل مرة أخرى. انقر على الزر 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:
غير ذلك:
- الخاصية الاحتياطية في لغة Kotlin
يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:
- يمكنك تخصيص واجب منزلي إذا لزم الأمر.
- التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
- وضع درجات للواجبات المنزلية.
ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.
إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 1
كيف يتم حصر LiveData
المخزّن في ViewModel
بحيث تتمكن العناصر الخارجية من قراءة البيانات بدون القدرة على تحديثها؟
- داخل الكائن
ViewModel
، يمكنك تغيير نوع البيانات إلىprivate
LiveData
. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوعMutableLiveData
. - داخل الكائن
ViewModel
، يمكنك تغيير نوع البيانات إلىprivate
MutableLiveData
. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوعLiveData
. - ضمن وحدة التحكُّم في واجهة المستخدم، غيِّر نوع البيانات إلى
private
MutableLiveData
. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوعLiveData
. - داخل الكائن
ViewModel
، يمكنك تغيير نوع البيانات إلىLiveData
. استخدِم موقعًا إلكترونيًا احتياطيًا لعرض البيانات للقراءة فقط من النوعLiveData
.
السؤال 2
يحدّث LiveData
وحدة التحكم في واجهة المستخدم (مثل جزء) إذا كانت وحدة التحكم في واجهة المستخدم بأي من الحالات التالية؟
- تم الاستئناف
- في الخلفية
- متوقفة مؤقتًا
- موقوف
السؤال 3
في نمط أداة الملاحظة LiveData
، ما هو العنصر القابل للملاحظة (ما يُلاحظه)؟
- طريقة أداة الملاحظة
- البيانات في عنصر
LiveData
- وحدة التحكُّم في واجهة المستخدم
- الكائن
ViewModel
بدء الدرس التالي:
وللحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، يُرجى الاطّلاع على الصفحة المقصودة لتطبيق الدروس التطبيقية حول الترميز Kotlin Fundamentals.