Android Kotlin Fundamentals 08.3 סינון ותצוגות מפורטות עם נתוני אינטרנט

Lab Lab זה הוא חלק מקורס Android Kotlin Fundamentals. כדי להפיק את המקסימום מהקורס הזה, יש לפעול ברצף לפי קודי שיעור ה-Lab. כל שיעורי Lab של הקורסים מופיעים בדף הנחיתה של Lab Kotlin Fundamentals ל-Android Lab.

מבוא

במעבדות הקוד הקודמות של השיעור הזה למדת איך לקבל נתונים על נדל"ן ממצפה משירות אינטרנט, ואיך ליצור RecyclerView עם פריסת רשת כדי לטעון ולהציג תמונות מהנתונים האלה. בשיעור ה-Lab הזה מסיימים את אפליקציית MarsRealEstate על ידי יישום היכולת לסנן את הנכסים ב-Mars לפי נכסים להשכרה או לרכישה. בנוסף, אתם יוצרים תצוגת פרטים כך שאם המשתמש מקיש על תמונת נכס בסקירה הכללית, הוא יראה תצוגת פרטים עם פרטים על הנכס הזה.

מה כבר צריך לדעת

  • איך ליצור קטעי קוד ולהשתמש בהם.
  • איך לנווט בין מקטעים ומשתמשים ב-safe Args (פלאגין של Gradle) כדי לעבור בין נתונים
  • כיצד להשתמש ברכיבי ארכיטקטורה, כולל מודלים של תצוגה, מפעלי תצוגה, טרנספורמציות ו-LiveData.
  • איך לאחזר נתונים מקודדים מסוג JSON משירות אינטרנט של REST ולנתח את הנתונים האלה באובייקטים של Kotlin באמצעות הספריות Retrofit ו-Moshi.

מה תלמדו

  • איך להשתמש בביטויים מחייבים מורכבים בקובצי הפריסה.
  • איך לשלוח בקשות רטרופיט לשירות אינטרנט עם אפשרויות שאילתה.

מה צריך לעשות

  • יש לשנות את אפליקציית MarsRealEstate כדי לסמן את הנכסים ב'מאדים' למכירה (לעומת נכסים להשכרה) באמצעות סמל סימן דולר.
  • כדי לפתוח בקשה לשירות אינטרנט, שמסננת את נכסי המאדים לפי סוג, משתמשים בתפריט האפשרויות שבדף הסקירה הכללית.
  • יוצרים מקטע מפורט עבור נכס מאדים, מחברים את הפלח הזה לתצוגת הסקירה הכללית באמצעות ניווט ומעבירים את נתוני הנכס לקטע הזה.

ב-Codelab הזה (ובקודי codelab קשורים) אתם עובדים עם אפליקציה שנקראת MarsRealEstate, שמציגה נכסים למכירה במאדים. האפליקציה הזו מתחברת לשרת אינטרנט כדי לאחזר ולהציג נתוני נכסים, כולל פרטים כמו המחיר והאם הנכס זמין למכירה או להשכרה. התמונות שמייצגות כל נכס הן תמונות מהחיים במאדים שצולמו על ידי רכבי הירח במאדים. ב-codelabs קודמים, יצרתם 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"
},

במשימה הזו, מתחילים לעבוד עם סוג הנכס Mars כדי להוסיף תמונה של סימן דולר לנכסים בדף הסקירה הכללית למכירה.

שלב 1: מעדכנים את נכס המאדים כך שיכלול את הסוג

הסיווג MarsProperty מגדיר את מבנה הנתונים של כל נכס שסופק על ידי שירות האינטרנט. במעבדת קוד קודמת, השתמשתם בספרייה Moshi כדי לנתח את תגובת ה-JSON הגולמית משירות האינטרנט של Mars לאובייקטים ספציפיים של נתונים מסוג MarsProperty.

בשלב זה מוסיפים לוגיקה לכיתה MarsProperty כדי לציין אם נכס מיועד להשכרה או לא (כלומר, אם הסוג הוא המחרוזת "rent" או "buy"). עליכם להשתמש בלוגיקה הזו ביותר ממקום אחד, ולכן עדיף שהיא תוצג כאן בקבוצת הנתונים במקום לשכפל אותה.

  1. פותחים את אפליקציית MarsRealEstate ממעבדת הקוד האחרונה. (אם אין לך את האפליקציה, אפשר להוריד את MarsRealEstateGrid).
  2. פתיחת 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 של פריטי הרשת.

  1. פתיחת res/layout/grid_view_item.xml זהו קובץ הפריסה של כל תא בפריסת רשת של RecyclerView. הקובץ מכיל כרגע רק את הרכיב <ImageView> של תמונת הנכס.
  2. בתוך האלמנט <data>, מוסיפים רכיב <import> לכיתה View. ייבוא משמש כשרוצים להשתמש ברכיבים של כיתה בביטוי לביטוי קישור נתונים בקובץ פריסה. במקרה כזה, צריך להשתמש בקבועים View.GONE ו-View.VISIBLE, ולכן נדרשת גישה למחלקה View.
<import type="android.view.View"/>
  1. צריך להקיף את כל תצוגת התמונה בסימן FrameLayout כדי לאפשר שרטוט של סימן הדולר מעל לתמונה של הנכס.
<FrameLayout
   android:layout_width="match_parent"
   android:layout_height="170dp">
             <ImageView 
                    android:id="@+id/mars_image"
            ...
</FrameLayout>
  1. עבור ImageView, יש לשנות את המאפיין android:layout_height ל-match_parent כדי למלא את הערכים של ההורה החדש FrameLayout.
android:layout_height="match_parent"
  1. יש להוסיף רכיב <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"/>
  1. יש להוסיף את המאפיין android:visibility לתצוגת התמונה mars_property_type. משתמשים בביטוי מחייב כדי לבדוק את סוג הנכס, ומקצים את החשיפה ל-View.GONE (להשכרה) או ל-View.VISIBLE (לרכישה).
 android:visibility="@{property.rental ? View.GONE : View.VISIBLE}"

