يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات 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
محدّد في ملف التنسيق ليكون بمثابة حاوية الملفات الشخصية. - تنسيق لعنصر واحد من البيانات.
إذا كانت جميع عناصر القائمة تبدو متشابهة، يمكنك استخدام التنسيق نفسه لكل العناصر، ولكن هذا الإجراء ليس إلزاميًا. يجب إنشاء تنسيق العنصر بشكل منفصل عن التنسيق "الأجزاء'"، حتى يمكن إنشاء عرض عنصر واحد وملء البيانات. - مدير التنسيق.
يدير مدير التنسيق مؤسسة (تنسيق) مكونات واجهة المستخدم في ملف شخصي. - حامل عرض.
يمتد حامل العرض الفئةViewHolder
. يحتوي هذا الملف على معلومات الملف الشخصي لعرض عنصر واحد من تنسيق العنصر. ويضيف الاطِّلاع أيضًا المعلومات التي يستخدمهاRecyclerView
لتحريك المشاهدات في جميع أنحاء الشاشة بكفاءة. - محوِّل.
يربط المحوِّل بياناتك بـRecyclerView
. وهي تعدّل البيانات بحيث يمكن عرضها فيViewHolder
. يستخدمRecyclerView
المحوِّل لمعرفة كيفية عرض البيانات على الشاشة.
في هذه المهمة، يمكنك إضافة RecyclerView
إلى ملف التنسيق وإعداد Adapter
لعرض بيانات النوم على RecyclerView
.
الخطوة 1: إضافة RecyclerView باستخدام LayoutManager
في هذه الخطوة، يمكنك استبدال ScrollView
بـ RecyclerView
في الملف fragment_sleep_tracker.xml
.
- نزِّل تطبيق RecyclerViewFundamentals-Starter من GitHub.
- إنشاء التطبيق وتشغيله. لاحظ كيفية عرض البيانات كنص بسيط.
- افتح ملف التنسيق
fragment_sleep_tracker.xml
في علامة التبويب التصميم في Android Studio. - في جزء شجرة المكوّنات، احذف
ScrollView
. يؤدي هذا الإجراء أيضًا إلى حذفTextView
داخلScrollView
. - في لوحة لوحة الألوان، انتقِل بين عناصر قائمة المكوّنات على يمين الصفحة للعثور على الحاويات، ثم اختَرها.
- اسحب
RecyclerView
من لوحة لوحة الألوان إلى جزء شجرة المكوّنات. ضَعRecyclerView
داخلConstraintLayout
.
- في حال فتح مربّع حوار يسألك عمّا إذا كنت تريد إضافة اعتمادية، انقر على حسنًا للسماح لـ "استوديو Android" بإضافة التبعية
recyclerview
إلى ملف Gradle. قد يستغرق الأمر بضع ثوانٍ، وتتم بعد ذلك مزامنة تطبيقك.
- افتح ملف الوحدة
build.gradle
، وانتقِل إلى النهاية، ودوِّن الاعتمادية الجديدة التي تبدو مشابهة للرمز أدناه:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- التبديل مرة أخرى إلى
fragment_sleep_tracker.xml
. - في علامة التبويب النص، ابحث عن رمز
RecyclerView
المعروض أدناه:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />
- امنح
RecyclerView
id
منsleep_list
.
android:id="@+id/sleep_list"
- يجب ضبط
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"
- أضِف مدير تنسيق إلى XML في
RecyclerView
. يحتاج كلRecyclerView
إلى مدير تنسيق يخبره بكيفية وضع العناصر في القائمة. يوفر نظام التشغيل AndroidLinearLayoutManager
، والذي يحدد العناصر تلقائيًا في قائمة عمودية من الصفوف ذات العرض الكامل.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- بدِّل إلى علامة التبويب تصميم ولاحظ أنّ القيود الإضافية تسبّبت في توسيع
RecyclerView
لملء المساحة المتوفّرة.
الخطوة 2: إنشاء تنسيق عنصر القائمة وحامل عرض النص
RecyclerView
عبارة عن حاوية فقط. في هذه الخطوة، يمكنك إنشاء التنسيق والبنية الأساسية للسلع ليتم عرضها داخل RecyclerView
.
وللوصول إلى RecyclerView
في أسرع وقت ممكن، تستخدم أولاً عنصر قائمة بسيطًا يعرِض جودة النوم على شكل رقم فقط. ولتحقيق هذا الهدف، تحتاج إلى حامل عرض، TextItemViewHolder
. تحتاج أيضًا إلى ملف شخصي، TextView
، للبيانات. (ستتعرَّف في خطوة لاحقة على المزيد من المعلومات عن أصحاب وضع العرض وكيفية وضع جميع بيانات النوم).
- أنشئ ملف تنسيق باسم
text_item_view.xml
. لا يهم ما تستخدمه كعنصر جذر نظرًا لأنك ستحل محل رمز النموذج. - في
text_item_view.xml
، احذف جميع الرموز المحددة. - أضِف
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" />
- فتح
Util.kt
انتقل إلى النهاية وأضِف التعريف الذي يظهر أدناه، والذي يؤدي إلى إنشاء صفTextItemViewHolder
. ضع الرمز في أسفل الملف، بعد آخر قوس إغلاق. يظهر الرمز باللغةUtil.kt
لأن صاحب الملف الشخصي هذا مؤقت، ويتم استبداله لاحقًا.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
- في حال مطالبتك بذلك، يمكنك استيراد
android.widget.TextView
وandroidx.recyclerview.widget.RecyclerView
.
الخطوة 3: إنشاء SleepNightAdaptiveer
تتمثل المهمة الأساسية لتنفيذ RecyclerView
في إنشاء المحوِّل. لديك طريقة عرض بسيطة لعرض العنصر، وتصميم لكل عنصر. يمكنك الآن إنشاء محوّل. ينشئ المحوِّل حامل عرض ويملأ البيانات لعرضها في RecyclerView
.
- في حزمة
sleeptracker
، أنشِئ فئة Kotlin جديدة باسمSleepNightAdapter
. - توسيع صف
SleepNightAdapter
بنسبةRecyclerView.Adapter
تُسمى الفئةSleepNightAdapter
لأنها تتكيّف مع عنصرSleepNight
بحيث يمكن لـRecyclerView
استخدامه. يجب أن يعرف المحوّل حامل العرض المطلوب استخدامه، لذلك يجب تمريرTextItemViewHolder
. عند استيراد المكوّنات اللازمة عند الطلب، ستظهر لك رسالة خطأ لأنّ هناك طرقًا إلزامية لتنفيذها.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}
- في المستوى الأعلى من
SleepNightAdapter
، أنشِئ متغيّرlistOf
SleepNight
للاحتفاظ بالبيانات.
var data = listOf<SleepNight>()
- خلال
SleepNightAdapter
، يمكنك إلغاءgetItemCount()
لعرض حجم قائمة ليالي النوم فيdata
. يحتاجRecyclerView
إلى معرفة عدد العناصر التي يعرضها المحوّل لعرضها، وذلك من خلال طلبgetItemCount()
.
override fun getItemCount() = data.size
- في
SleepNightAdapter
، ألغِ دالةonBindViewHolder()
، كما هو موضّح أدناه.
يتم استدعاء الدالةonBindViewHolder()
من قِبلRecyclerView
لعرض البيانات لعنصر قائمة واحد في الموضع المحدد. وبالتالي، تستخدم الطريقةonBindViewHolder()
وسيطتين: حامل عرض وموضع للبيانات لربطه. في هذا التطبيق، يكون حامل البطاقة هوTextItemViewHolder
، والموضع هو الموضع في القائمة.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}
- داخل
onBindViewHolder()
، يمكنك إنشاء متغيّر لعنصر واحد في موضع معيّن في البيانات.
val item = data[position]
- تشتمل
ViewHolder
التي أنشأتها على خاصية تُسمىtextView
. داخلonBindViewHolder()
، اضبطtext
منtextView
على رقم جودة النوم. يعرض هذا الرمز قائمة بالأرقام فقط، ولكن هذا المثال البسيط يتيح لك معرفة كيفية نقل المحوّل للبيانات إلى صاحب العرض وعلى الشاشة.
holder.textView.text = item.sleepQuality.toString()
- في
SleepNightAdapter
، يجب تنفيذ السياسةonCreateViewHolder()
وتنفيذها، والتي يتم استدعاءها عندما تحتاجRecyclerView
إلى مالك عرض لتمثيل عنصر.
تعتمد هذه الدالة على معلَمتَين وتعرضViewHolder
. وتكون المعلَمةparent
، وهي مجموعة الملف الشخصي التي تحتفظ بحامل الملف الشخصي، دائمًا هيRecyclerView
. يتم استخدام المعلَمةviewType
عند توفّر طرق عرض متعددة فيRecyclerView
نفسه. على سبيل المثال، إذا أضفت قائمة بالمشاهدات النصية والصور والفيديو فيRecyclerView
نفسها، ستحتاج الدالةonCreateViewHolder()
إلى معرفة نوع الملف الشخصي الذي يجب استخدامه.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}
- في
onCreateViewHolder()
، أنشئ مثيلاً لـLayoutInflater
.
تستخدم أداة تعديل التنسيق كيفية إنشاء طرق عرض من تنسيقات XML. يتضمنcontext
معلومات حول كيفية تضخيم العرض بشكل صحيح. في محوّل عرض إعادة التدوير، يتم دائمًا تمرير المحتوى في سياق مجموعة الملفات الشخصيةparent
، وهيRecyclerView
.
val layoutInflater = LayoutInflater.from(parent.context)
- في
onCreateViewHolder()
، أنشئview
بطلب منlayoutinflater
لتضخيمه.
مرر في تنسيق XML للملف الشخصي، وملفparent
الشخصي للملف الشخصي. الوسيطة المنطقية هيattachToRoot
. يجب أن تكون هذه الوسيطةfalse
، لأنRecyclerView
يضيف هذا العنصر إلى العرض الهرمي لك عندما يحين وقته.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView
- في غضون
onCreateViewHolder()
، يمكنك إرجاعTextItemViewHolder
باستخدامview
.
return TextItemViewHolder(view)
- يحتاج المحوّل إلى إبلاغ
RecyclerView
عند تغيّرdata
، لأنّRecyclerView
لا يعرف أي بيانات عن البيانات. ولا يمكنه معرفة سوى حوامل الملفات الشخصية التي يقدِّمها المحوِّل له.
لإبلاغRecyclerView
عند تغيُّر البيانات التي تعرضها هذه الميزة، أضِف أداة ضبط مخصَّصة إلى المتغيّرdata
الذي أعلى الصفSleepNightAdapter
. في واجهة برمجة التطبيقات، أعطdata
قيمة جديدة، ثم اتصل بـnotifyDataSetChanged()
لتشغيل إعادة رسم القائمة بالبيانات الجديدة.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}
الخطوة 4: إبلاغ RecyclerView حول المحوّل
يحتاج RecyclerView
إلى معرفة المزيد عن المحوّل لاستخدامه في الحصول على حواسن عرض.
- فتح
SleepTrackerFragment.kt
- في
onCreateview()
، أنشئ محوّلًا. ضع هذا الرمز بعد إنشاء نموذجViewModel
وقبل عبارةreturn
.
val adapter = SleepNightAdapter()
- ربط
adapter
بـRecyclerView
.
binding.sleepList.adapter = adapter
- عليك تنظيف مشروعك وإعادة إنشائه لتعديل الكائن
binding
.
إذا استمر ظهور الأخطاء حولbinding.sleepList
أوbinding.FragmentSleepTrackerBinding
، يُرجى إلغاء ذاكرة التخزين المؤقت وإعادة التشغيل. (اختَر الملف >؛ إلغاء صلاحية ذاكرات التخزين المؤقت / إعادة التشغيل.)
في حال تشغيل التطبيق الآن، ليست هناك أخطاء، ولكن لن تظهر لك أي بيانات تظهر عند النقر على بدء ثم إيقاف.
الخطوة 5: الحصول على البيانات في المحوِّل
حتى الآن، لديك محول، فضلاً عن طريقة للحصول على البيانات من المحوّل إلى RecyclerView
. عليك الآن الحصول على البيانات على المحوّل من جهاز ViewModel
.
- فتح
SleepTrackerViewModel
- ابحث عن المتغيّر
nights
الذي يخزّن جميع ليالي النوم، وهي البيانات التي يجب عرضها. يتم ضبط المتغيرnights
من خلال استدعاءgetAllNights()
في قاعدة البيانات. - عليك إزالة
private
منnights
، لأنك ستنشئ مراقبًا يحتاج إلى الوصول إلى هذا المتغيّر. يجب أن يظهر البيان كما يلي:
val nights = database.getAllNights()
- في حزمة
database
، افتحSleepDatabaseDao
. - ابحث عن الدالة
getAllNights()
. لاحظ أن هذه الدالة تعرض قائمة قيمSleepNight
على النحوLiveData
. وهذا يعني أن المتغيرnights
يحتوي علىLiveData
الذي يتم تحديثه بواسطةRoom
، ويمكنك ملاحظةnights
لمعرفة وقت تغييره. - فتح
SleepTrackerFragment
- في
onCreateView()
، أسفل إنشاءadapter
، يمكنك إنشاء مراقب على المتغيرnights
.
من خلال تقديم الجزءviewLifecycleOwner
الخاص بمالك اللعبة، يمكنك التأكّد من أن جهاز الرصد هذا نشط فقط عندما يكونRecyclerView
على الشاشة.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})
- داخل المراقب، كلما تحصل على قيمة غير فارغة (لـ
nights
)، عيّن القيمة إلى المحوّلdata
. هذا هو الرمز الكامل للمراقب وإعداد البيانات:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})
- إنشاء الرمز وتشغيله
ستظهر لك أرقام جودة النوم كقائمة في حال عمل المحوّل. تعرض لقطة الشاشة على اليمين القيمة -1 بعد النقر على بدء. تعرض لقطة الشاشة على اليسار الرقم الذي تم تعديله لجودة النوم بعد النقر على إيقاف واختيار تقييم الجودة.
الخطوة 6: استكشاف كيفية إعادة تدوير منصات العرض
RecyclerView
حوالي قواعد إعادة التدوير، ما يعني أنّها تعيد استخدامها. أثناء انتقال الملف الشخصي خارج الشاشة، يعيد RecyclerView
استخدام الملف الشخصي الذي ينطبق على الملف الشخصي الذي يمرّ على الشاشة.
ونظرًا لإعادة تدوير حوامل الملفات الشخصية هذه، تأكد من ضبط onBindViewHolder()
أو إعادة ضبط أي عمليات تخصيص ربما ضبطتها العناصر السابقة على إحدى منصات العرض.
على سبيل المثال، يمكنك ضبط لون النص على اللون الأحمر في حوامل العرض التي تحصل على تقييمات جودة أقل من أو تساوي 1 وتمثل نومًا سيئًا.
- في الصف
SleepNightAdapter
، أضِف الرمز التالي إلى نهايةonBindViewHolder()
.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}
- شغِّل التطبيق.
- أضِف بعض البيانات المنخفضة جودة النوم، وسيظهر الرقم باللون الأحمر.
- أضِف تقييمات عالية لجودة النوم إلى أن يظهر لك رقم أحمر مرتفع على الشاشة.
عندما يُعيدRecyclerView
استخدام أصحاب الشاشات، يُعيد استخدام أحد أصحاب الشاشات الحمراء للحصول على تقييم عالي الجودة. يتم عرض التقييم العالي عن طريق الخطأ باللون الأحمر.
- ولحلّ هذه المشكلة، عليك إضافة عبارة
else
لضبط اللون على اللون الأسود إذا كانت الجودة لا تقلّ عن واحدة أو تساويها.
في كلتا الحالتين، سيستخدم صاحب العرض لون النص الصحيح لكل سلعة.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}
- شغِّل التطبيق، ويجب أن تحمل الأرقام دائمًا اللون الصحيح.
تهانينا. أصبح لديك 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 المقدم والصقه.
- أنشئ ملفًا جديدًا لمورد التنسيق وأطلق عليه الاسم
list_item_sleep_night
. - استبدل الرمز بالكامل في الملف بالرمز أدناه. بعد ذلك، يمكنك التعرُّف على التنسيق الذي أنشأته للتو.
<?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>
- انتقِل إلى علامة التبويب التصميم في "استوديو Android". في عرض التصميم، يبدو التنسيق على النحو التالي: لقطة الشاشة على اليمين أدناه. في طريقة العرض المخطط، تظهر لقطة الشاشة على اليسار.
الخطوة 2: إنشاء حامل عرض
- فتح
SleepNightAdapter.kt
- أنشئ صفًا داخل
SleepNightAdapter
باسمViewHolder
واجعله يمتد إلىRecyclerView.ViewHolder
.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}
- في
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
- في تعريف
SleepNightAdapter
، بدلاً منTextItemViewHolder
، استخدمSleepNightAdapter.ViewHolder
الذي أنشأته للتو.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {
تعديل onCreateViewHolder()
:
- يجب تغيير توقيع
onCreateViewHolder()
لعرضViewHolder
. - يمكنك تغيير أداة تضخيم التنسيق لاستخدام مورد التنسيق الصحيح،
list_item_sleep_night
. - إزالة البث من
TextView
- بدلاً من عرض
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()
:
- غيّر توقيع
onBindViewHolder()
بحيث تكون المعلمةholder
ViewHolder
بدلاً منTextItemViewHolder
. - داخل
onBindViewHolder()
، عليك حذف جميع الرموز، باستثناء تعريفitem
. - حدِّد
val
res
الذي يشتمل على مرجع إلىresources
لهذا الملف الشخصي.
val res = holder.itemView.context.resources
- ضبط نص عرض النص
sleepLength
على المدة. انسخ الرمز أدناه، والذي يستدعي وظيفة تنسيق متوفّرة مع رمز إجراء التفعيل.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
- يؤدي ذلك إلى حدوث خطأ، حيث يجب تحديد
convertDurationToFormatted()
. افتحUtil.kt
وألغِ التعليق على الرمز وعمليات الاستيراد المرتبطة به. (اختَر الرمز > والتعليق مع تعليقات السطر.) - ارجع إلى
onBindViewHolder()
، واستخدِمconvertNumericQualityToString()
لضبط الجودة.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
- قد تحتاج إلى استيراد هذه الوظائف يدويًا.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString
- اضبط الرمز الصحيح للجودة. يتوفّر رمز
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
})
- في ما يلي دالة
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
})
}
- شغِّل تطبيقك. ومن المفترض أن تبدو شاشتك مثل لقطة الشاشة أدناه، حيث تعرض رمز جودة النوم، إلى جانب نص لمدة النوم وجودة النوم.
اكتملت معاملة RecyclerView
الآن. لقد تعلّمت كيفية تطبيق آلية Adapter
وViewHolder
، وجمعتهما لعرض قائمة تتضمّن RecyclerView
Adapter
.
يعرض الرمز حتى الآن عملية إنشاء محوّل وحامل عرض. ومع ذلك، يمكنك تحسين هذا الرمز. يتم مزج الرمز المطلوب عرضه والرمز الذي يُدار إدارة منصات العرض، ويمكن لتطبيق onBindViewHolder()
معرفة تفاصيل حول كيفية تعديل ViewHolder
.
في أحد تطبيقات الإنتاج، قد يكون لديك العديد من حاملات الملفات الشخصية، ومحوِّلات أكثر تعقيدًا، وعدد من مطوِّري البرامج الذين يُجرون تغييرات. يجب تنظيم الرمز بحيث يكون كل ما يتعلق بحامل الملف الشخصي متوفرًا فقط في حامل العرض.
الخطوة 1: ReFact onBindViewholder()
في هذه الخطوة، يمكنك إعادة ضبط الرمز ونقل جميع وظائف حامل العرض إلى ViewHolder
. والهدف من عملية إعادة التصميم هذه ليس تغيير شكل التطبيق بالنسبة إلى المستخدم، وإنما تسهيل وصول مطوّري البرامج إلى الرمز وجعله أكثر أمانًا. لحسن الحظ، يحتوي "استوديو Android" على أدوات لمساعدتك.
- في
SleepNightAdapter
، فيonBindViewHolder()
، اختَر كل شيء باستثناء العبارة لتعريف المتغيرitem
. - انقر بزر الماوس الأيمن، ثم اختَر العامل الإضافي >؛ استخراج >؛ الدالة.
- أدخِل اسمًا للدالة
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
})
}
- ضع المؤشر على الكلمة
holder
من المعلمةholder
التي تخصbind()
. اضغط علىAlt+Enter
(Option+Enter
على جهاز Mac) لفتح قائمة الأهداف. اختَر تحويل المعلّمة إلى المُستلِم لتحويل هذه الدالة إلى وظيفة إضافة لها التوقيع التالي:
private fun ViewHolder.bind(item: SleepNight) {...}
- اقطع دالة
bind()
والصقها فيViewHolder
. - عرض
bind()
بشكل علني - يمكنك استيراد
bind()
إلى المحوّل، إذا لزم الأمر. - لأن ذلك الآن في
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
.
- في
onCreateViewHolder()
، اختَر جميع الرموز في نص الدالة. - انقر بزر الماوس الأيمن، ثم اختَر العامل الإضافي >؛ استخراج >؛ الدالة.
- أدخِل اسمًا للدالة
from
واقبل المعلّمات المقترحة. انقر على حسنًا. - ضَع المؤشر على اسم الدالة
from
. اضغط علىAlt+Enter
(Option+Enter
على جهاز Mac) لفتح قائمة الأهداف. - اختَر الانتقال إلى العنصر المصاحب. يجب أن تكون الدالة
from()
في كائن مصاحب حتى يمكن استدعاؤها في الفئةViewHolder
، وليس عند استدعاء مثيلViewHolder
. - انقل العنصر
companion
إلى الفئةViewHolder
. - عرض
from()
بشكل علني - في
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)
}
}
- غيِّر توقيع الفئة
ViewHolder
بحيث تكون طريقة الإنشاء خاصة. بما أنّfrom()
أصبحت الآن طريقة تعرِض مثيلViewHolder
جديدًا، لم يعد هناك أي سبب يستدعي أن يتصل أي شخص بمنشئViewHolder
.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
- شغِّل التطبيق. يجب أن ينشئ تطبيقك التطبيق ويشغِّله كما في السابق، وهي النتيجة المطلوبة بعد إعادة هيكلة النظام.
مشروع "استوديو 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
.
بدء الدرس التالي: