Android Kotlin Fundamentals 07.1: أساسيات إعادة التدوير

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

مقدمة

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

ما يجب معرفته

ويجب أن تكون على دراية بما يلي:

  • إنشاء واجهة مستخدم أساسية باستخدام نشاط وأجزاء وملفات شخصية.
  • التنقّل بين الأجزاء واستخدام safeArgs لتمرير البيانات بين الأجزاء
  • استخدام نماذج العرض وعرض مصانع النماذج والتحوّلات وLiveData ومراصدها.
  • إنشاء قاعدة بيانات Room، وإنشاء ميزة "DAO"، وتحديد الكيانات
  • استخدام الكوروتينات لمهام قاعدة البيانات والمهام الأخرى القديمة.

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

  • كيفية استخدام RecyclerView مع Adapter وViewHolder لعرض قائمة بالعناصر.

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

  • يمكنك تغيير تطبيق تتبع MySleepquality من الدرس السابق لاستخدام RecyclerView لعرض بيانات جودة النوم.

في هذا الدرس التطبيقي حول الترميز، يمكنك إنشاء جزء RecyclerView من تطبيق يتتبَّع جودة النوم. يستخدم التطبيق قاعدة بيانات Room لتخزين بيانات النوم بمرور الوقت.

يحتوي تطبيق تتبُّع النوم للمبتدئين على شاشتين، يمثّلهما جزءان، على النحو الموضّح في الشكل التالي.

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

يستخدم هذا التطبيق بنية مبسّطة مع وحدة تحكم واجهة المستخدم ViewModel وLiveData. ويستخدم التطبيق أيضًا قاعدة بيانات Room لجعل بيانات النوم مستمرة.

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

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

لإتاحة جميع حالات الاستخدام هذه، يوفّر نظام التشغيل Android أداة RecyclerView.

تتمثل أكبر ميزة في RecyclerView في أنها فعّالة جدًا للقوائم الكبيرة:

  • وفقًا للإعدادات التلقائية، يعمل RecyclerView فقط على معالجة العناصر المرئية على الشاشة أو رسمها فقط. على سبيل المثال، إذا تضمنت القائمة عناصر الآلاف، ولكن لا يمكن عرض سوى 10 عناصر، فلن يكفي RecyclerView سوى رسم 10 عناصر على الشاشة. عندما ينتقل المستخدم، يحدّد RecyclerView العناصر الجديدة التي يجب أن تظهر على الشاشة وتبذل جهودًا كافية لعرض تلك العناصر.
  • عندما يتم تمرير أحد العناصر خارج الشاشة، تتم إعادة تدوير مرات مشاهدة العنصر. وهذا يعني أنه تمت تعبئة العنصر بمحتوى جديد ينتقل إلى الشاشة. يوفّر سلوك RecyclerView هذا الكثير من وقت المعالجة ويساعد في تمرير القوائم بسلاسة.
  • عندما يتم تغيير عنصر، بدلاً من إعادة رسم القائمة بأكملها، يمكن لتطبيق RecyclerView تعديل هذا العنصر. تُعدّ هذه المكاسب الهائلة من الكفاءة عند عرض قوائم العناصر المعقدة!

في التسلسل المعروض أدناه، يمكنك ملاحظة أن ملفًا شخصيًا واحدًا تمت تعبئته بالبيانات، ABC. بعد انتقال هذا الملف الشخصي خارج الشاشة، يعيد RecyclerView استخدام الملف الشخصي للبيانات الجديدة XYZ.

نمط المحوّل

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

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

تنفيذ RecyclerView

لعرض بياناتك في RecyclerView، تحتاج إلى الأجزاء التالية:

  • البيانات المطلوب عرضها.
  • مثيل RecyclerView محدّد في ملف التنسيق ليكون بمثابة حاوية الملفات الشخصية.
  • تنسيق لعنصر واحد من البيانات.
    إذا كانت جميع عناصر القائمة تبدو متشابهة، يمكنك استخدام التنسيق نفسه لكل العناصر، ولكن هذا الإجراء ليس إلزاميًا. يجب إنشاء تنسيق العنصر بشكل منفصل عن التنسيق "الأجزاء&#39"، حتى يمكن إنشاء عرض عنصر واحد وملء البيانات.
  • مدير التنسيق.
    يدير مدير التنسيق مؤسسة (تنسيق) مكونات واجهة المستخدم في ملف شخصي.
  • حامل عرض.
    يمتد حامل العرض الفئة ViewHolder. يحتوي هذا الملف على معلومات الملف الشخصي لعرض عنصر واحد من تنسيق العنصر. ويضيف الاطِّلاع أيضًا المعلومات التي يستخدمها RecyclerView لتحريك المشاهدات في جميع أنحاء الشاشة بكفاءة.
  • محوِّل.
    يربط المحوِّل بياناتك بـ RecyclerView. وهي تعدّل البيانات بحيث يمكن عرضها في ViewHolder. يستخدم RecyclerView المحوِّل لمعرفة كيفية عرض البيانات على الشاشة.