עד עכשיו ראית רק ביטויים מחייבים בפריסות שמשתמשות במשתנים נפרדים שהוגדרו ברכיב <data>. ביטויי קישור הם עוצמתיים ביותר ומאפשרים לבצע פעולות כמו בדיקות וחישובים מתמטיים בלבד בתוך פריסת ה-XML. במקרה כזה, משתמשים באופרטור "טרינרי" (?:) כדי לבצע בדיקה (האובייקט הזה הוא השכרה?). יש לספק תוצאה אחת כ-true (הסתרה של סמל הדולר עם View.GONE) ותוצאה אחרת כ-false (הצגת הסמל הזה עם 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>
  1. הידור והפעלה של האפליקציה, לתשומת ליבך: נכסים שאינם שכרים מופיעים עם סמל הדולר.

נכון לעכשיו, האפליקציה שלך מציגה את כל מאפייני המאדים ברשת הסקירה הכללית. אם משתמש מחפש נכס להשכרה במאדים, אפשר להשתמש בסמלים כדי לציין אילו מהנכסים הזמינים למכירה הם שימושיים, אבל עדיין יש הרבה נכסים שצריך לגלול בדף. במשימה הזו מוסיפים תפריט אפשרויות לקטע הסקירה הכללית, שמאפשר למשתמש להציג רק נכסים להשכרה, רק נכסים למכירה או להציג הכול.

דרך אחת לבצע את המשימה הזו היא לבדוק את הסוג של כל MarsProperty ברשת הסקירה הכללית ולהציג רק את הנכסים התואמים. עם זאת, לשירות האינטרנט בפועל של Mars יש פרמטר או אפשרות שאילתה (שנקראים filter) שמאפשר לקבל רק מאפיינים מסוג rent או מסוג buy. תוכל להשתמש בשאילתת הסינון הזו עם כתובת האתר של שירות האינטרנט realestate בדפדפן הבא:

https://android-kotlin-fun-mars-server.appspot.com/realestate?filter=buy

במשימה הזו, צריך לשנות את המחלקה MarsApiService כדי להוסיף אפשרות שאילתה לבקשה של שירות אינטרנט עם Retrofit. לאחר מכן, מתחברים מחדש לתפריט האפשרויות כדי להוריד מחדש את כל נתוני הנכסים ב-Mars באמצעות אפשרות השאילתה הזו. מאחר שהתגובה שאתם מקבלים משירות האינטרנט מכילה רק את הנכסים שמעניינים אתכם, אין צורך לשנות כלל את הלוגיקה של תצוגת התצוגה עבור רשת הסקירה הכללית.

שלב 1: עדכון שירות Mars API

כדי לשנות את הבקשה, עליך לחזור לקורס MarsApiService שהוטמע ב-codelab הראשון בסדרה הזו. אתם משנים את הכיתה כדי לספק API לסינון.

  1. פתיחת network/MarsApiService.kt מתחת לייבוא, יוצרים enum בשם MarsApiFilter כדי להגדיר קבועות שתואמות לערכי השאילתה שאליהם שירות האינטרנט מצפה.
enum class MarsApiFilter(val value: String) {
   SHOW_RENT("rent"),
   SHOW_BUY("buy"),
   SHOW_ALL("all") }
  1. יש לשנות את השיטה getProperties() כדי לקבל קלט מחרוזת עבור שאילתת המסנן, ולהוסיף הערות באמצעות @Query("filter"), כפי שמוצג למטה.

    יש לייבא את retrofit2.http.Query כשתופיע הבקשה.

    ההערה ב-@Query מורה לשיטה getProperties() (וכך מבצעת רטרופיט) להגיש את בקשת שירות האינטרנט עם אפשרות הסינון. בכל פעם שמתבצעת קריאה ל-getProperties(), כתובת ה-URL של הבקשה כוללת את החלק ?filter=type, שמפנה את שירות האינטרנט להגיב לתוצאות שתואמות לשאילתה הזו.
fun getProperties(@Query("filter") type: String):  

שלב 2: עדכון מודל תצוגת הסקירה הכללית

ביקשת נתונים מ-MarsApiService בשיטת getMarsRealEstateProperties() ב-OverviewViewModel. עכשיו צריך לעדכן את הבקשה כך שתחול ארגומנט הסינון.

  1. פתיחת overview/OverviewViewModel.kt תבחינו בשגיאות ב-Android Studio עקב השינויים שביצעתם בשלב הקודם. צריך להוסיף MarsApiFilter (המספר הכולל של ערכי המסננים האפשריים) כפרמטר לקריאה ל-getMarsRealEstateProperties().

    ייבוא של com.example.android.marsrealestate.network.MarsApiFilter לפי בקשה.
private fun getMarsRealEstateProperties(filter: MarsApiFilter) {
  1. יש לשנות את הקריאה ל-getProperties() בשירות Retrofit כדי להעביר את שאילתת הסינון הזו כמחרוזת.
var getPropertiesDeferred = MarsApi.retrofitService.getProperties(filter.value)
  1. בבלוק init {}, יש להעביר את MarsApiFilter.SHOW_ALL כארגומנט אל getMarsRealEstateProperties() כדי להציג את כל הנכסים בפעם הראשונה שהאפליקציה נטענת.
init {
   getMarsRealEstateProperties(MarsApiFilter.SHOW_ALL)
}
  1. בסוף הכיתה, מוסיפים שיטה updateFilter() שמובילה לארגומנט MarsApiFilter וקוראים getMarsRealEstateProperties() לארגומנט הזה.
fun updateFilter(filter: MarsApiFilter) {
   getMarsRealEstateProperties(filter)
}

שלב 3: מחברים את המקטע לתפריט האפשרויות

השלב האחרון הוא לחבר את תפריט האפשרויות הנוספות לקטע הקוד כדי להפעיל את updateFilter() בדגם התצוגה המפורטת כשהמשתמש בוחר אפשרות בתפריט.

  1. פתיחת 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>
  1. פתיחת overview/OverviewFragment.kt בסוף הכיתה, צריך ליישם את השיטה onOptionsItemSelected() כדי לבחור פריטים בתפריט.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
} 
  1. ב-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
}
  1. הידור והפעלה של האפליקציה. האפליקציה מפעילה את רשת הסקירה הכללית הראשונה עם כל סוגי הנכסים והנכסים למכירה המסומנים בסמל הדולר.
  2. בוחרים באפשרות השכרה בתפריט האפשרויות. המאפיינים נטענים מחדש ואף אחד מהם לא מופיע עם סמל הדולר. (מוצגים רק נכסים להשכרה). ייתכן שתצטרכו להמתין מספר דקות עד שהמסך יתרענן כדי להציג רק את הנכסים המסוננים.
  3. בוחרים באפשרות קנייה מתפריט האפשרויות. הנכסים נטענים מחדש וכולם מופיעים עם סמל הדולר. (מוצגים רק נכסים למכירה).

