هذا الدرس التطبيقي حول الترميز هو جزء من دورة "أساسيات Android Kotlin". يمكنك تحقيق أقصى استفادة من هذه الدورة التدريبية إذا اتبعت ترتيب الخطوات في دروس البرمجة. يتم إدراج جميع الدروس التطبيقية حول الترميز الخاصة بالدورة التدريبية في الصفحة المقصودة للدروس التطبيقية حول الترميز في دورة Android Kotlin Fundamentals.
مقدمة
في الدروس التطبيقية السابقة لهذا الدرس، تعلّمت كيفية الحصول على بيانات حول العقارات على المريخ من خدمة ويب، وكيفية إنشاء RecyclerView
باستخدام تصميم شبكي لتحميل الصور وعرضها من تلك البيانات. في هذا الدرس العملي، ستنهي تطبيق MarsRealEstate من خلال تنفيذ إمكانية فلترة العقارات على كوكب المريخ حسب ما إذا كانت متاحة للاستئجار أو الشراء. يمكنك أيضًا إنشاء عرض تفصيلي حتى إذا نقر المستخدم على صورة مكان مخصّص للاستئجار في النظرة العامة، يظهر له عرض تفصيلي يتضمّن تفاصيل حول هذا المكان.
ما يجب معرفته
- كيفية إنشاء واستخدام الأجزاء
- كيفية التنقّل بين الأجزاء واستخدام Safe Args (إضافة Gradle) لتمرير البيانات بين الأجزاء
- كيفية استخدام مكوّنات البنية، بما في ذلك نماذج العرض، ومصانع نماذج العرض، وعمليات التحويل، و
LiveData
- كيفية استرداد البيانات المرمّزة بتنسيق JSON من خدمة ويب REST وتحليل هذه البيانات إلى عناصر Kotlin باستخدام مكتبتَي Retrofit وMoshi
ما ستتعرّف عليه
- كيفية استخدام تعبيرات الربط المعقّدة في ملفات التصميم
- كيفية إرسال طلبات Retrofit إلى خدمة ويب تتضمّن خيارات طلب بحث
المهام التي ستنفذها
- عدِّل تطبيق MarsRealEstate لوضع علامة على عقارات Mars المعروضة للبيع (مقابل تلك المعروضة للإيجار) باستخدام رمز الدولار.
- استخدِم قائمة الخيارات في صفحة النظرة العامة لإنشاء طلب خدمة ويب يفلتر خصائص المريخ حسب النوع.
- أنشئ جزء تفاصيل لموقع Mars، واربط هذا الجزء بشبكة النظرة العامة باستخدام التنقّل، ومرِّر بيانات الموقع إلى هذا الجزء.
في هذا الدرس التطبيقي حول الترميز (والدروس التطبيقية ذات الصلة)، ستعمل على تطبيق يُسمى MarsRealEstate يعرض عقارات معروضة للبيع على كوكب المريخ. يتصل هذا التطبيق بخادم إنترنت لاسترداد بيانات العقارات وعرضها، بما في ذلك تفاصيل مثل السعر وما إذا كان العقار متاحًا للبيع أو الإيجار. الصور التي تمثّل كلّ موقع هي صور حقيقية من المريخ تم التقاطها بواسطة مركبات استكشاف المريخ التابعة لوكالة ناسا. في دروس سابقة، أنشأت RecyclerView
باستخدام تنسيق شبكة لجميع صور العقارات:
في هذا الإصدار من التطبيق، يمكنك العمل مع نوع العقار (إيجار مقابل شراء) وإضافة رمز إلى تصميم الشبكة لوضع علامة على العقارات المعروضة للبيع:
يمكنك تعديل قائمة خيارات التطبيق لفلترة الشبكة وعرض العقارات المتاحة للاستئجار أو البيع فقط:
وأخيرًا، يمكنك إنشاء عرض تفصيلي لمكان إقامة فردي، وربط الرموز في شبكة النظرة العامة بقطعة التفاصيل هذه باستخدام التنقّل:
حتى الآن، كان الجزء الوحيد من بيانات موقع Mars الذي استخدمته هو عنوان URL لصورة الموقع. لكنّ بيانات المكان المخصّص للاستئجار، والتي حدّدتها في الفئة MarsProperty
، تتضمّن أيضًا معرّفًا وسعرًا ونوعًا (مكان مخصّص للاستئجار أو للبيع). لتذكيرك، إليك مقتطف من بيانات JSON التي تحصل عليها من خدمة الويب:
{
"price":8000000,
"id":"424908",
"type":"rent",
"img_src": "http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631290305226E03_DXXX.jpg"
},
في هذه المهمة، ستبدأ العمل مع نوع الموقع "المريخ" لإضافة صورة علامة الدولار إلى المواقع المعروضة للبيع في صفحة النظرة العامة.
الخطوة 1: تعديل MarsProperty لتضمين النوع
تحدّد الفئة MarsProperty
بنية البيانات لكل سمة تقدّمها خدمة الويب. في درس برمجة سابق، استخدمت مكتبة Moshi لتحليل استجابة JSON الأولية من خدمة الويب الخاصة بكوكب المريخ إلى عناصر بيانات MarsProperty
فردية.
في هذه الخطوة، ستضيف بعض المنطق إلى الفئة MarsProperty
للإشارة إلى ما إذا كانت هناك عقارات متاحة للاستئجار أم لا (أي ما إذا كان النوع هو السلسلة "rent"
أو "buy"
). ستستخدم هذا المنطق في أكثر من مكان، لذا من الأفضل إضافته هنا في فئة البيانات بدلاً من تكراره.
- افتح تطبيق MarsRealEstate من آخر تجربة عملية. (يمكنك تنزيل تطبيق MarsRealEstateGrid إذا لم يكن مثبّتًا لديك).
- فتح "
network/MarsProperty.kt
" أضِف نصًا برمجيًا إلى تعريف الفئةMarsProperty
، وأضِف دالة جلب مخصّصة للسمةisRental
تعرض القيمةtrue
إذا كان العنصر من النوع"rent"
.
data class MarsProperty(
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) {
val isRental
get() = type == "rent"
}
الخطوة 2: تعديل تخطيط عنصر الشبكة
الآن، عدِّل تخطيط السلعة لشبكة الصور لعرض عنصر قابل للرسم بعلامة الدولار فقط على صور الأماكن المتاحة للإيجار:
باستخدام تعبيرات ربط البيانات، يمكنك إجراء هذا الاختبار بالكامل في تنسيق XML لعناصر الشبكة.
- فتح "
res/layout/grid_view_item.xml
" هذا هو ملف التصميم لكل خلية فردية في تصميم الشبكة الخاص بـRecyclerView
. يحتوي الملف حاليًا على العنصر<ImageView>
فقط لصورة المكان المخصّص للاستئجار. - داخل العنصر
<data>
، أضِف العنصر<import>
للفئةView
. يمكنك استخدام عمليات الاستيراد عندما تريد استخدام مكوّنات فئة داخل تعبير ربط البيانات في ملف تنسيق. في هذه الحالة، ستستخدم الثوابتView.GONE
وView.VISIBLE
، لذا عليك الوصول إلى الفئةView
.
<import type="android.view.View"/>
- أحِط عرض الصورة بالكامل بعلامة
FrameLayout
، للسماح بتجميع الرسم القابل للرسم بعلامة الدولار فوق صورة المكان المخصّص للاستئجار.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
...
</FrameLayout>
- بالنسبة إلى
ImageView
، غيِّر السمةandroid:layout_height
إلىmatch_parent
لملء العنصر الرئيسي الجديدFrameLayout
.
android:layout_height="match_parent"
- أضِف عنصر
<ImageView>
ثانيًا أسفل العنصر الأول مباشرةً، داخلFrameLayout
. استخدِم التعريف الموضّح أدناه. تظهر هذه الصورة في أسفل يسار عنصر الشبكة، فوق صورة المريخ، وتستخدم العنصر القابل للرسم المحدّد فيres/drawable/ic_for_sale_outline.xml
لرمز علامة الدولار.
<ImageView
android:id="@+id/mars_property_type"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_gravity="bottom|end"
android:adjustViewBounds="true"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_for_sale_outline"
tools:src="@drawable/ic_for_sale_outline"/>
- أضِف السمة
android:visibility
إلى طريقة عرض الصورةmars_property_type
. استخدِم تعبير ربط لاختبار نوع السمة، وحدِّد إذن الوصول إما إلىView.GONE
(للاستئجار) أوView.VISIBLE
(للشراء).
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"
حتى الآن، لم تظهر لك تعبيرات الربط إلا في التصاميم التي تستخدم متغيرات فردية محدّدة في العنصر <data>
. تتسم تعبيرات الربط بالقوة الفائقة وتتيح لك إجراء عمليات مثل الاختبارات والحسابات الرياضية بالكامل داخل تنسيق XML. في هذه الحالة، يمكنك استخدام عامل التشغيل الثلاثي (?:
) لإجراء اختبار (هل هذا العنصر مخصّص للاستئجار؟). يمكنك تقديم نتيجة واحدة للقيمة "صحيح" (إخفاء رمز علامة الدولار باستخدام View.GONE
) وأخرى للقيمة "خطأ" (إظهار هذا الرمز باستخدام View.VISIBLE
).
يظهر ملف grid_view_item.xml
الكامل الجديد أدناه:
<layout 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">
<data>
<import type="android.view.View"/>
<variable
name="property"
type="com.example.android.marsrealestate.network.MarsProperty" />
</data>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="170dp">
<ImageView
android:id="@+id/mars_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
android:padding="2dp"
app:imageUrl="@{property.imgSrcUrl}"
tools:src="@tools:sample/backgrounds/scenic"/>
<ImageView
android:id="@+id/mars_property_type"
android:layout_width="wrap_content"
android:layout_height="45dp"
android:layout_gravity="bottom|end"
android:adjustViewBounds="true"
android:padding="5dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_for_sale_outline"
android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"
tools:src="@drawable/ic_for_sale_outline"/>
</FrameLayout>
</layout>
- جمِّع التطبيق وشغِّله، ولاحظ أنّ الأماكن غير المخصّصة للاستئجار تتضمّن رمز علامة الدولار.
يعرض تطبيقك حاليًا جميع خصائص Mars في شبكة النظرة العامة. إذا كان المستخدم يتسوّق بحثًا عن مكان للإيجار على كوكب المريخ، سيكون من المفيد عرض الرموز للإشارة إلى الأماكن المتاحة للبيع، ولكن سيظل هناك الكثير من الأماكن التي يمكن تصفّحها على الصفحة. في هذه المهمة، ستضيف قائمة خيارات إلى جزء النظرة العامة تتيح للمستخدم عرض أماكن للاستئجار فقط أو أماكن للبيع فقط أو عرض جميع الأماكن.
إحدى الطرق التي يمكنك اتّباعها لإنجاز هذه المهمة هي اختبار النوع لكل MarsProperty
في شبكة النظرة العامة وعرض السمات المطابقة فقط. ومع ذلك، تحتوي خدمة الويب الفعلية الخاصة بكوكب المريخ على مَعلمة طلب بحث أو خيار (يُسمى filter
) يتيح لك الحصول على خصائص من النوع rent
أو النوع buy
فقط. يمكنك استخدام طلب البحث هذا مع عنوان URL لخدمة الويب realestate
في متصفّح على النحو التالي:
https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buy
في هذه المهمة، ستعدّل الفئة MarsApiService
لإضافة خيار طلب إلى طلب خدمة الويب باستخدام Retrofit. بعد ذلك، يمكنك ربط قائمة الخيارات بإعادة تنزيل جميع بيانات موقع "المريخ" باستخدام خيار طلب البحث هذا. بما أنّ الردّ الذي تتلقّاه من خدمة الويب لا يحتوي إلا على الخصائص التي تهمّك، لن تحتاج إلى تغيير منطق عرض طريقة العرض لشبكة النظرة العامة على الإطلاق.
الخطوة 1: تعديل خدمة Mars API
لتغيير الطلب، عليك إعادة زيارة فئة MarsApiService
التي نفّذتها في الدرس العملي الأول ضمن هذه السلسلة. يمكنك تعديل الفئة لتوفير واجهة برمجة تطبيقات للفلترة.
- فتح "
network/MarsApiService.kt
" أسفل عمليات الاستيراد مباشرةً، أنشئenum
باسمMarsApiFilter
لتحديد الثوابت التي تتطابق مع قيم طلب البحث التي تتوقّعها خدمة الويب.
enum class MarsApiFilter(val value: String) {
SHOW_RENT("rent"),
SHOW_BUY("buy"),
SHOW_ALL("all") }
- عدِّل طريقة
getProperties()
لتلقّي إدخال سلسلة لطلب الفلتر، وأضِف تعليقًا توضيحيًا إلى هذا الإدخال باستخدام@Query("filter")
، كما هو موضّح أدناه.
استيرادretrofit2.http.Query
عندما يُطلب منك ذلك
تطلب التعليقات التوضيحية@Query
من طريقةgetProperties()
(وبالتالي Retrofit) إرسال طلب خدمة الويب مع خيار الفلتر. في كل مرة يتم فيها استدعاءgetProperties()
، يتضمّن عنوان URL للطلب الجزء?filter=type
، الذي يوجّه خدمة الويب إلى الرد بنتائج تطابق هذا الطلب.
fun getProperties(@Query("filter") type: String):
الخطوة 2: تعديل نموذج عرض النظرة العامة
تطلب البيانات من MarsApiService
في طريقة getMarsRealEstateProperties()
في OverviewViewModel
. عليك الآن تعديل هذا الطلب لتضمين وسيطة الفلتر.
- فتح "
overview/OverviewViewModel.kt
" ستظهر لك أخطاء في Android Studio بسبب التغييرات التي أجريتها في الخطوة السابقة. أضِفMarsApiFilter
(تعداد قيم الفلتر المحتملة) كمعلَمة إلى طلبgetMarsRealEstateProperties()
.
انقر على "استيراد"com.example.android.marsrealestate.network.MarsApiFilter
عندما يُطلب منك ذلك.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
- عدِّل طلب
getProperties()
في خدمة Retrofit لتمرير طلب البحث الخاص بهذا الفلتر كسلسلة.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
- في كتلة
init {}
، مرِّرMarsApiFilter.SHOW_ALL
كوسيط إلىgetMarsRealEstateProperties()
، لعرض جميع الخصائص عند تحميل التطبيق لأول مرة.
init {
getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}
- في نهاية الفئة، أضِف طريقة
updateFilter()
تأخذ وسيطةMarsApiFilter
وتستدعيgetMarsRealEstateProperties()
باستخدام هذه الوسيطة.
fun updateFilter(filter: MarsApiFilter) {
getMarsRealEstateProperties(filter)
}
الخطوة 3: ربط الجزء بقائمة الخيارات
الخطوة الأخيرة هي ربط القائمة الكاملة بالجزء لعرض updateFilter()
في نموذج العرض عندما يختار المستخدم أحد خيارات القائمة.
- فتح "
res/menu/overflow_menu.xml
" يحتوي تطبيق MarsRealEstate على قائمة خيارات إضافية حالية توفّر الخيارات الثلاثة المتاحة: عرض جميع الأماكن المتاحة للاستئجار، وعرض الأماكن المتاحة للاستئجار فقط، وعرض الأماكن المتاحة للبيع فقط.
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/show_all_menu"
android:title="@string/show_all" />
<item
android:id="@+id/show_rent_menu"
android:title="@string/show_rent" />
<item
android:id="@+id/show_buy_menu"
android:title="@string/show_buy" />
</menu>
- فتح "
overview/OverviewFragment.kt
" في نهاية الفئة، نفِّذ طريقةonOptionsItemSelected()
للتعامل مع اختيارات عناصر القائمة.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
}
- في
onOptionsItemSelected()
، استدعِ الدالةupdateFilter()
في نموذج العرض باستخدام الفلتر المناسب. استخدِم كتلةwhen {}
في Kotlin للتبديل بين الخيارات. استخدِمMarsApiFilter.SHOW_ALL
لقيمة الفلتر التلقائية. عليك عرضtrue
لأنّك تعاملت مع عنصر القائمة. استيرادMarsApiFilter
(com.example.android.marsrealestate.network.MarsApiFilter
) عند الطلب يتم عرض طريقةonOptionsItemSelected()
الكاملة أدناه.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
viewModel.updateFilter(
when (item.itemId) {
R.id.show_rent_menu -> MarsApiFilter.SHOW_RENT
R.id.show_buy_menu -> MarsApiFilter.SHOW_BUY
else -> MarsApiFilter.SHOW_ALL
}
)
return true
}
- يمكنك تجميع التطبيق وتشغيله. سيطلق التطبيق شبكة النظرة العامة الأولى التي تتضمّن جميع أنواع الأماكن المتاحة للاستئجار، مع وضع علامة الدولار على الأماكن المتاحة للبيع.
- اختَر استئجار من قائمة الخيارات. تتم إعادة تحميل المواقع ولا يظهر أي منها مع رمز الدولار. (يتم عرض الأماكن المخصّصة للاستئجار فقط). قد تحتاج إلى الانتظار بضع لحظات إلى أن تتم إعادة تحميل العرض ليظهر فقط المواقع التي تمّت فلترتها.
- اختَر شراء من قائمة الخيارات. تتم إعادة تحميل المواقع مرة أخرى، وتظهر جميعها مع رمز الدولار. (يتم عرض العقارات المعروضة للبيع فقط).
أصبح لديك الآن شبكة قابلة للتمرير تضم رموزًا لخصائص المريخ، ولكن حان الوقت للحصول على مزيد من التفاصيل. في هذه المهمة، ستضيف جزء تفاصيل لعرض تفاصيل عقار معيّن. ستعرض جزء التفاصيل صورة أكبر والسعر ونوع العقار، أي ما إذا كان مخصّصًا للاستئجار أو للبيع.
يتم تشغيل هذه الفئة عندما ينقر المستخدم على صورة في شبكة النظرة العامة. لإجراء ذلك، عليك إضافة أداة معالجة onClick
إلى عناصر شبكة RecyclerView
، ثم الانتقال إلى الجزء الجديد. يمكنك التنقّل من خلال إحداث تغيير في LiveData
ViewModel
، كما فعلت طوال هذه الدروس. يمكنك أيضًا استخدام مكوّن Safe Args الإضافي في Navigation لتمرير معلومات MarsProperty
المحدّدة من جزء النظرة العامة إلى جزء التفاصيل.
الخطوة 1: إنشاء نموذج عرض التفاصيل وتعديل تخطيط التفاصيل
على غرار العملية التي استخدمتها لنموذج عرض النظرة العامة والقصاصات، عليك الآن تنفيذ نموذج العرض وملفات التنسيق لقصاصة التفاصيل.
- فتح "
detail/DetailViewModel.kt
" وكما أنّ ملفات Kotlin ذات الصلة بالشبكة مضمّنة في المجلدnetwork
وملفات النظرة العامة فيoverview
، يحتوي المجلدdetail
على الملفات المرتبطة بعرض التفاصيل. لاحظ أنّ الفئةDetailViewModel
(الفارغة حاليًا) تأخذmarsProperty
كمعلَمة في الدالة الإنشائية.
class DetailViewModel( marsProperty: MarsProperty,
app: Application) : AndroidViewModel(app) {
}
- داخل تعريف الفئة، أضِف
LiveData
لسمة Mars المحدّدة، وذلك لعرض هذه المعلومات في طريقة العرض التفصيلية. اتّبِع النمط المعتاد لإنشاءMutableLiveData
يحتوي علىMarsProperty
نفسه، ثمّ اعرض خاصيةLiveData
عامة غير قابلة للتغيير.
استيرادandroidx.lifecycle.LiveData
واستيرادandroidx.lifecycle.MutableLiveData
عند الطلب.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
get() = _selectedProperty
- أنشئ حزمة
init {}
واضبط قيمة سمة Mars المحدّدة باستخدام العنصرMarsProperty
من الدالة الإنشائية.
init {
_selectedProperty.value = marsProperty
}
- افتح
res/layout/fragment_detail.xml
واطّلِع عليه في عرض التصميم.
هذا هو ملف التصميم الخاص بقطعة التفاصيل. تحتوي علىImageView
للصورة الكبيرة، وTextView
لنوع المكان (للاستئجار أو البيع)، وTextView
للسعر. يُرجى العِلم أنّ تخطيط القيود مضمّن فيScrollView
، لذا سيتم التمرير تلقائيًا إذا أصبح العرض كبيرًا جدًا بالنسبة إلى الشاشة، مثلاً عندما يعرضه المستخدم في الوضع الأفقي. - انتقِل إلى علامة التبويب نص للاطّلاع على التنسيق. في أعلى التصميم، قبل العنصر
<ScrollView>
مباشرةً، أضِف العنصر<data>
لربط نموذج عرض التفاصيل بالتصميم.
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
- أضِف السمة
app:imageUrl
إلى العنصرImageView
. اضبطها علىimgSrcUrl
من الموقع المحدّد لنموذج العرض.
سيتم استخدام أداة ربط البيانات التي تحمّل صورة باستخدام Glide تلقائيًا هنا أيضًا، لأنّ هذه الأداة تراقب جميع سماتapp:imageUrl
.
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"
الخطوة 2: تحديد التنقّل في نموذج عرض النظرة العامة
عندما ينقر المستخدم على صورة في نموذج النظرة العامة، يجب أن يؤدي ذلك إلى الانتقال إلى جزء يعرض تفاصيل حول العنصر الذي تم النقر عليه.
- فتح "
overview/OverviewViewModel.kt
" أضِف السمة_navigateToSelectedProperty
MutableLiveData
واعرضها باستخدامLiveData
غير قابل للتغيير.
عندما يتغيّر هذاLiveData
إلى قيمة غير فارغة، يتم تشغيل التنقّل. (ستضيف قريبًا الرمز البرمجي لمراقبة هذا المتغيّر وتفعيل التنقّل).
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
get() = _navigateToSelectedProperty
- في نهاية الفئة، أضِف طريقة
displayPropertyDetails()
التي تضبط _navigateToSelectedProperty
على موقع Mars المحدّد.
fun displayPropertyDetails(marsProperty: MarsProperty) {
_navigateToSelectedProperty.value = marsProperty
}
- أضِف طريقة
displayPropertyDetailsComplete()
التي تضبط قيمة_navigateToSelectedProperty
على القيمة الخالية. تحتاج إلى ذلك لوضع علامة على حالة التنقّل على أنّها مكتملة، ولتجنُّب إعادة تشغيل التنقّل عندما يعود المستخدم من عرض التفاصيل.
fun displayPropertyDetailsComplete() {
_navigateToSelectedProperty.value = null
}
الخطوة 3: إعداد أدوات معالجة النقرات في أداة ربط الشبكة والجزء
- فتح "
overview/PhotoGridAdapter.kt
" في نهاية الصف، أنشئ صفًا مخصّصًاOnClickListener
يأخذ تعبير lambda مع مَعلمةmarsProperty
. داخل الفئة، حدِّد الدالةonClick()
التي تم ضبطها على مَعلمة lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
- انتقِل للأعلى إلى تعريف الفئة
PhotoGridAdapter
، وأضِف السمة الخاصةOnClickListener
إلى الدالة الإنشائية.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
ListAdapter<MarsProperty,
PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
- اجعل الصورة قابلة للنقر من خلال إضافة
onClickListener
إلى عنصر الشبكة في طريقةonBindviewHolder()
. حدِّد أداة معالجة النقرات بين طلباتgetItem() and bind()
.
override fun onBindViewHolder(holder: MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.itemView.setOnClickListener {
onClickListener.onClick(marsProperty)
}
holder.bind(marsProperty)
}
- فتح "
overview/OverviewFragment.kt
" في طريقةonCreateView()
، استبدِل السطر الذي يبدأ السمةbinding.photosGrid.adapter
بالسطر الموضّح أدناه.
يضيف هذا الرمز العنصرPhotoGridAdapter.onClickListener
إلى الدالة الإنشائيةPhotoGridAdapter
، ويستدعيviewModel.displayPropertyDetails()
باستخدام العنصرMarsProperty
الذي تم تمريره. يؤدي ذلك إلى تشغيلLiveData
في نموذج العرض للتنقّل.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})
الخطوة 4: تعديل الرسم البياني للتنقّل وجعل MarsProperty قابلاً للتسلسل
عندما ينقر المستخدم على صورة في شبكة النظرة العامة، يجب أن ينتقل التطبيق إلى جزء التفاصيل وأن يمرّر تفاصيل الموقع المحدّد على كوكب المريخ حتى يتمكّن عرض التفاصيل من عرض هذه المعلومات.
لديك حاليًا أداة معالجة نقرات من PhotoGridAdapter
للتعامل مع النقرة، وطريقة لتفعيل التنقّل من نموذج العرض. ولكن ليس لديك بعد عنصر MarsProperty
يتم تمريره إلى جزء التفاصيل. يمكنك استخدام Safe Args من مكوّن التنقّل.
- فتح "
res/navigation/nav_graph.xml
" انقر على علامة التبويب نص لعرض رمز XML الخاص برسم التنقّل. - داخل العنصر
<fragment>
الخاص بـ "جزء التفاصيل"، أضِف العنصر<argument>
الموضّح أدناه. تتضمّن هذه الوسيطة، التي تُسمّىselectedProperty
، النوعMarsProperty
.
<argument
android:name="selectedProperty"
app:argType="com.example.android.marsrealestate.network.MarsProperty"
/>
- يمكنك تجميع التطبيق. ستظهر لك رسالة خطأ في التنقّل لأنّ
MarsProperty
ليس قابلاً للتسلسل. تتيح واجهةParcelable
إمكانية تسلسل العناصر، ما يتيح نقل بيانات العناصر بين الأجزاء أو الأنشطة. في هذه الحالة، لكي يتم تمرير البيانات داخل العنصرMarsProperty
إلى جزء التفاصيل من خلال Safe Args، يجب أن تنفّذMarsProperty
الواجهةParcelable
. والخبر السار هو أنّ Kotlin توفّر اختصارًا سهلاً لتنفيذ هذه الواجهة. - فتح "
network/MarsProperty.kt
" أضِف التعليق التوضيحي@Parcelize
إلى تعريف الفئة.
استورِدkotlinx.android.parcel.Parcelize
عند الطلب.
يستخدم التعليق التوضيحي@Parcelize
إضافات Kotlin Android لتنفيذ الطرق تلقائيًا في واجهةParcelable
لهذه الفئة. ليس عليك اتّخاذ أي إجراء آخر.
@Parcelize
data class MarsProperty (
- غيِّر تعريف الفئة
MarsProperty
لتوسيعParcelable
.
استورِدandroid.os.Parcelable
عند الطلب.
يبدو تعريف الفئةMarsProperty
الآن على النحو التالي:
@Parcelize
data class MarsProperty (
val id: String,
@Json(name = "img_src") val imgSrcUrl: String,
val type: String,
val price: Double) : Parcelable {
الخطوة 5: ربط الأجزاء
لن تتنقّل بعد، بل سيتم التنقّل الفعلي في الأجزاء. في هذه الخطوة، ستضيف الأجزاء الأخيرة لتنفيذ التنقّل بين الأجزاء الخاصة بالنظرة العامة والتفاصيل.
- فتح "
overview/OverviewFragment.kt
" فيonCreateView()
، أسفل الأسطر التي تهيئ أداة ربط شبكة الصور، أضِف الأسطر الموضّحة أدناه لمراقبةnavigatedToSelectedProperty
من نموذج عرض النظرة العامة.
استيرادandroidx.lifecycle.Observer
واستيرادandroidx.navigation.fragment.findNavController
عند الطلب
يتحقّق المراقب ممّا إذا كانMarsProperty
، أيit
في تعبير lambda، ليس فارغًا، وإذا كان كذلك، يحصل على أداة التحكّم في التنقّل من الجزء الذي يحتوي علىfindNavController()
. استدعِ الدالةdisplayPropertyDetailsComplete()
لإخبار نموذج العرض بإعادة ضبطLiveData
إلى الحالة الفارغة، حتى لا يتم تشغيل التنقّل مرة أخرى عن طريق الخطأ عند عودة التطبيق إلىOverviewFragment
.
viewModel.navigateToSelectedProperty.observe(this, Observer {
if ( null != it ) {
this.findNavController().navigate(
OverviewFragmentDirections.actionShowDetail(it))
viewModel.displayPropertyDetailsComplete()
}
})
- فتح "
detail/DetailFragment.kt
" أضِف هذا السطر أسفل طلبsetLifecycleOwner()
مباشرةً في طريقةonCreateView()
. يحصل هذا السطر على عنصرMarsProperty
المحدّد من Safe Args.
لاحظ استخدام عامل تأكيد عدم القيمة الفارغة في Kotlin (!!
). إذا لم يكنselectedProperty
متوفّرًا، يعني ذلك حدوث خطأ فادح، ويجب أن يعرض الرمز خطأ مؤشر فارغ. (في رمز الإنتاج، يجب التعامل مع هذا الخطأ بطريقة ما).
val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
- أضِف هذا السطر بعد ذلك للحصول على
DetailViewModelFactory
جديدة. ستستخدمDetailViewModelFactory
للحصول على مثيل منDetailViewModel
. يتضمّن التطبيق الأوّلي عملية تنفيذDetailViewModelFactory
، لذا كل ما عليك فعله هنا هو إعداده.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
- أخيرًا، أضِف هذا الخط للحصول على
DetailViewModel
من المصنع وربط جميع الأجزاء.
binding.viewModel = ViewModelProviders.of(
this, viewModelFactory).get(DetailViewModel::class.java)
- يمكنك تجميع التطبيق وتشغيله، ثم النقر على أي صورة لمكان على كوكب المريخ. يظهر جزء التفاصيل الخاص بتفاصيل الموقع. انقر على زر "الرجوع" للعودة إلى صفحة النظرة العامة، ولاحظ أنّ شاشة التفاصيل لا تزال فارغة إلى حدّ ما. يمكنك الانتهاء من إضافة بيانات الموقع إلى صفحة التفاصيل هذه في المهمة التالية.
في الوقت الحالي، تعرض صفحة التفاصيل صورة المريخ نفسها التي اعتدت رؤيتها في صفحة النظرة العامة. يحتوي الصف MarsProperty
أيضًا على نوع الموقع (للاستئجار أو الشراء) وسعر الموقع. يجب أن تتضمّن شاشة التفاصيل كلتا القيمتَين، وسيكون من المفيد أن تشير الأماكن المتاحة للاستئجار إلى أنّ السعر هو قيمة شهرية. يمكنك استخدام عمليات تحويل LiveData
في نموذج العرض لتنفيذ كلّ من هذين الأمرين.
- فتح "
res/values/strings.xml
" يتضمّن الرمز الأولي موارد السلاسل، الموضّحة أدناه، لمساعدتك في إنشاء السلاسل الخاصة بعرض التفاصيل. بالنسبة إلى السعر، ستستخدم المرجعdisplay_price_monthly_rental
أو المرجعdisplay_price
، وذلك حسب نوع السمة.
<string name="type_rent">Rent</string>
<string name="type_sale">Sale</string>
<string name="display_type">For %s</string>
<string name="display_price_monthly_rental">$%,.0f/month</string>
<string name="display_price">$%,.0f</string>
- فتح "
detail/DetailViewModel.kt
" في أسفل الصف، أضِف الرمز البرمجي الموضّح أدناه.
استيرادandroidx.lifecycle.Transformations
إذا طُلب منك ذلك
يختبر هذا التحويل ما إذا كان المكان المحدّد مخصّصًا للاستئجار، وذلك باستخدام الاختبار نفسه من المهمة الأولى. إذا كانت السمة عبارة عن مكان مخصّص للاستئجار، سيختار التحويل السلسلة المناسبة من الموارد باستخدام مفتاح تبديلwhen {}
في Kotlin. يجب أن ينتهي كل من هذين السلسلتين برقم، لذا عليك ربطproperty.price
بعد ذلك.
val displayPropertyPrice = Transformations.map(selectedProperty) {
app.applicationContext.getString(
when (it.isRental) {
true -> R.string.display_price_monthly_rental
false -> R.string.display_price
}, it.price)
}
- استورِد فئة
R
التي تم إنشاؤها للوصول إلى موارد السلسلة في المشروع.
import com.example.android.marsrealestate.R
- بعد عملية التحويل
displayPropertyPrice
، أضِف الرمز البرمجي الموضّح أدناه. يجمع هذا التحويل عدّة موارد سلاسل، استنادًا إلى ما إذا كان نوع المكان المخصّص للاستئجار.
val displayPropertyType = Transformations.map(selectedProperty) {
app.applicationContext.getString(R.string.display_type,
app.applicationContext.getString(
when (it.isRental) {
true -> R.string.type_rent
false -> R.string.type_sale
}))
}
- فتح "
res/layout/fragment_detail.xml
" ما عليك سوى تنفيذ خطوة أخرى، وهي ربط السلاسل الجديدة (التي أنشأتها باستخدام عمليات التحويلLiveData
) بطريقة العرض التفصيلية. لإجراء ذلك، اضبط قيمة الحقل النصي لنوع العقار علىviewModel.displayPropertyType
، والحقل النصي لقيمة السعر علىviewModel.displayPropertyPrice
.
<TextView
android:id="@+id/property_type_text"
...
android:text="@{viewModel.displayPropertyType}"
...
tools:text="To Rent" />
<TextView
android:id="@+id/price_value_text"
...
android:text="@{viewModel.displayPropertyPrice}"
...
tools:text="$100,000" />
- يمكنك تجميع التطبيق وتشغيله. ستظهر الآن جميع بيانات الموقع في صفحة التفاصيل بتنسيق جيد.
مشروع "استوديو Android": MarsRealEstateFinal
تعبيرات الربط
- استخدِم تعبيرات الربط في ملفات تنسيق XML لتنفيذ عمليات برمجية بسيطة، مثل العمليات الرياضية أو الاختبارات الشرطية، على البيانات المرتبطة.
- للإشارة إلى الفئات داخل ملف التصميم، استخدِم العلامة
<import>
داخل العلامة<data>
.
خيارات طلبات البحث في خدمات الويب
- يمكن أن تتضمّن الطلبات المقدَّمة إلى خدمات الويب مَعلمات اختيارية.
- لتحديد مَعلمات الطلب في الطلب، استخدِم التعليق التوضيحي
@Query
في Retrofit.
دورة Udacity التدريبية:
مستندات مطوّري تطبيقات Android:
- نظرة عامة على ViewModel
- نظرة عامة على LiveData
- برامج ربط البيانات
- التصاميم وتعبيرات الربط
- التنقّل
- بدء استخدام "مكوّن التنقّل"
- تمرير البيانات بين وجهات (تتضمّن أيضًا وصفًا لميزة Safe Args)
Transformations
صفViewModelProvider
صفViewModelProvider.Factory
صف
غير ذلك:
يسرد هذا القسم مهامًا منزلية محتملة للطلاب الذين يعملون على هذا الدرس التطبيقي العملي كجزء من دورة تدريبية يقودها مدرّب. على المعلّم تنفيذ ما يلي:
- حدِّد واجبًا منزليًا إذا لزم الأمر.
- توضيح كيفية إرسال الواجبات المنزلية للطلاب
- وضع درجات للواجبات المنزلية
يمكن للمدرّبين استخدام هذه الاقتراحات بالقدر الذي يريدونه، ويجب ألا يترددوا في تكليف الطلاب بأي واجبات منزلية أخرى يرونها مناسبة.
إذا كنت تعمل على هذا الدرس العملي بنفسك، يمكنك استخدام مهام الواجب المنزلي هذه لاختبار معلوماتك.
الإجابة عن هذه الأسئلة
السؤال 1
ما هي وظيفة العلامة <import>
في ملف تصميم XML؟
▢ تضمين ملف تصميم واحد في ملف آخر
▢ تضمين رمز Kotlin داخل ملف التصميم
▢ توفير إمكانية الوصول إلى السمات المرتبطة بالبيانات
▢ السماح لك بالإشارة إلى الصفوف وأفرادها في عبارات الربط
السؤال 2
كيف يمكن إضافة خيار طلب بحث إلى طلب خدمة ويب REST في Retrofit؟
▢ إلحاق طلب البحث بنهاية عنوان URL للطلب
▢ أضِف مَعلمة للطلب إلى الدالة التي تُجري الطلب، وأضِف تعليقًا توضيحيًا إلى هذه المَعلمة باستخدام @Query
.
▢ استخدِم فئة Query
لإنشاء طلب.
▢ استخدِم طريقة addQuery()
في أداة إنشاء Retrofit.
بدء الدرس التالي:
للحصول على روابط تؤدي إلى دروس تطبيقية أخرى في هذه الدورة التدريبية، اطّلِع على الصفحة المقصودة الخاصة بالدروس التطبيقية حول أساسيات Android Kotlin.