في هذه المهمة، يمكنك إضافة RecyclerView إلى ملف التنسيق وإعداد Adapter لعرض بيانات النوم على RecyclerView.

الخطوة 1: إضافة RecyclerView باستخدام LayoutManager

في هذه الخطوة، يمكنك استبدال ScrollView بـ RecyclerView في الملف fragment_sleep_tracker.xml.

  1. نزِّل تطبيق RecyclerViewFundamentals-Starter من GitHub.
  2. إنشاء التطبيق وتشغيله. لاحظ كيفية عرض البيانات كنص بسيط.
  3. افتح ملف التنسيق fragment_sleep_tracker.xml في علامة التبويب التصميم في Android Studio.
  4. في جزء شجرة المكوّنات، احذف ScrollView. يؤدي هذا الإجراء أيضًا إلى حذف TextView داخل ScrollView.
  5. في لوحة لوحة الألوان، انتقِل بين عناصر قائمة المكوّنات على يمين الصفحة للعثور على الحاويات، ثم اختَرها.
  6. اسحب RecyclerView من لوحة لوحة الألوان إلى جزء شجرة المكوّنات. ضَع RecyclerView داخل ConstraintLayout.

  1. في حال فتح مربّع حوار يسألك عمّا إذا كنت تريد إضافة اعتمادية، انقر على حسنًا للسماح لـ "استوديو Android" بإضافة التبعية recyclerview إلى ملف Gradle. قد يستغرق الأمر بضع ثوانٍ، وتتم بعد ذلك مزامنة تطبيقك.

  1. افتح ملف الوحدة build.gradle، وانتقِل إلى النهاية، ودوِّن الاعتمادية الجديدة التي تبدو مشابهة للرمز أدناه:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
  1. التبديل مرة أخرى إلى fragment_sleep_tracker.xml.
  2. في علامة التبويب النص، ابحث عن رمز RecyclerView المعروض أدناه:
<androidx.recyclerview.widget.RecyclerView
   android:layout_width="match_parent"
   android:layout_height="match_parent" />
  1. امنح RecyclerView id من sleep_list.
android:id="@+id/sleep_list"
  1. يجب ضبط RecyclerView بحيث تشغل المساحة المتبقية من الشاشة داخل ConstraintLayout. للقيام بذلك، ضع الجزء العلوي من RecyclerView في الزر ابدأ، ومن الأسفل إلى الزر محو، وعلى كل جانب إلى الزر الرئيسي. اضبط عرض وارتفاع التنسيق على 0 بكسل مستقلة الكثافة في "محرّر التنسيق" أو في XML، باستخدام الرمز التالي:
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@+id/clear_button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/stop_button"
  1. أضِف مدير تنسيق إلى XML في RecyclerView. يحتاج كل RecyclerView إلى مدير تنسيق يخبره بكيفية وضع العناصر في القائمة. يوفر نظام التشغيل Android LinearLayoutManager، والذي يحدد العناصر تلقائيًا في قائمة عمودية من الصفوف ذات العرض الكامل.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
  1. بدِّل إلى علامة التبويب تصميم ولاحظ أنّ القيود الإضافية تسبّبت في توسيع RecyclerView لملء المساحة المتوفّرة.

الخطوة 2: إنشاء تنسيق عنصر القائمة وحامل عرض النص

RecyclerView عبارة عن حاوية فقط. في هذه الخطوة، يمكنك إنشاء التنسيق والبنية الأساسية للسلع ليتم عرضها داخل RecyclerView.