עכשיו מופיעה רשת גלילה של סמלים עבור נכסים של מאדים, אבל הגיע הזמן לקבל פרטים נוספים. במשימה הזו מוסיפים מקטעים של פרטים כדי להציג את הפרטים של נכס ספציפי. קטע המידע יציג תמונה גדולה יותר, מחיר וסוג הנכס — בין אם מדובר בהשכרה או למכירה.

קטע זה מופעל כשהמשתמש מקיש על תמונה ברשת הסקירה הכללית. כדי לבצע זאת, עליך להוסיף פונקציות מסוג listener של onClick לפריטים ברשת RecyclerView. לאחר מכן עליך לעבור אל הקטע החדש. הניווט על ידי הפעלת שינוי של LiveData בViewModel, כפי שעשית בקורסים האלה. אפשר גם להשתמש בפלאגין 'אזורים בטוחים' של רכיב הניווט כדי להעביר את המידע על MarsProperty שנבחר מקטע הסקירה הכללית לקטע הקוד של הפרטים.

שלב 1: יוצרים את מודל תצוגת הפרטים ומעדכנים את פריסת הפרטים

בדומה לתהליך שבו נעשה שימוש במודל הסקירה הכללית ובמודלי התצוגה, כך אתם צריכים להטמיע עכשיו את מודל התצוגה ואת קובצי הפריסה עבור קטע הקוד.

  1. פתיחת detail/DetailViewModel.kt בדיוק כמו שקובצי Kotlin הקשורים לרשת כלולים בתיקייה network ובקובצי הסקירה הכללית ב-overview, כך התיקייה detail מכילה את הקבצים המשויכים לתצוגת הפרטים. לידיעתך, מחלקה DetailViewModel (ריקה כרגע) מקבלת את הערך marsProperty כפרמטר בבנייה.
class DetailViewModel( marsProperty: MarsProperty,
                     app: Application) : AndroidViewModel(app) {
}
  1. בתוך הגדרת הכיתה, יש להוסיף את LiveData לנכס המאדים שנבחר, כדי לחשוף את המידע הזה בתצוגת הפרטים. יש לפעול לפי הדפוס הרגיל של יצירת MutableLiveData כדי לשמור את ה-MarsProperty עצמו ולאחר מכן לחשוף את הנכס הגלוי לכול של LiveData.

    יש לייבא את androidx.lifecycle.LiveData ולייבא את androidx.lifecycle.MutableLiveData לפי בקשה.
private val _selectedProperty = MutableLiveData<MarsProperty>()
val selectedProperty: LiveData<MarsProperty>
   get() = _selectedProperty
  1. יש ליצור בלוק מסוג init {} ולהגדיר את הערך של מאפיין ה-Mars שנבחר באמצעות אובייקט MarsProperty מהבונה.
    init {
        _selectedProperty.value = marsProperty
    }
  1. פותחים את res/layout/fragment_detail.xml ומחפשים את התמונה בתצוגת העיצוב.

    זהו קובץ הפריסה של קטע הקוד. הוא מכיל ImageView עבור התמונה הגדולה, TextView עבור סוג הנכס (השכרה או מכירה) ו-TextView עבור המחיר. לתשומת ליבכם, פריסת האילוץ מוקפת ב-ScrollView כך שהיא תיגלל באופן אוטומטי אם התצוגה גדולה מדי לתצוגה, למשל כשהמשתמש צופה בה במצב פריסה לרוחב.
  2. נכנסים לכרטיסיית טקסט לפריסה. בחלק העליון של הפריסה, ממש לפני הרכיב <ScrollView>, מוסיפים רכיב <data> כדי לשייך את המודל של תצוגת הפרטים לפריסה.
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
  1. יש להוסיף את המאפיין app:imageUrl לאלמנט ImageView. יש להגדיר אותו כ-imgSrcUrl מהנכס שהוגדר בדגם התצוגה המפורטת.

    בנוסף, גם המתאם המחייב שטוען תמונה באמצעות החלקה ישתמש כאן באופן אוטומטי כי המתאם צופה בכל המאפיינים של app:imageUrl.
 app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"

