يشكّل هذا الدرس التطبيقي جزءًا من الدورة التدريبية لأساسيات Android Kotlin. ستحصل على أقصى قيمة ممكنة من هذه الدورة التدريبية إذا كنت تستخدم الدروس التطبيقية حول الترميز بشكل متسلسل. يتم إدراج جميع الدروس التطبيقية حول ترميز الدورات التدريبية في الصفحة المقصودة لدروس الترميز Android Kotlin Fundamentals.
المقدّمة
في الدرس التطبيقي السابق حول الترميز، تعلّمت كيفية الحصول على البيانات من إحدى خدمات الويب وتحليل الاستجابة إلى كائن بيانات. في هذا الدرس التطبيقي حول الترميز، يمكنك الاستفادة من هذه المعلومات لتحميل الصور وعرضها من عنوان URL على الويب. وتُعيد أيضًا مراجعة كيفية إنشاء RecyclerView
واستخدامها لعرض شبكة من الصور على صفحة النظرة العامة.
ما يجب معرفته
- كيفية إنشاء الأجزاء واستخدامها
- كيفية استخدام مكوِّنات البنية، بما في ذلك نماذج العرض ومصانع النماذج والاطّلاع على التغييرات و
LiveData
. - كيفية استرداد JSON من خدمة الويب REST وتحليل هذه البيانات إلى كائنات Kotlin باستخدام مكتبات Retrofit وMoshi.
- طريقة إنشاء تنسيق الشبكة باستخدام
RecyclerView
- آلية عمل
Adapter
وViewHolder
وDiffUtil
ما ستتعرّف عليه
- كيفية استخدام مكتبة الكتابة بالتمرير لتحميل صورة وعرضها من عنوان URL للويب
- كيفية استخدام
RecyclerView
ومحوِّل الشبكة لعرض شبكة من الصور - كيفية التعامل مع الأخطاء المحتملة أثناء تنزيل الصور وعرضها.
المهام التي ستنفِّذها
- عدِّل تطبيق Mars RealEstate للحصول على عنوان URL للصورة من بيانات موقع Mars، واستخدم Glide لتحميل هذه الصورة وعرضها.
- إضافة رسم متحرك للتحميل ورمز خطأ إلى التطبيق.
- استخدِم السمة
RecyclerView
لعرض شبكة من صور الموقع الإلكتروني على كوكب المريخ. - يمكنك إضافة حالة ومعالجة الخطأ إلى
RecyclerView
.
في هذا الدرس التطبيقي حول الترميز (ومختبرات الرموز ذات الصلة)، يمكنك العمل مع تطبيق اسمه Mars RealEstate الذي يعرض عقارات للبيع على كوكب المريخ. يتصل التطبيق بخادم إنترنت لاسترداد بيانات العقارات وعرضها، بما في ذلك التفاصيل مثل السعر وما إذا كان العقار متاحًا للبيع أو الإيجار. الصور التي تمثل كل موقع هي صور حقيقية من كوكب المريخ تم التقاطها من كوكب ناسا لكوكب المريخ.
يتم ملء إصدار التطبيق الذي تنشئه في هذا الدرس التطبيقي في صفحة النظرة العامة، التي تعرض شبكة من الصور. تُعد الصور جزءًا من بيانات الموقع التي يحصل عليها تطبيقك من خدمة الويب للعقارات على كوكب المريخ. سيستخدم تطبيقك"مكتبة بالتمرير"لتحميل الصور وعرضها وRecyclerView
لإنشاء تنسيق الشبكة للصور. سيعمل تطبيقك أيضًا على معالجة أخطاء الشبكة بشكل أنيق.
قد يبدو عرض صورة من عنوان URL على الويب بوضوح، ولكن هناك قدرًا كبيرًا من الهندسة لإجرائها على نحو جيد. ويجب تنزيل الصورة وتخزينها مؤقتًا وفك ترميزها من تنسيقها المضغوط إلى صورة يمكن لنظام التشغيل Android استخدامها. ويجب تخزين الصورة مؤقتًا في ذاكرة تخزين مؤقت داخل الذاكرة أو ذاكرة تخزين مؤقت مستندة إلى مساحة التخزين أو كليهما. يجب أن يحدث كل هذا في سلاسل المحادثات ذات الأولوية المنخفضة بحيث تظل واجهة المستخدم متجاوبة. وللحصول على أفضل أداء للشبكة ووحدة المعالجة المركزية (CPU)، قد تحتاج إلى جلب وفك أكثر من صورة واحدة في الوقت نفسه. يمكن أن يكون تعلّم كيفية تحميل الصور من الشبكة هو درس تطبيقي في الترميز.
لحسن الحظ، يمكنك استخدام مكتبة طوّرها المنتدى باسم Glide لتنزيل الصور والتخزين المؤقت لها وفك تشفيرها والتخزين المؤقت لها. تتيح لك "الكتابة بالتمرير" جهدًا أقل بكثير من تنفيذ كل ذلك من البداية.
تحتاج ميزة "الكتابة بالتمرير" بشكل أساسي إلى عنصرَين:
- تمثل هذه السمة عنوان URL للصورة التي تريد تحميلها وعرضها.
- عنصر
ImageView
لعرض تلك الصورة.
في هذه المهمة، ستتعرّف على كيفية استخدام Glide لعرض صورة واحدة من خدمة الويب للعقارات. يمكنك عرض الصورة التي تمثل أول موقع لكوكب المريخ في قائمة المواقع التي تعرضها خدمة الويب. في ما يلي لقطات الشاشة قبل التغيير وبعده:
الخطوة 1: إضافة الاعتمادية بالتمرير
- افتح تطبيق Mars RealEstate من آخر درس تطبيقي حول الترميز. (يمكنك تنزيل Mars RealEstateNetwork هنا إذا لم يكن لديك التطبيق).
- شغِّل التطبيق لمعرفة وظائفه. (يعرض تفاصيل نصية لخاصية تتوفَّر افتراضيًا على كوكب المريخ).
- افتح build.gradle (الوحدة: التطبيق).
- في القسم
dependencies
، أضِف السطر التالي لمكتبة "الكتابة بالتمرير":
implementation "com.github.bumptech.glide:glide:$version_glide"
لاحظ أن رقم الإصدار محدّد سلفًا بشكل منفصل في ملف Gradle للمشروع.
- انقر على المزامنة الآن لإعادة بناء المشروع باستخدام الاعتمادية الجديدة.
الخطوة 2: تعديل نموذج العرض
بعد ذلك، عدّل الفئة OverviewViewModel
لتضمين البيانات المباشرة لموقع واحد على كوكب المريخ.
- فتح
overview/OverviewViewModel.kt
أسفلLiveData
مباشرةً في_response
، أضِف كلاً من البيانات المباشرة الداخلية (المتغيّرة) والخارجية (غير القابلة للتغيير) لعنصرMarsProperty
واحد.
استيراد صفMarsProperty
(com.example.android.marsrealestate.network.MarsProperty
) عند الطلب
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property
- في طريقة
getMarsRealEstateProperties()
، ابحث عن السطر داخل الكتلةtry/catch {}
التي تضبط_response.value
على عدد المواقع. أضِف الاختبار الموضّح أدناه. في حال توفُّر كائناتMarsProperty
، يحدّد هذا الاختبار قيمة_property
LiveData
على السمة الأولى فيlistResult
.
if (listResult.size > 0) {
_property.value = listResult[0]
}
تظهر الآن حزمة try/catch {}
الكاملة على النحو التالي:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
if (listResult.size > 0) {
_property.value = listResult[0]
}
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
- افتح ملف
res/layout/fragment_overview.xml
. في العنصر<TextView>
، غيِّرandroid:text
لربطه بالمكوّنimgSrcUrl
فيproperty
LiveData
:
android:text="@{viewModel.property.imgSrcUrl}"
- شغّل التطبيق. ولا يعرض
TextView
سوى عنوان URL للصورة في موقع المريخ الأول. كل ما فعلته حتى الآن هو إعداد نموذج العرض والبيانات المباشرة لعنوان URL هذا.
الخطوة 3: إنشاء محوّل مُلزِم واستدعاء Glide
أصبح لديك الآن عنوان URL لصورة لعرضها، وحان الوقت لبدء العمل باستخدام "الكتابة بالتمرير" لتحميل تلك الصورة. في هذه الخطوة، يمكنك استخدام محوّل ربط للحصول على عنوان URL من إحدى سمات XML المرتبطة بعلامة ImageView
، ويمكنك استخدام "الكتابة بالتمرير" لتحميل الصورة. محولات الربط هي طرق إضافة تقع بين الملف الشخصي والبيانات المرتبطة لتوفير سلوك مخصّص عند تغيير البيانات. في هذه الحالة، يكون السلوك المخصّص هو استدعاء"محو"لتحميل صورة من عنوان URL إلى ImageView
.
- فتح
BindingAdapters.kt
سيحمل هذا الملف محوّلات الربط المُستخدمة في التطبيق. - يمكنك إنشاء دالة
bindImage()
التي تستخدمImageView
وString
كمعلّمات. يمكنك إضافة تعليقات توضيحية إلى الدالة باستخدام@BindingAdapter
. يُعلِّق التعليق التوضيحي@BindingAdapter
على ربط البيانات الذي تريد تنفيذه باستخدام محوِّل الربط هذا عندما يحتوي عنصر XML على السمةimageUrl
.
استيرادandroidx.databinding.BindingAdapter
وandroid.widget.ImageView
عند الطلب
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
- داخل الدالة
bindImage()
، أضِف كتلةlet {}
للوسيطةimgUrl
:
imgUrl?.let {
}
- داخل الكتلة
let {}
، أضِف السطر الموضح أدناه لتحويل سلسلة عنوان URL (من XML) إلى عنصرUri
. يمكنك استيرادandroidx.core.net.toUri
عند طلبه.
يجب أن يستخدم الكائنUri
النهائي نظام HTTPS، لأن الخادم الذي تسحب الصور منه يتطلب هذا النظام. لاستخدام مخطط HTTPS، عليك إلحاقbuildUpon.scheme("https")
بأداة إنشاءtoUri
. الطريقةtoUri()
هي دالة إضافة Kotlin من مكتبة Android KTX، ولذلك تبدو وكأنها جزء من الفئةString
.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
- لا تزال داخل
let {}
، اتصل بـGlide.with()
لتحميل الصورة من الكائنUri
إلىImageView
. يمكنك استيرادcom.bumptech.glide.Glide
عند طلبها.
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)
الخطوة 4: تعديل التنسيق والأجزاء
على الرغم من أن Glide قد حمّلت الصورة، ليس هناك ما تراه بعد. تتمثل الخطوة التالية في تعديل التنسيق والأجزاء باستخدام ImageView
لعرض الصورة.
- فتح
res/layout/gridview_item.xml
هذا هو ملف مورد التنسيق الذي ستستخدمه لكل عنصر فيRecyclerView
لاحقًا في الدرس التطبيقي حول الترميز. يمكنك استخدام البطاقة مؤقتًا هنا لعرض صورة واحدة فقط. - أعلى العنصر
<ImageView>
، أضِف العنصر<data>
لربط البيانات واربطه بالفئةOverviewViewModel
:
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
- أضِف السمة
app:imageUrl
إلى العنصرImageView
لاستخدام محول الربط الجديد لتحميل الصور:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
- فتح
overview/OverviewFragment.kt
في الطريقةonCreateView()
، علِّق السطر الذي يؤدي إلى تضخيم الفئةFragmentOverviewBinding
ويخصّصه للمتغيّر الملزِم. هذا إجراء مؤقت فقط، وستتمكّن من الرجوع إليه لاحقًا.
//val binding = FragmentOverviewBinding.inflate(inflater)
- يمكنك إضافة سطر لتضخيم فئة
GridViewItemBinding
بدلاً من ذلك. استيرادcom.example.android.marsrealestate. databinding.GridViewItemBinding
عند الطلب.
val binding = GridViewItemBinding.inflate(inflater)
- شغِّل التطبيق. من المفترض أن ترى الآن صورة من أول
MarsProperty
في قائمة النتائج.
الخطوة 5: إضافة صور بسيطة التحميل والخطأ
يمكن لميزة "الكتابة بالتمرير" تحسين تجربة المستخدم من خلال عرض صورة العنصر النائب أثناء تحميل الصورة وصورة الخطأ في حال تعذّر التحميل، على سبيل المثال في حال كانت الصورة مفقودة أو تالفة. وفي هذه الخطوة، يمكنك إضافة هذه الوظيفة إلى محوّل الربط وإلى التنسيق.
- افتح
res/drawable/ic_broken_image.xml
، وانقر على علامة التبويب معاينة على اليسار. بالنسبة إلى صورة الخطأ، أنت تستخدم رمز الصورة المكسورة والمتوفّر في مكتبة الرموز المدمجة. يستخدم هذا المتّجه القابل للرسم السمةandroid:tint
لتلوين الرمز باللون الرمادي.
- فتح
res/drawable/loading_animation.xml
هذه الرسومات القابلة للرسم هي رسوم متحركة يتم تحديدها بالعلامة<animate-rotate>
. وتعمل الصورة المتحركة على تدوير الصورة القابلة للرسم،loading_img.xml
، حول النقطة المركزية. (لم ترَ الصورة المتحركة في المعاينة).
- ارجع إلى ملف
BindingAdapters.kt
. في الطريقةbindImage()
، عدِّل الاستدعاء إلىGlide.with()
لاستدعاء الدالةapply()
بينload()
وinto()
. يمكنك استيرادcom.bumptech.glide.request.RequestOptions
عند طلب ذلك.
يضبط هذا الرمز صورة التحميل النائبة أثناء التحميل (يمكن سحبloading_animation
). يؤدي الرمز أيضًا إلى ضبط صورة لاستخدامها في حال تعذّر تحميل الصورة (يمكن سحبbroken_image
). تظهر طريقةbindImage()
الكاملة الآن على النحو التالي:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri =
imgUrl.toUri().buildUpon().scheme("https").build()
Glide.with(imgView.context)
.load(imgUri)
.apply(RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
}
}
- شغِّل التطبيق. ووفقًا لسرعة اتصال الشبكة، قد ترى صورة التحميل لفترة وجيزة أثناء تنزيل تطبيق Glide أثناء عرض صورة الموقع. لكنك لن ترى رمز الصورة المعطلة بعد، حتى في حال إيقاف الشبكة، يمكنك إصلاح ذلك في الجزء الأخير من الدرس التطبيقي حول الترميز.
يُحمِّل تطبيقك الآن معلومات الموقع من الإنترنت. باستخدام بيانات من عنصر القائمة الأول (MarsProperty
)، أنشأت موقع LiveData
في نموذج الملف الشخصي، واستخدمت عنوان URL للصورة من بيانات ذلك الموقع لتعبئة ImageView
. ولكن الهدف هو أن يعرض تطبيقك شبكة من الصور، لذا تريد استخدام RecyclerView
مع GridLayoutManager
.
الخطوة 1: تعديل نموذج العرض
يحتوي نموذج الملف الشخصي الآن على _property
LiveData
الذي يحتوي على كائن MarsProperty
واحد، وهو العنصر الأول في قائمة الردود من خدمة الويب. في هذه الخطوة، يمكنك تغيير LiveData
للاحتفاظ بقائمة العناصر التي تتضمّن MarsProperty
بالكامل.
- فتح
overview/OverviewViewModel.kt
- غيِّر المتغير
_property
الخاص إلى_properties
. غيِّر النوع لتتضمن قائمة بكائناتMarsProperty
.
private val _properties = MutableLiveData<List<MarsProperty>>()
- استبدِل بيانات
property
الخارجية المباشرة بـproperties
. أضف القائمة إلى نوعLiveData
هنا أيضًا:
val properties: LiveData<List<MarsProperty>>
get() = _properties
- انتقِل للأسفل إلى طريقة
getMarsRealEstateProperties()
. داخل الكتلةtry {}
، استبدل الاختبار الكامل الذي أضفته في المهمة السابقة بالسطر الموضّح أدناه. بما أنّ المتغيّرlistResult
يحتوي على قائمةMarsProperty
من العناصر، يمكنك ببساطة تخصيصها إلى_properties.value
بدلاً من اختبارها لاستجابة ناجحة.
_properties.value = listResult
سيبدو الآن الحظر try/catch
الكامل على النحو التالي:
try {
var listResult = getPropertiesDeferred.await()
_response.value = "Success: ${listResult.size} Mars properties retrieved"
_properties.value = listResult
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
الخطوة 2: تعديل التنسيقات والأجزاء
وتتمثل الخطوة التالية في تغيير تصميم التطبيق وأجزاءه لاستخدام عرض إعادة التدوير وتنسيق الشبكة، بدلاً من عرض الصورة الواحدة.
- فتح
res/layout/gridview_item.xml
غيّر ربط البيانات منOverviewViewModel
إلىMarsProperty
، ثم أعد تسمية المتغير إلى"property"
.
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
- في
<ImageView>
، غيِّر سمةapp:imageUrl
للإشارة إلى عنوان URL للصورة في العنصرMarsProperty
:
app:imageUrl="@{property.imgSrcUrl}"
- فتح
overview/OverviewFragment.kt
فيonCreateview()
، ألغِ التعليق على السطر الذي يؤدي إلى تضخيمFragmentOverviewBinding
. حذف الخط الذي يؤدي إلى تضخيمGridViewBinding
أو التعليق عليه. وتتراجع هذه التغييرات عن التغييرات المؤقتة التي أجريتها في المهمة الأخيرة.
val binding = FragmentOverviewBinding.inflate(inflater)
// val binding = GridViewItemBinding.inflate(inflater)
- فتح
res/layout/fragment_overview.xml
حذف العنصر<TextView>
بالكامل - أضِف العنصر
<RecyclerView>
هذا بدلاً من ذلك، الذي يستخدم تنسيقGridLayoutManager
وتنسيقgrid_view_item
لسلعة واحدة:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/photos_grid"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="6dp"
android:clipToPadding="false"
app:layoutManager=
"androidx.recyclerview.widget.GridLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:spanCount="2"
tools:itemCount="16"
tools:listitem="@layout/grid_view_item" />
الخطوة 3: إضافة محوِّل شبكة الصورة
يحتوي الآن تنسيق fragment_overview
على RecyclerView
، بينما يحتوي التنسيق grid_view_item
على ImageView
واحد. في هذه الخطوة، يمكنك ربط البيانات بـ RecyclerView
من خلال محوّل RecyclerView
.
- فتح
overview/PhotoGridAdapter.kt
- إنشاء الفئة
PhotoGridAdapter
، باستخدام مُعلّمات دالة الإنشاء الموضحة أدناه. تمتد الفئةPhotoGridAdapter
إلىListAdapter
، حيث تحتاج أداة الإنشاء إلى نوع عنصر القائمة وصاحب العرض وتطبيقDiffUtil.ItemCallback
.
استيراد صفَيandroidx.recyclerview.widget.ListAdapter
وcom.example.android.marsrealestate.network.MarsProperty
عند طلبهما في الخطوات التالية، يمكنك تنفيذ الأجزاء المفقودة الأخرى من هذه الشركة المصنّعة التي تتسبب في حدوث أخطاء.
class PhotoGridAdapter : ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
}
- انقر في أي مكان في صف
PhotoGridAdapter
واضغط علىControl+i
لتنفيذ الطرقListAdapter
، وهيonCreateViewHolder()
وonBindViewHolder()
.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
TODO("not implemented")
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
TODO("not implemented")
}
- في نهاية تعريف الفئة
PhotoGridAdapter
، بعد الطرق التي أضفتها، أضِف تعريف الكائن المصاحب للسمةDiffCallback
كما هو موضّح أدناه.
استيرادandroidx.recyclerview.widget.DiffUtil
عند الطلب
يمتد العنصرDiffCallback
إلىDiffUtil.ItemCallback
بنوع العنصر الذي تريد مقارنته:MarsProperty
.
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
- اضغط على
Control+i
لتنفيذ أساليب المقارنة لهذا الكائن، وهيareItemsTheSame()
وareContentsTheSame()
.
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented")
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
TODO("not implemented") }
- بالنسبة إلى طريقة
areItemsTheSame()
، أزِل المهام. استخدِم عامل تشغيل المساواة المرجعية (===
) في Kotlin' والذي يعرضtrue
إذا كان مرجع العنصرoldItem
وnewItem
متطابقًا.
override fun areItemsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem === newItem
}
- بالنسبة إلى
areContentsTheSame()
، استخدِم عامل تشغيل المساواة العادي على رقم تعريفoldItem
وnewItem
فقط.
override fun areContentsTheSame(oldItem: MarsProperty,
newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}
- لا تزال داخل الفئة
PhotoGridAdapter
، أسفل العنصر المصاحب، أضِف تعريف الفئة الداخلية للسمةMarsPropertyViewHolder
، والذي يمتد إلىRecyclerView.ViewHolder
.
استيرادandroidx.recyclerview.widget.RecyclerView
وcom.example.android.marsrealestate.databinding.GridViewItemBinding
عند طلب ذلك.
تحتاج إلى المتغيّرGridViewItemBinding
لربطMarsProperty
بالتنسيق، لذا أدخِل المتغير إلىMarsPropertyViewHolder
. ونظرًا لأن الفئة الأساسية منViewHolder
تتطلب عرضًا في دالة الإنشاء، فإنك تضبطها للعرض الجذر الملزِم.
class MarsPropertyViewHolder(private var binding:
GridViewItemBinding):
RecyclerView.ViewHolder(binding.root) {
}
- في
MarsPropertyViewHolder
، يمكنك إنشاء طريقةbind()
تستخدم كائنMarsProperty
كوسيطة وضبطbinding.property
لهذا الكائن. يمكنك استدعاءexecutePendingBindings()
بعد إعداد الخاصية، مما يؤدي إلى تنفيذ التحديث فورًا.
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}
- في
onCreateViewHolder()
، يُرجى إزالة قائمة المهام المهام وإضافة السطر الموضح أدناه. استيرادandroid.view.LayoutInflater
عند الطلب.
يجب أن تعرض طريقةonCreateViewHolder()
عرضMarsPropertyViewHolder
جديدًا تم إنشاؤه عن طريق تضخيمGridViewItemBinding
واستخدامLayoutInflater
من سياقViewGroup
الرئيسي.
return MarsPropertyViewHolder(GridViewItemBinding.inflate(
LayoutInflater.from(parent.context)))
- في طريقة
onBindViewHolder()
، أزِل المهام المهام وأضِف الأسطر المعروضة أدناه. وعليك هنا استدعاءgetItem()
للحصول على الكائنMarsProperty
المرتبط بالموضعRecyclerView
الحالي، ثم تمرير هذه الخاصية إلى الطريقةbind()
فيMarsPropertyViewHolder
.
val marsProperty = getItem(position)
holder.bind(marsProperty)
الخطوة 4: إضافة محوِّل الربط وتوصيل الأجزاء
وأخيرًا، استخدِم BindingAdapter
لإعداد PhotoGridAdapter
مع قائمة العناصر MarsProperty
. يؤدي استخدام السياسة BindingAdapter
لضبط بيانات RecyclerView
إلى ربط البيانات LiveData
تلقائيًا لقائمة عناصر MarsProperty
. وبعد ذلك، يتم استدعاء محول الربط تلقائيًا عند تغيير قائمة MarsProperty
.
- فتح
BindingAdapters.kt
- في نهاية الملف، أضِف طريقة
bindRecyclerView()
تأخذRecyclerView
وقائمة من العناصرMarsProperty
كوسيطات. يمكنك إضافة تعليقات توضيحية إلى هذه الطريقة باستخدام@BindingAdapter
.
يمكنك استيرادandroidx.recyclerview.widget.RecyclerView
وcom.example.android.marsrealestate.network.MarsProperty
عند طلبها.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
}
- من داخل دالة
bindRecyclerView()
، يُرجى إرسالrecyclerView.adapter
إلىPhotoGridAdapter
، ثم الاتصال بadapter.submitList()
مع البيانات. يؤدي ذلك إلى إعلامRecyclerView
عند توفّر قائمة جديدة.
استيراد com.example.android.marsrealestate.overview.PhotoGridAdapter
عند الطلب.
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
- فتح
res/layout/fragment_overview.xml
أضِف السمةapp:listData
إلى العنصرRecyclerView
واضبطها علىviewmodel.properties
باستخدام ربط البيانات.
app:listData="@{viewModel.properties}"
- فتح
overview/OverviewFragment.kt
فيonCreateView()
، وقبل المكالمة إلىsetHasOptionsMenu()
مباشرة، عليك إعداد محوّلRecyclerView
فيbinding.photosGrid
باستخدام عنصرPhotoGridAdapter
جديد.
binding.photosGrid.adapter = PhotoGridAdapter()
- شغِّل التطبيق. من المفترض أن تظهر شبكة من
MarsProperty
صورة. أثناء التمرير لرؤية صور جديدة، يعرض التطبيق رمز تقدم التحميل قبل عرض الصورة نفسها. في حال تفعيل "وضع الطيران"، تظهر الصور التي لم يتم تحميلها بعد كرموز تشير إلى أعطال.
يعرض تطبيق Mars RealEstate رمز الصورة المعطلة عندما لا يمكن جلب الصورة. ولكن في حالة عدم وجود شبكة، يعرض التطبيق شاشة فارغة.
هذه ليست تجربة مستخدم رائعة. في هذه المهمة، يمكنك إضافة معالجة أساسية للأخطاء، لمنح المستخدم فكرة أفضل عن ما يحدث. إذا لم يكن الإنترنت متاحًا، سيعرض التطبيق رمز خطأ الاتصال. أثناء جلب التطبيق لقائمة MarsProperty
، سيعرض التطبيق الصورة المتحركة للتحميل.
الخطوة 1: إضافة الحالة إلى نموذج الملف الشخصي
للبدء، عليك إنشاء LiveData
في نموذج الملف الشخصي لتمثيل حالة طلب الويب. هناك ثلاث حالات يجب مراعاتها - التحميل والنجاح والفشل. تحدث حالة التحميل أثناء انتظارك للبيانات في المكالمة إلى await()
.
- فتح
overview/OverviewViewModel.kt
في أعلى الملف (بعد عمليات الاستيراد، وقبل تعريف الفئة)، أضِفenum
لتمثيل جميع الحالات المتاحة:
enum class MarsApiStatus { LOADING, ERROR, DONE }
- أعد تسمية تعريفات البيانات المباشرة الداخلية والخارجية في
_response
على مستوىOverviewViewModel
إلى_status
. نظرًا لأنك أضفت دعمًا لـ_properties
LiveData
في وقت سابق من هذا الدرس التطبيقي حول الترميز، لم يتم استخدام استجابة خدمة الويب الكاملة. تحتاج إلىLiveData
هنا لتتبع الحالة الحالية، حتى تتمكن من إعادة تسمية المتغيرات الحالية.
يمكنك أيضًا تغيير الأنواع من String
إلى MarsApiStatus.
.
private val _status = MutableLiveData<MarsApiStatus>()
val status: LiveData<MarsApiStatus>
get() = _status
- انتقل للأسفل إلى طريقة
getMarsRealEstateProperties()
وحدِّث_response
إلى_status
هنا أيضًا. غيِّر السلسلة"Success"
إلى الحالةMarsApiStatus.DONE
، واضبط السلسلة"Failure"
إلىMarsApiStatus.ERROR
. - أضِف الحالة
MarsApiStatus.LOADING
في أعلى المجموعةtry {}
، قبل المكالمة إلىawait()
. هذه هي الحالة الأولية أثناء تشغيل الكوروتين وأنت في انتظار البيانات. تظهر الآن حزمةtry/catch {}
الكاملة على النحو التالي:
try {
_status.value = MarsApiStatus.LOADING
var listResult = getPropertiesDeferred.await()
_status.value = MarsApiStatus.DONE
_properties.value = listResult
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
}
- بعد ظهور حالة الخطأ في الحظر
catch {}
، اضبط_properties
LiveData
على قائمة فارغة. يؤدي هذا الإجراء إلى محوRecyclerView
.
} catch (e: Exception) {
_status.value = MarsApiStatus.ERROR
_properties.value = ArrayList()
}
الخطوة 2: إضافة محوِّل ملزِم لحالة الصورة
لديك الآن حالة في نموذج العرض، ولكنها لا تمثّل سوى مجموعة من الحالات. كيف يمكن عرضه في التطبيق نفسه؟ في هذه الخطوة، يمكنك استخدام ImageView
المرتبط بربط البيانات لعرض الرموز لحالة التحميل والخطأ. عندما يكون التطبيق في حالة التحميل أو حالة الخطأ، من المفترض أن يكون ImageView
مرئيًا. عند انتهاء تحميل التطبيق، يجب أن يكون ImageView
غير مرئي.
- فتح
BindingAdapters.kt
أضِف محوّل ربط جديدًا باسمbindStatus()
يأخذImageView
وقيمةMarsApiStatus
كوسيطات. استيرادcom.example.android.marsrealestate.overview.MarsApiStatus
عند الطلب.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView,
status: MarsApiStatus?) {
}
- أضِف
when {}
داخل طريقةbindStatus()
للتبديل بين الحالات المختلفة.
when (status) {
}
- داخل
when {}
، أضِف حافظة لحالة التحميل (MarsApiStatus.LOADING
). وبالنسبة إلى هذه الحالة، اضبطImageView
على مرئي، خصِّص له حركة التحميل. هذه هي الصورة المتحركة نفسها القابلة للسحب التي استخدمتها في ميزة "الكتابة بالتمرير" في المهمة السابقة. استيرادandroid.view.View
عند الطلب.
when (status) {
MarsApiStatus.LOADING -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.loading_animation)
}
}
- أضِف حالة لحالة الخطأ، وهي
MarsApiStatus.ERROR
. على غرار الإجراء الذي اتخذته في حالةLOADING
، اضبط الحالةImageView
على "مرئية" وأعِد استخدام خطأ الاتصال القابل للرسم.
MarsApiStatus.ERROR -> {
statusImageView.visibility = View.VISIBLE
statusImageView.setImageResource(R.drawable.ic_connection_error)
}
- أضِف تجربة دعم للحالة التي تم تنفيذها، وهي
MarsApiStatus.DONE
. لديك استجابة ناجحة هنا، لذا يمكنك إيقاف حق الوصول إلى الحالةImageView
لإخفائها.
MarsApiStatus.DONE -> {
statusImageView.visibility = View.GONE
}
الخطوة 3: إضافة الحالة ImageView إلى التنسيق
- فتح
res/layout/fragment_overview.xml
أسفل العنصرRecyclerView
، داخلConstraintLayout
، أضِفImageView
الموضّح أدناه.
لدىImageView
هذه القيود نفسها التي تنطبق علىRecyclerView
. ومع ذلك، يستخدم العرض والارتفاعwrap_content
لتوسيط الصورة بدلاً من تمديد الصورة لملء العرض. لاحظ أيضًا السمةapp:marsApiStatus
التي تشتمل على طريقة عرض "BindingAdapter
" عندما تتغير خاصية الحالة في نموذج الملف الشخصي.
<ImageView
android:id="@+id/status_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:marsApiStatus="@{viewModel.status}" />
- فعِّل وضع الطيران في المحاكي أو الجهاز لمحاكاة الاتصال بالشبكة المفقودة. اجمع التطبيق وشغِّله، ولاحظ ظهور صورة الخطأ:
- انقر على زر الرجوع لإغلاق التطبيق، ثم أوقِف وضع الطيران. استخدِم شاشة التطبيقات الأخيرة لإرجاع التطبيق. ووفقًا لسرعة الاتصال بالشبكة، قد ترى مؤشر سريان قصيرًا جدًا أثناء تحميل التطبيق لطلب بحث خدمة الويب قبل بدء تحميل الصور.
مشروع Android Studio: Mars RealEstateNetwork
- لتبسيط عملية إدارة الصور، يمكنك استخدام المكتبة بالتمرير لتنزيل الصور وتخزينها في التطبيق وفك ترميزها والتخزين المؤقت فيها.
- تحتاج ميزة "الكتابة بالتمرير" إلى عنصرَين لتحميل صورة من الإنترنت: عنوان URL لصورة وعنصر
ImageView
لوضع الصورة فيها. ولتحديد هذه الخيارات، استخدِم طريقتَيload()
وinto()
مع "الكتابة بالتمرير". - محولات الربط هي طرق الإضافات التي تقع بين زاوية العرض و البيانات المرتبطة بهذا الملف الشخصي. وتوفّر محوّلات الربط سلوكًا مخصّصًا عند تغيّر البيانات، مثلاً، لطلب"بالتمرير"لتحميل صورة من عنوان URL إلى
ImageView
. - مهايئات الربط هي طرق إضافات تم إدخال تعليقات توضيحية عليها باستخدام التعليق التوضيحي
@BindingAdapter
. - لإضافة خيارات إلى طلب "الكتابة بالتمرير"، استخدِم طريقة
apply()
. على سبيل المثال، استخدِمapply()
معplaceholder()
لتحديد بيانات قابلة للرسم، واستخدِمapply()
معerror()
لتحديد خطأ يمكن رسمه. - لإنشاء شبكة من الصور، استخدِم
RecyclerView
مع السمةGridLayoutManager
. - لتعديل قائمة الخصائص عندما تتغير، استخدِم محوِّل ربط بين
RecyclerView
والتنسيق.
دورة Udacity:
مستندات مطوّر برامج Android:
غير ذلك:
يسرد هذا القسم المهام الدراسية المحتملة للطلاب الذين يعملون من خلال هذا الدرس التطبيقي حول الترميز في إطار دورة تدريبية يُديرها معلِّم. يجب أن ينفِّذ المعلّم ما يلي:
- يمكنك تخصيص واجب منزلي إذا لزم الأمر.
- التواصل مع الطلاب بشأن كيفية إرسال الواجبات المنزلية
- وضع درجات للواجبات المنزلية.
ويمكن للمعلّمين استخدام هذه الاقتراحات بقدر ما يريدون أو بقدر ما يريدون، ويجب عدم التردد في تخصيص أي واجبات منزلية أخرى مناسبة.
إذا كنت تستخدم هذا الدرس التطبيقي بنفسك، يمكنك استخدام هذه الواجبات المنزلية لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 1
ما طريقة الكتابة بالتمرير التي تستخدمها للإشارة إلى علامة ImageView
التي ستتضمّن الصورة التي تم تحميلها؟
▢ into()
▢ with()
▢ imageview()
▢ apply()
السؤال 2
كيف يمكنك تحديد صورة عنصر نائب ليتم عرضها أثناء تحميل تطبيق Glide؟
▢ استخدِم الطريقة into()
مع رسم قابل للرسم.
▢ استخدِم RequestOptions()
واستدعِ طريقة placeholder()
مع بيانات قابلة للرسم.
▢ خصِّص الخاصية Glide.placeholder
على عنصر قابل للرسم.
▢ استخدِم RequestOptions()
واستدعِ طريقة loadingImage()
مع بيانات قابلة للرسم.
السؤال 3
كيف يتم توضيح أن الطريقة هي محوّل ربط؟
▢ استدعاء الطريقة setBindingAdapter()
على LiveData
.
▢ وضِّح الطريقة في ملف Kotlin باسم BindingAdapters.kt
.
▢ استخدِم السمة android:adapter
في تنسيق XML.
▢ يمكنك إضافة تعليقات توضيحية إلى الطريقة باستخدام @BindingAdapter
.
بدء الدرس التالي:
وللحصول على روابط إلى دروس تطبيقية أخرى حول الترميز في هذه الدورة التدريبية، يُرجى الاطّلاع على الصفحة المقصودة لتطبيق الدروس التطبيقية حول الترميز Kotlin Fundamentals.