وللوصول إلى RecyclerView في أسرع وقت ممكن، تستخدم أولاً عنصر قائمة بسيطًا يعرِض جودة النوم على شكل رقم فقط. ولتحقيق هذا الهدف، تحتاج إلى حامل عرض، TextItemViewHolder. تحتاج أيضًا إلى ملف شخصي، TextView، للبيانات. (ستتعرَّف في خطوة لاحقة على المزيد من المعلومات عن أصحاب وضع العرض وكيفية وضع جميع بيانات النوم).

  1. أنشئ ملف تنسيق باسم text_item_view.xml. لا يهم ما تستخدمه كعنصر جذر نظرًا لأنك ستحل محل رمز النموذج.
  2. في text_item_view.xml، احذف جميع الرموز المحددة.
  3. أضِف TextView مع مساحة متروكة 16dp في البداية والنهاية، وبحجم نص 24sp. يجب أن يتطابق العرض مع العنصر الرئيسي، ويحيط الارتفاع بالمحتوى. بما أن هذا العرض يتم عرضه داخل RecyclerView، فلن تحتاج إلى وضع العرض داخل ViewGroup.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:textSize="24sp"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:layout_width="match_parent"       
    android:layout_height="wrap_content" />
  1. فتح Util.kt انتقل إلى النهاية وأضِف التعريف الذي يظهر أدناه، والذي يؤدي إلى إنشاء صف TextItemViewHolder. ضع الرمز في أسفل الملف، بعد آخر قوس إغلاق. يظهر الرمز باللغة Util.kt لأن صاحب الملف الشخصي هذا مؤقت، ويتم استبداله لاحقًا.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
  1. في حال مطالبتك بذلك، يمكنك استيراد android.widget.TextView وandroidx.recyclerview.widget.RecyclerView.

الخطوة 3: إنشاء SleepNightAdaptiveer

تتمثل المهمة الأساسية لتنفيذ RecyclerView في إنشاء المحوِّل. لديك طريقة عرض بسيطة لعرض العنصر، وتصميم لكل عنصر. يمكنك الآن إنشاء محوّل. ينشئ المحوِّل حامل عرض ويملأ البيانات لعرضها في RecyclerView.

  1. في حزمة sleeptracker، أنشِئ فئة Kotlin جديدة باسم SleepNightAdapter.
  2. توسيع صف SleepNightAdapter بنسبة RecyclerView.Adapter تُسمى الفئة SleepNightAdapter لأنها تتكيّف مع عنصر SleepNight بحيث يمكن لـ RecyclerView استخدامه. يجب أن يعرف المحوّل حامل العرض المطلوب استخدامه، لذلك يجب تمرير TextItemViewHolder. عند استيراد المكوّنات اللازمة عند الطلب، ستظهر لك رسالة خطأ لأنّ هناك طرقًا إلزامية لتنفيذها.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
  1. في المستوى الأعلى من SleepNightAdapter، أنشِئ متغيّر listOf SleepNight للاحتفاظ بالبيانات.
var data =  listOf<SleepNight>()
  1. خلال SleepNightAdapter، يمكنك إلغاء getItemCount() لعرض حجم قائمة ليالي النوم في data. يحتاج RecyclerView إلى معرفة عدد العناصر التي يعرضها المحوّل لعرضها، وذلك من خلال طلب getItemCount().
override fun getItemCount() = data.size
  1. في SleepNightAdapter، ألغِ دالة onBindViewHolder()، كما هو موضّح أدناه.

    يتم استدعاء الدالة onBindViewHolder() من قِبل RecyclerView لعرض البيانات لعنصر قائمة واحد في الموضع المحدد. وبالتالي، تستخدم الطريقة onBindViewHolder() وسيطتين: حامل عرض وموضع للبيانات لربطه. في هذا التطبيق، يكون حامل البطاقة هو TextItemViewHolder، والموضع هو الموضع في القائمة.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
  1. داخل onBindViewHolder()، يمكنك إنشاء متغيّر لعنصر واحد في موضع معيّن في البيانات.
 val item = data[position]
  1. تشتمل ViewHolder التي أنشأتها على خاصية تُسمى textView. داخل onBindViewHolder()، اضبط text من textView على رقم جودة النوم. يعرض هذا الرمز قائمة بالأرقام فقط، ولكن هذا المثال البسيط يتيح لك معرفة كيفية نقل المحوّل للبيانات إلى صاحب العرض وعلى الشاشة.
holder.textView.text = item.sleepQuality.toString()
  1. في SleepNightAdapter، يجب تنفيذ السياسة onCreateViewHolder() وتنفيذها، والتي يتم استدعاءها عندما تحتاج RecyclerView إلى مالك عرض لتمثيل عنصر.

    تعتمد هذه الدالة على معلَمتَين وتعرض ViewHolder. وتكون المعلَمة parent، وهي مجموعة الملف الشخصي التي تحتفظ بحامل الملف الشخصي، دائمًا هي RecyclerView. يتم استخدام المعلَمة viewType عند توفّر طرق عرض متعددة في RecyclerView نفسه. على سبيل المثال، إذا أضفت قائمة بالمشاهدات النصية والصور والفيديو في RecyclerView نفسها، ستحتاج الدالة onCreateViewHolder() إلى معرفة نوع الملف الشخصي الذي يجب استخدامه.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
  1. في onCreateViewHolder()، أنشئ مثيلاً لـ LayoutInflater.

    تستخدم أداة تعديل التنسيق كيفية إنشاء طرق عرض من تنسيقات XML. يتضمن context معلومات حول كيفية تضخيم العرض بشكل صحيح. في محوّل عرض إعادة التدوير، يتم دائمًا تمرير المحتوى في سياق مجموعة الملفات الشخصية parent، وهي RecyclerView.