שלב 2: הגדרת ניווט במודל תצוגת הסקירה הכללית

כאשר המשתמש מקיש על תמונה במודל הסקירה הכללית, הוא צריך לנווט לניווט אל שבר שמציג פרטים על הפריט שנלחץ.

  1. פתיחת overview/OverviewViewModel.kt יש להוסיף נכס MutableLiveData ב-_navigateToSelectedProperty ולחשוף אותו עם LiveData שלם.

    כשה-LiveData הזה משתנה לערך null, הניווט מופעל. (בקרוב תוסיפו את הקוד כדי לבחון את המשתנה הזה ולהפעיל את הניווט).
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
   get() = _navigateToSelectedProperty
  1. בסוף הכיתה, מוסיפים שיטת displayPropertyDetails() שמגדירה _navigateToSelectedProperty לנכס המאדים שנבחר.
fun displayPropertyDetails(marsProperty: MarsProperty) {
   _navigateToSelectedProperty.value = marsProperty
}
  1. יש להוסיף שיטה displayPropertyDetailsComplete() שמחזירה את הערך של _navigateToSelectedProperty. הפעולה הזו נדרשת כדי לסמן את מצב הניווט, וכדי שהניווט לא יופעל שוב כשהמשתמש יחזור מתצוגת הפרטים.
fun displayPropertyDetailsComplete() {
   _navigateToSelectedProperty.value = null
}

שלב 3: הגדרת מאזינים לקליקים במתאם ובמקטע הרשת

  1. פתיחת overview/PhotoGridAdapter.kt בסוף הכיתה, יוצרים כיתה מותאמת אישית ב-OnClickListener של למדה עם פרמטר marsProperty. בתוך הכיתה, מגדירים פונקציית onClick() שמוגדרת כפרמטר lambda.
class OnClickListener(val clickListener: (marsProperty:MarsProperty) -> Unit) {
     fun onClick(marsProperty:MarsProperty) = clickListener(marsProperty)
}
  1. יש לגלול למעלה אל הגדרת הכיתה עבור PhotoGridAdapter, ולהוסיף לנכס נכס OnClickListener פרטי.
class PhotoGridAdapter( private val onClickListener: OnClickListener ) :
       ListAdapter<MarsProperty,              
           PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
  1. כדי להפוך תמונה לזמינה, יש להוסיף את 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)
}
  1. פתיחת overview/OverviewFragment.kt בשיטה onCreateView(), צריך להחליף את השורה שמפעילה את הנכס של binding.photosGrid.adapter בשורה שמוצגת למטה.

    הקוד הזה מוסיף את האובייקט PhotoGridAdapter.onClickListener לבנייה של PhotoGridAdapter, ומתקשר אל viewModel.displayPropertyDetails() עם האובייקט של MarsProperty שהועבר. הפעולה הזו תפעיל את LiveData במודל התצוגה של הניווט.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
   viewModel.displayPropertyDetails(it)
})

שלב 4: משנים את תרשים הניווט והופכים את הנכס של Mars לנכס משותף

כשמשתמש מקיש על תמונה בתצוגת הסקירה הכללית, האפליקציה צריכה לעבור לקטע הפרטים ולעבור על הפרטים של נכס Mars שנבחר כדי שתצוגת הפרטים תוכל להציג את המידע הזה.

כרגע יש לך event listener מ-PhotoGridAdapter לטיפול בהקשה, ודרך להפעיל את הניווט ממודל התצוגה. אך עדיין אין לך אובייקט MarsProperty מועבר לקטע המידע. לשם כך, אתם משתמשים בפקודות בטוחה מרכיב הניווט.

  1. פתיחת res/navigation/nav_graph.xml לוחצים על הכרטיסייה טקסט כדי להציג את קוד ה-XML של תרשים הניווט.
  2. בתוך האלמנט <fragment> עבור קטע הקוד, מוסיפים את הרכיב <argument> שמוצג למטה. הארגומנט הזה, בשם selectedProperty, מכיל את הסוג MarsProperty.