val layoutInflater = LayoutInflater.from(parent.context)
  1. في onCreateViewHolder()، أنشئ view بطلب من layoutinflater لتضخيمه.

    مرر في تنسيق XML للملف الشخصي، وملف parent الشخصي للملف الشخصي. الوسيطة المنطقية هي attachToRoot. يجب أن تكون هذه الوسيطة false، لأن RecyclerView يضيف هذا العنصر إلى العرض الهرمي لك عندما يحين وقته.
val view = layoutInflater
       .inflate(R.layout.text_item_view, parent, false) as TextView
  1. في غضون onCreateViewHolder()، يمكنك إرجاع TextItemViewHolder باستخدام view.
return TextItemViewHolder(view)
  1. يحتاج المحوّل إلى إبلاغ RecyclerView عند تغيّر data، لأنّ RecyclerView لا يعرف أي بيانات عن البيانات. ولا يمكنه معرفة سوى حوامل الملفات الشخصية التي يقدِّمها المحوِّل له.

    لإبلاغ RecyclerView عند تغيُّر البيانات التي تعرضها هذه الميزة، أضِف أداة ضبط مخصَّصة إلى المتغيّر data الذي أعلى الصف SleepNightAdapter. في واجهة برمجة التطبيقات، أعط data قيمة جديدة، ثم اتصل بـ notifyDataSetChanged() لتشغيل إعادة رسم القائمة بالبيانات الجديدة.
var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

الخطوة 4: إبلاغ RecyclerView حول المحوّل

يحتاج RecyclerView إلى معرفة المزيد عن المحوّل لاستخدامه في الحصول على حواسن عرض.

  1. فتح SleepTrackerFragment.kt
  2. في onCreateview()، أنشئ محوّلًا. ضع هذا الرمز بعد إنشاء نموذج ViewModel وقبل عبارة return.
val adapter = SleepNightAdapter()
  1. ربط adapter بـ RecyclerView.
binding.sleepList.adapter = adapter
  1. عليك تنظيف مشروعك وإعادة إنشائه لتعديل الكائن binding.

    إذا استمر ظهور الأخطاء حول binding.sleepList أو binding.FragmentSleepTrackerBinding، يُرجى إلغاء ذاكرة التخزين المؤقت وإعادة التشغيل. (اختَر الملف &gt؛ إلغاء صلاحية ذاكرات التخزين المؤقت / إعادة التشغيل.)

    في حال تشغيل التطبيق الآن، ليست هناك أخطاء، ولكن لن تظهر لك أي بيانات تظهر عند النقر على بدء ثم إيقاف.

الخطوة 5: الحصول على البيانات في المحوِّل

حتى الآن، لديك محول، فضلاً عن طريقة للحصول على البيانات من المحوّل إلى RecyclerView. عليك الآن الحصول على البيانات على المحوّل من جهاز ViewModel.

  1. فتح SleepTrackerViewModel
  2. ابحث عن المتغيّر nights الذي يخزّن جميع ليالي النوم، وهي البيانات التي يجب عرضها. يتم ضبط المتغير nights من خلال استدعاء getAllNights() في قاعدة البيانات.
  3. عليك إزالة private من nights، لأنك ستنشئ مراقبًا يحتاج إلى الوصول إلى هذا المتغيّر. يجب أن يظهر البيان كما يلي:
val nights = database.getAllNights()
  1. في حزمة database، افتح SleepDatabaseDao.
  2. ابحث عن الدالة getAllNights(). لاحظ أن هذه الدالة تعرض قائمة قيم SleepNight على النحو LiveData. وهذا يعني أن المتغير nights يحتوي على LiveData الذي يتم تحديثه بواسطة Room، ويمكنك ملاحظة nights لمعرفة وقت تغييره.
  3. فتح SleepTrackerFragment
  4. في onCreateView()، أسفل إنشاء adapter، يمكنك إنشاء مراقب على المتغير nights.

    من خلال تقديم الجزء viewLifecycleOwner الخاص بمالك اللعبة، يمكنك التأكّد من أن جهاز الرصد هذا نشط فقط عندما يكون RecyclerView على الشاشة.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   })
  1. داخل المراقب، كلما تحصل على قيمة غير فارغة (لـ nights)، عيّن القيمة إلى المحوّل data. هذا هو الرمز الكامل للمراقب وإعداد البيانات:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.data = it
   }
})
  1. إنشاء الرمز وتشغيله

    ستظهر لك أرقام جودة النوم كقائمة في حال عمل المحوّل. تعرض لقطة الشاشة على اليمين القيمة -1 بعد النقر على بدء. تعرض لقطة الشاشة على اليسار الرقم الذي تم تعديله لجودة النوم بعد النقر على إيقاف واختيار تقييم الجودة.