<argument
   android:name="selectedProperty"
   app:argType="com.example.android.marsrealestate.network.MarsProperty"
   />
  1. הידור של האפליקציה. הניווט מציג שגיאה כי MarsProperty לא זמין להורדה. הממשק Parcelable מאפשר סידור של אובייקטים כך שהאובייקטים יוכלו לעבור בין מקטעים או פעילויות. במקרה זה, כדי שהנתונים בתוך אובייקט MarsProperty יועברו לקטע הקוד של הפרטים באמצעות בטוח, הארגומנטים של MarsProperty חייבים להטמיע את הממשק Parcelable. החדשות הטובות הן ש-Kotlin מספקת קיצור דרך קל ליישום הממשק הזה.
  2. פתיחת network/MarsProperty.kt צריך להוסיף את ההערה @Parcelize להגדרת הכיתה.

    כשמייבאים בקשה kotlinx.android.parcel.Parcelize, מייבאים אותה.

    ההערה @Parcelize משתמשת בתוספים של Kotlin ל-Android כדי להטמיע באופן אוטומטי את השיטות בממשק Parcelable של הכיתה הזו. אין צורך לבצע פעולה נוספת!
@Parcelize
data class MarsProperty (
  1. יש לשנות את הגדרת הכיתה 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: חיבור המקטעים

עדיין אינכם מנווטים – הניווט בפועל מקוטע. בשלב זה מוסיפים את הביטים האחרונים ליישום הניווט בין הסקירה הכללית לבין קטעי הקוד של הפרטים.

  1. פתיחת overview/OverviewFragment.kt ב-onCreateView(), מתחת לשורות שמפעילים את המתאם של רשת התמונות, יש להוסיף את השורות שבהמשך כדי לראות את navigatedToSelectedProperty מהמודל של תצוגת הסקירה הכללית.

    יש לייבא את androidx.lifecycle.Observer ולייבא את androidx.navigation.fragment.findNavController לפי בקשה.

    הצופה בודק אם MarsPropertyit בלאמדה – לא Null, ואם כן, הוא מקבל את בקר הניווט מהמקטע באמצעות findNavController(). יש להתקשר אל displayPropertyDetailsComplete() כדי לבקש ממודל התצוגה לאפס את LiveData למצב null, כדי לא להפעיל שוב את הניווט בטעות כשהאפליקציה תחזיר אל OverviewFragment.
viewModel.navigateToSelectedProperty.observe(this, Observer {
   if ( null != it ) {   
      this.findNavController().navigate(
              OverviewFragmentDirections.actionShowDetail(it))             
      viewModel.displayPropertyDetailsComplete()
   }
})
  1. פתיחת detail/DetailFragment.kt יש להוסיף את השורה הזו מתחת לקריאה ב-setLifecycleOwner() בשיטה onCreateView(). שורה זו מקבלת את האובייקט MarsProperty שנבחר מהקבוצות הבטוחות.

    יש לשים לב לשימוש באופרטור ההצהרה Kotlin' (!!). אם selectedProperty לא שם, משהו נורא קרה ולמעשה ברצונך להעביר קוד מצביע. (בקוד הייצור, צריך לטפל בדרך זו בשגיאה).
 val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
  1. יש להוסיף את השורה הזו כדי לקבל DetailViewModelFactory חדש. צריך להשתמש בפונקציה DetailViewModelFactory כדי לקבל מופע של DetailViewModel. האפליקציה למתחילים כוללת הטמעה של DetailViewModelFactory, לכן צריך לבצע אותה רק כאן.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
  1. לבסוף, יש להוסיף את השורה הזו כדי לקבל DetailViewModel מהמפעל ולחבר את כל החלקים.
      binding.viewModel = ViewModelProviders.of(
                this, viewModelFactory).get(DetailViewModel::class.java)
  1. הידור והפעלה של האפליקציה והקש על תמונה של נכס Mars. קטע הקוד מופיע עבור פרטי הנכס הזה. מקישים על הלחצן 'הקודם' כדי לחזור לדף הסקירה הכללית ושמים לב שמסך הפרטים עדיין מעט צר. במשימה הבאה מסיימים להוסיף את נתוני הנכסים לדף הפרטים.

כרגע, דף הפרטים מציג רק את אותה תמונה של מאדים שאתם רגילים לראות בדף הסקירה הכללית. לקטגוריה MarsProperty יש גם סוג נכס (השכרה או קנייה) ומחיר נכס. מסך הפרטים צריך לכלול את שני הערכים האלה, ומועיל אם הנכסים להשכרה ציינו שהמחיר היה ערך לחודש. LiveData משתמש בטרנספורמציות במודל התצוגה כדי ליישם את שני הדברים האלה.

  1. פתיחת 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>
  1. פתיחת 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)
}
  1. אפשר לייבא את הכיתה R שנוצרה כדי לקבל גישה למשאבי המחרוזת בפרויקט.
import com.example.android.marsrealestate.R
  1. לאחר השינוי של 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
                   }))
}
  1. פתיחת 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" />
  1. הידור והפעלה של האפליקציה. עכשיו כל נתוני הנכס מופיעים בדף הפרטים, בפורמט יפה.

פרויקט ב-Android Studio: MarsRealEstateFinal

ביטויי קישור

  • שימוש בביטויי קישור בקובצי פריסת XML לביצוע פעולות פרוגרמטיות פשוטות, כמו בדיקות מתמטיות או בדיקות מותנות, בנתוני איגוד.
  • כדי להפנות לכיתות בקובץ הפריסה, יש להשתמש בתג <import> שבתג <data>.

אפשרויות שאילתה של שירות אינטרנט

  • בקשות לשירותי אינטרנט יכולות לכלול פרמטרים אופציונליים.
  • כדי לציין פרמטרים של שאילתות בבקשה, צריך להשתמש בהערה @Query בקטע Retrofit.

קורס אוניברסיטה:

התיעוד של מפתח Android:

אחר:

בקטע הזה מפורטות מטלות שיעורי בית אפשריות לתלמידים שעובדים עם קוד Lab הזה, במסגרת קורס בהדרכת מורה. למורה יש אפשרות לבצע את הפעולות הבאות:

  • אם צריך, מקצים שיעורי בית.
  • ספרו לתלמידים איך מגישים מטלות בשיעורי בית.
  • לתת ציונים למטלות שיעורי הבית.

המורים יכולים להשתמש בהצעות האלה כמה שפחות, ומומלץ להקצות להן כל שיעורי בית שדעתם מתאימה להם.

אם אתם עובדים בעצמכם על שיעור הקוד הזה, אתם מוזמנים להשתמש במטלות שיעורי הבית האלה כדי לבחון את הידע שלכם.

מענה על השאלות הבאות

שאלה 1

מה עושה התג <import> בקובץ פריסת XML?

▬ לכלול קובץ פריסה אחד בקובץ אחר.

▸ הטמעת קוד Kotlin בקובץ הפריסה.

הוספה צריך לספק גישה לנכסים שקשורים לנתונים.

הוספה כאן אפשר להפנות לכיתות ולחברים בכיתה באמצעות ביטויים מחייבים.

שאלה 2

איך מוסיפים אפשרות לשאילתה בשיחה עם שירות אינטרנט של REST ב-Retrofit?

הוספה יש להוסיף את השאילתה לסוף כתובת ה-URL של הבקשה.

הוספה צריך להוסיף פרמטר לשאילתה כדי להפעיל את הבקשה, ולהוסיף הערות לפרמטר הזה באמצעות @Query.

הוספה יש להשתמש בכיתה Query כדי ליצור בקשה.

הוספה יש להשתמש בשיטה addQuery() בכלי ליצירת רטרופיט.

להשתתפות בשיעור הבא: 9.1: מאגר

קישורים למעבדות אחרות של הקוד בקורס הזה זמינים בדף הנחיתה של Android Kotlin Fundamentals Codelabs.