الخطوة 6: استكشاف كيفية إعادة تدوير منصات العرض

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

ونظرًا لإعادة تدوير حوامل الملفات الشخصية هذه، تأكد من ضبط onBindViewHolder() أو إعادة ضبط أي عمليات تخصيص ربما ضبطتها العناصر السابقة على إحدى منصات العرض.

على سبيل المثال، يمكنك ضبط لون النص على اللون الأحمر في حوامل العرض التي تحصل على تقييمات جودة أقل من أو تساوي 1 وتمثل نومًا سيئًا.

  1. في الصف SleepNightAdapter، أضِف الرمز التالي إلى نهاية onBindViewHolder().
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
}
  1. شغِّل التطبيق.
  2. أضِف بعض البيانات المنخفضة جودة النوم، وسيظهر الرقم باللون الأحمر.
  3. أضِف تقييمات عالية لجودة النوم إلى أن يظهر لك رقم أحمر مرتفع على الشاشة.

    عندما يُعيد RecyclerView استخدام أصحاب الشاشات، يُعيد استخدام أحد أصحاب الشاشات الحمراء للحصول على تقييم عالي الجودة. يتم عرض التقييم العالي عن طريق الخطأ باللون الأحمر.

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

    في كلتا الحالتين، سيستخدم صاحب العرض لون النص الصحيح لكل سلعة.
if (item.sleepQuality <= 1) {
   holder.textView.setTextColor(Color.RED) // red
} else {
   // reset
   holder.textView.setTextColor(Color.BLACK) // black
}
  1. شغِّل التطبيق، ويجب أن تحمل الأرقام دائمًا اللون الصحيح.

تهانينا. أصبح لديك RecyclerView أساسي يعمل بشكل كامل.

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

يؤدي استخدام ViewHolder البسيطة التي أضفتها إلى Util.kt إلى التفاف TextView في TextItemViewHolder.

class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)

لماذا لا يستخدم RecyclerView TextView مباشرةً فقط؟ يوفّر سطر الرمز هذا الكثير من الوظائف. يوضِّح ViewHolder عرضًا للسلع والبيانات الوصفية حول مكانه ضمن RecyclerView. يعتمد RecyclerView على هذه الوظيفة لضبط وضع الملف الشخصي بشكل صحيح أثناء تمرير القائمة، ولتنفيذ إجراءات مثيرة للاهتمام مثل عرض الصور المتحركة عند إضافة عناصر أو إزالتها في Adapter.

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

الخطوة 1: إنشاء تنسيق العنصر

في هذه الخطوة، يمكنك إنشاء ملف التنسيق لعنصر واحد. يتكون التنسيق من ConstraintLayout مع ImageView لجودة النوم وTextView لمدة النوم وTextView للجودة كنص. نظرًا لأنك أجريت تخطيطات من قبل، انسخ رمز XML المقدم والصقه.

  1. أنشئ ملفًا جديدًا لمورد التنسيق وأطلق عليه الاسم list_item_sleep_night.
  2. استبدل الرمز بالكامل في الملف بالرمز أدناه. بعد ذلك، يمكنك التعرُّف على التنسيق الذي أنشأته للتو.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="wrap_content">

   <ImageView
       android:id="@+id/quality_image"
       android:layout_width="@dimen/icon_size"
       android:layout_height="60dp"
       android:layout_marginStart="16dp"
       android:layout_marginTop="8dp"
       android:layout_marginBottom="8dp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       tools:srcCompat="@drawable/ic_sleep_5" />

   <TextView
       android:id="@+id/sleep_length"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginStart="8dp"
       android:layout_marginTop="8dp"
       android:layout_marginEnd="16dp"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toEndOf="@+id/quality_image"
       app:layout_constraintTop_toTopOf="@+id/quality_image"
       tools:text="Wednesday" />

   <TextView
       android:id="@+id/quality_string"
       android:layout_width="0dp"
       android:layout_height="20dp"
       android:layout_marginTop="8dp"
       app:layout_constraintEnd_toEndOf="@+id/sleep_length"
       app:layout_constraintHorizontal_bias="0.0"
       app:layout_constraintStart_toStartOf="@+id/sleep_length"
       app:layout_constraintTop_toBottomOf="@+id/sleep_length"
       tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>
  1. انتقِل إلى علامة التبويب التصميم في "استوديو Android". في عرض التصميم، يبدو التنسيق على النحو التالي: لقطة الشاشة على اليمين أدناه. في طريقة العرض المخطط، تظهر لقطة الشاشة على اليسار.

الخطوة 2: إنشاء حامل عرض

  1. فتح SleepNightAdapter.kt
  2. أنشئ صفًا داخل SleepNightAdapter باسم ViewHolder واجعله يمتد إلى RecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
  1. في ViewHolder، يمكنك الحصول على مراجع للمشاهدات. تحتاج إلى مرجع للملفات الشخصية التي سيتم تعديلها في ViewHolder. في كل مرة تربط فيها ViewHolder، عليك الوصول إلى الصورتين وعرض النص. (يمكنك تحويل هذا الرمز لاستخدام ربط البيانات لاحقًا.)
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)

الخطوة 3: استخدام حامل العرض في SleepNightAdaptiveer

  1. في تعريف SleepNightAdapter، بدلاً من TextItemViewHolder، استخدم SleepNightAdapter.ViewHolder الذي أنشأته للتو.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {

تعديل onCreateViewHolder():

  1. يجب تغيير توقيع onCreateViewHolder() لعرض ViewHolder.
  2. يمكنك تغيير أداة تضخيم التنسيق لاستخدام مورد التنسيق الصحيح، list_item_sleep_night.
  3. إزالة البث من TextView
  4. بدلاً من عرض TextItemViewHolder، يمكنك عرض ViewHolder.

    في ما يلي دالة onCreateViewHolder() المعدَّلة المكتملة:
    override fun onCreateViewHolder(
            parent: ViewGroup, viewType: Int): ViewHolder {
        val layoutInflater = 
            LayoutInflater.from(parent.context)
        val view = layoutInflater
                .inflate(R.layout.list_item_sleep_night, 
                         parent, false)
        return ViewHolder(view)
    }

تعديل onBindViewHolder():

  1. غيّر توقيع onBindViewHolder() بحيث تكون المعلمة holder ViewHolder بدلاً من TextItemViewHolder.
  2. داخل onBindViewHolder()، عليك حذف جميع الرموز، باستثناء تعريف item.
  3. حدِّد val res الذي يشتمل على مرجع إلى resources لهذا الملف الشخصي.
val res = holder.itemView.context.resources
  1. ضبط نص عرض النص sleepLength على المدة. انسخ الرمز أدناه، والذي يستدعي وظيفة تنسيق متوفّرة مع رمز إجراء التفعيل.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
  1. يؤدي ذلك إلى حدوث خطأ، حيث يجب تحديد convertDurationToFormatted(). افتح Util.kt وألغِ التعليق على الرمز وعمليات الاستيراد المرتبطة به. (اختَر الرمز &gt والتعليق مع تعليقات السطر.)
  2. ارجع إلى onBindViewHolder()، واستخدِم convertNumericQualityToString() لضبط الجودة.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
  1. قد تحتاج إلى استيراد هذه الوظائف يدويًا.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
  1. اضبط الرمز الصحيح للجودة. يتوفّر رمز ic_sleep_active الجديد لك في رمز إجراء التفعيل.
holder.qualityImage.setImageResource(when (item.sleepQuality) {
   0 -> R.drawable.ic_sleep_0
   1 -> R.drawable.ic_sleep_1
   2 -> R.drawable.ic_sleep_2
   3 -> R.drawable.ic_sleep_3
   4 -> R.drawable.ic_sleep_4
   5 -> R.drawable.ic_sleep_5
   else -> R.drawable.ic_sleep_active
})
  1. في ما يلي دالة onBindViewHolder() المعدّلة التي تم الانتهاء من إعدادها، وستُحدِّد جميع بيانات ViewHolder:
   override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = data[position]
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. شغِّل تطبيقك. ومن المفترض أن تبدو شاشتك مثل لقطة الشاشة أدناه، حيث تعرض رمز جودة النوم، إلى جانب نص لمدة النوم وجودة النوم.

اكتملت معاملة RecyclerView الآن. لقد تعلّمت كيفية تطبيق آلية Adapter وViewHolder، وجمعتهما لعرض قائمة تتضمّن RecyclerView Adapter.

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

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

الخطوة 1: ReFact onBindViewholder()

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

  1. في SleepNightAdapter، في onBindViewHolder()، اختَر كل شيء باستثناء العبارة لتعريف المتغير item.
  2. انقر بزر الماوس الأيمن، ثم اختَر العامل الإضافي &gt؛ استخراج &gt؛ الدالة.
  3. أدخِل اسمًا للدالة bind واقبل المعلّمات المقترحة. انقر على حسنًا.

    تم وضع دالة bind() أسفل onBindViewHolder().
    private fun bind(holder: ViewHolder, item: SleepNight) {
        val res = holder.itemView.context.resources
        holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
        holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
        holder.qualityImage.setImageResource(when (item.sleepQuality) {
            0 -> R.drawable.ic_sleep_0
            1 -> R.drawable.ic_sleep_1
            2 -> R.drawable.ic_sleep_2
            3 -> R.drawable.ic_sleep_3
            4 -> R.drawable.ic_sleep_4
            5 -> R.drawable.ic_sleep_5
            else -> R.drawable.ic_sleep_active
        })
    }
  1. ضع المؤشر على الكلمة holder من المعلمة holder التي تخص bind(). اضغط على Alt+Enter (Option+Enter على جهاز Mac) لفتح قائمة الأهداف. اختَر تحويل المعلّمة إلى المُستلِم لتحويل هذه الدالة إلى وظيفة إضافة لها التوقيع التالي:
private fun ViewHolder.bind(item: SleepNight) {...}
  1. اقطع دالة bind() والصقها في ViewHolder.
  2. عرض bind() بشكل علني
  3. يمكنك استيراد bind() إلى المحوّل، إذا لزم الأمر.
  4. لأن ذلك الآن في ViewHolder، يمكنك إزالة جزء ViewHolder من التوقيع. إليك الرمز النهائي للدالة bind() في الفئة ViewHolder.
fun bind(item: SleepNight) {
   val res = itemView.context.resources
   sleepLength.text = convertDurationToFormatted(
           item.startTimeMilli, item.endTimeMilli, res)
   quality.text = convertNumericQualityToString(
           item.sleepQuality, res)
   qualityImage.setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

الخطوة 2: إعادة إنشاء عامل التشغيل onCreateViewholder

وتؤدي طريقة onCreateViewHolder() في المحوّل حاليًا إلى تضخيم العرض من مورد التنسيق ViewHolder. ومع ذلك، ليس للتضخم أي علاقة بالمحوّل، وكل ما يتعلق بـ ViewHolder. من المفترض أن يحدث التضخم في ViewHolder.

  1. في onCreateViewHolder()، اختَر جميع الرموز في نص الدالة.
  2. انقر بزر الماوس الأيمن، ثم اختَر العامل الإضافي &gt؛ استخراج &gt؛ الدالة.
  3. أدخِل اسمًا للدالة from واقبل المعلّمات المقترحة. انقر على حسنًا.
  4. ضَع المؤشر على اسم الدالة from. اضغط على Alt+Enter (Option+Enter على جهاز Mac) لفتح قائمة الأهداف.
  5. اختَر الانتقال إلى العنصر المصاحب. يجب أن تكون الدالة from() في كائن مصاحب حتى يمكن استدعاؤها في الفئة ViewHolder، وليس عند استدعاء مثيل ViewHolder.
  6. انقل العنصر companion إلى الفئة ViewHolder.
  7. عرض from() بشكل علني
  8. في onCreateViewHolder()، غيِّر عبارة return لعرض نتيجة الاتصال بـ from() في صف ViewHolder.

    يجب أن تكون طريقتك في onCreateViewHolder() وfrom() المكتملة مثل الرمز أدناه، ويجب أن يتم إنشاء الرمز وتشغيله بدون أخطاء.
    override fun onCreateViewHolder(parent: ViewGroup, viewType: 
Int): ViewHolder {
        return ViewHolder.from(parent)
    }
companion object {
   fun from(parent: ViewGroup): ViewHolder {
       val layoutInflater = LayoutInflater.from(parent.context)
       val view = layoutInflater
               .inflate(R.layout.list_item_sleep_night, parent, false)
       return ViewHolder(view)
   }
}
  1. غيِّر توقيع الفئة ViewHolder بحيث تكون طريقة الإنشاء خاصة. بما أنّ from() أصبحت الآن طريقة تعرِض مثيل ViewHolder جديدًا، لم يعد هناك أي سبب يستدعي أن يتصل أي شخص بمنشئ ViewHolder.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
  1. شغِّل التطبيق. يجب أن ينشئ تطبيقك التطبيق ويشغِّله كما في السابق، وهي النتيجة المطلوبة بعد إعادة هيكلة النظام.

مشروع "استوديو Android": RecyclerViewFundamentals

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

لعرض بياناتك في RecyclerView، تحتاج إلى الأجزاء التالية:

  • RecyclerView
    لإنشاء مثيل من RecyclerView، حدِّد عنصر <RecyclerView> في ملف التنسيق.
  • التنسيق مدير
    تستخدم RecyclerView LayoutManager لإضافة تنظيم العناصر في RecyclerView، مثل وضعها في شبكة أو في قائمة خطية.

    في <RecyclerView> في ملف التنسيق، يمكنك ضبط السمة app:layoutManager على مدير التنسيق (مثل LinearLayoutManager أو GridLayoutManager).

    ويمكنك أيضًا ضبط LayoutManager لعنصر RecyclerView آليًا. (وتتناول هذه التقنية درسًا تطبيقيًا لاحقًا حول الترميز.)
  • تنسيق لكل عنصر
    إنشاء تنسيق لعنصر واحد من البيانات في ملف تنسيق XML
  • المحوّل
    أنشئ محوّلًا يُعدّ البيانات وكيفية عرضها في ViewHolder. اربط المحوّل بجهاز RecyclerView.

    عندما يتم تشغيل RecyclerView، سيتم استخدام المحوّل لمعرفة كيفية عرض البيانات على الشاشة.

    يتطلب منك المحوّل تنفيذ الطرق التالية:
    getItemCount() لإرجاع عدد العناصر.
    onCreateViewHolder() لإرجاع ViewHolder لعنصر في القائمة.
    onBindViewHolder() لتكييف البيانات مع طرق العرض لعنصر في القائمة.

  • Viewholder
    يحتوي ViewHolder على معلومات الملف الشخصي لعرض عنصر واحد من تنسيق العنصر.
  • تعمل الطريقة onBindViewHolder() في المحوّل على تعديل البيانات وفقًا للعروض. وتتجاوز هذه الطريقة دائمًا. عادةً ما يعمل onBindViewHolder() على تضخيم تنسيق عنصر ما، ووضع البيانات في طرق العرض في التنسيق.
  • ولأن RecyclerView لا تعرف أي شيء عن البيانات، يحتاج Adapter إلى إعلام RecyclerView بوقت حدوث هذه البيانات. يمكنك استخدام notifyDataSetChanged() لإعلام Adapter بأنّ البيانات قد تغيّرت.

دورة Udacity:

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

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

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

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

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

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

السؤال 1

كيف يتم عرض العناصر في RecyclerView؟ يُرجى اختيار كل الإجابات المناسبة.

▢ يعرض العناصر في قائمة أو شبكة.

▢ بالتمرير أفقيًا أو أفقيًا.

▢ التمرير بشكل قطري على الأجهزة الأكبر حجمًا مثل الأجهزة اللوحية

▢ يتيح التنسيقات المخصصة عندما لا تكون قائمة أو شبكة كافية لحالة الاستخدام.

السؤال 2

ما هي مزايا استخدام RecyclerView؟ يُرجى اختيار كل الإجابات المناسبة.

▢ عرض القوائم الكبيرة بكفاءة

▢ يتم تحديث البيانات تلقائيًا.

▢ لتقليل الحاجة إلى عمليات إعادة التحميل عند تعديل عنصر أو حذفه أو إضافته إلى القائمة.

▢ إعادة استخدام العرض الذي ينتقل خارج الشاشة لعرض العنصر التالي الذي يتم تمريره على الشاشة.

السؤال 3

ما بعض أسباب استخدام المحوّلات؟ يُرجى اختيار كل الإجابات المناسبة.

▢ يُسهِّل فصل المخاوف تغيير الرمز واختباره.

RecyclerView غير مدرَج في البيانات التي يتم عرضها.

▢ لا داعي للقلق بشأن طبقات معالجة البيانات في كيفية عرض البيانات.

▢ سيتم تشغيل التطبيق بشكل أسرع.

السؤال 4

أيّ مما يلي ينطبق على ViewHolder؟ يُرجى اختيار كل الإجابات المناسبة.

▢ يتم تحديد التنسيق ViewHolder في ملفات تنسيق XML.

▢ هناك ViewHolder واحدة لكل وحدة بيانات في مجموعة البيانات.

▢ يمكنك إضافة أكثر من ViewHolder إلى RecyclerView.

▢ تربط Adapter البيانات بـ ViewHolder.

بدء الدرس التالي: 7.2: DiffUtil and dataربط باستخدام RecyclerView