Android Kotlin Fundamentals 08.3 فیلتر کردن و نمایش جزئیات با داده های اینترنتی

این کد لبه بخشی از دوره آموزشی Android Kotlin Fundamentals است. اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. همه کدهای دوره در صفحه فرود کد لبه های کدهای Android Kotlin Fundamentals فهرست شده اند.

مقدمه

در کدهای قبلی برای این درس، یاد گرفتید که چگونه داده‌های مربوط به املاک و مستغلات در مریخ را از یک سرویس وب دریافت کنید، و چگونه یک RecyclerView با طرح‌بندی شبکه‌ای برای بارگیری و نمایش تصاویر از آن داده‌ها ایجاد کنید. در این نرم‌افزار، برنامه MarsRealEstate را با اجرای قابلیت فیلتر کردن ویژگی‌های Mars بر اساس اینکه آیا برای اجاره یا خرید در دسترس هستند، به پایان می‌رسانید. شما همچنین یک نمای جزئیات ایجاد می کنید تا اگر کاربر روی عکس ملک در نمای کلی ضربه بزند، یک نمای جزئیات با جزئیات مربوط به آن ویژگی را ببیند.

آنچه از قبل باید بدانید

  • نحوه ایجاد و استفاده از قطعات
  • نحوه پیمایش بین قطعات و استفاده از Safe Args (یک پلاگین Gradle) برای انتقال داده ها بین قطعات.
  • نحوه استفاده از مولفه‌های معماری از جمله مدل‌های مشاهده، کارخانه‌های مشاهده مدل، تبدیل‌ها و LiveData .
  • نحوه بازیابی داده های رمزگذاری شده JSON از یک وب سرویس REST و تجزیه آن داده ها به اشیاء Kotlin با کتابخانه های Retrofit و Moshi .

چیزی که یاد خواهید گرفت

  • نحوه استفاده از عبارات الزام آور پیچیده در فایل های طرح بندی.
  • نحوه درخواست Retrofit به یک وب سرویس با گزینه های پرس و جو.

کاری که خواهی کرد

  • برنامه MarsRealEstate را تغییر دهید تا دارایی های مریخ را که برای فروش هستند (در مقابل آنهایی که برای اجاره هستند) با نماد علامت دلار علامت گذاری کنید.
  • از منوی گزینه ها در صفحه نمای کلی برای ایجاد یک درخواست وب سرویس استفاده کنید که ویژگی های Mars را بر اساس نوع فیلتر می کند.
  • یک قطعه جزئیات برای یک ویژگی مریخ ایجاد کنید، آن قطعه را با ناوبری به شبکه نمای کلی متصل کنید و داده های ویژگی را به آن قطعه ارسال کنید.

در این کد لبه (و کدهای مرتبط) با اپلیکیشنی به نام MarsRealEstate کار می کنید که املاکی را برای فروش در مریخ نشان می دهد. این برنامه برای بازیابی و نمایش داده های ملک، از جمله جزئیاتی مانند قیمت و اینکه آیا ملک برای فروش یا اجاره موجود است یا خیر، به یک سرور اینترنتی متصل می شود. تصاویری که هر ملک را نشان می دهد، عکس های واقعی از مریخ است که از مریخ نوردهای ناسا گرفته شده است. در کدهای قبلی، یک RecyclerView با طرح شبکه ای برای تمام عکس های دارایی ایجاد کردید:

در این نسخه از برنامه، شما با نوع ملک (اجاره در مقابل خرید) کار می کنید و یک نماد به طرح شبکه اضافه می کنید تا املاکی را که برای فروش هستند علامت گذاری کنید:

شما منوی گزینه برنامه را تغییر می دهید تا شبکه را فیلتر کند تا فقط دارایی هایی را که برای اجاره یا فروش هستند نشان دهد:

و در نهایت، یک نمای جزئیات برای یک ویژگی جداگانه ایجاد می‌کنید و نمادهای روی شبکه نمای کلی را با ناوبری به آن قطعه جزئیات متصل می‌کنید:

تاکنون، تنها بخشی از داده‌های دارایی مریخ که استفاده کرده‌اید، نشانی اینترنتی تصویر دارایی است. اما داده‌های دارایی - که در کلاس 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 را به‌روزرسانی کنید تا نوع آن را در بر بگیرد

کلاس MarsProperty ساختار داده را برای هر ویژگی ارائه شده توسط وب سرویس تعریف می کند. در آزمایشگاه کد قبلی، از کتابخانه Moshi برای تجزیه پاسخ JSON خام از وب سرویس Mars به ​​اشیاء داده MarsProperty استفاده کردید.

در این مرحله، شما مقداری منطق به کلاس MarsProperty اضافه می‌کنید تا مشخص کنید که آیا یک ملک برای اجاره است یا نه (یعنی اینکه نوع آن رشته "rent" یا "buy" است). شما از این منطق در بیش از یک مکان استفاده خواهید کرد، بنابراین بهتر است آن را در اینجا در کلاس داده داشته باشید تا اینکه آن را تکرار کنید.

  1. برنامه MarsRealEstate را از آخرین کد لبه باز کنید. (اگر برنامه را ندارید می توانید MarsRealEstateGrid را دانلود کنید.)
  2. network/MarsProperty.kt باز کنید. یک بدنه به تعریف کلاس MarsProperty اضافه کنید و یک گیرنده سفارشی برای isRental اضافه کنید که اگر شی از نوع "rent" true را برمی گرداند.
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 اضافه کنید. هنگامی که می خواهید از اجزای یک کلاس در داخل یک عبارت اتصال داده در یک فایل طرح بندی استفاده کنید، از import استفاده می کنید. در این حالت، شما از ثابت های 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 را به FrameLayout تغییر دهید تا match_parent والد جدید پر شود.
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> استفاده می‌کنند. عبارات Binding بسیار قدرتمند هستند و شما را قادر می سازند تا عملیاتی مانند تست ها و محاسبات ریاضی را به طور کامل در طرح XML خود انجام دهید. در این حالت، از عملگر سه تایی ( ?: ) برای انجام تست استفاده می کنید (آیا این شی یک اجاره است؟). شما یک نتیجه برای درست (پنهان کردن نماد علامت دلار با 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. برنامه را کامپایل و اجرا کنید و توجه داشته باشید که املاکی که اجاره ای نیستند نماد علامت دلار را دارند.

در حال حاضر برنامه شما تمام ویژگی های Mars را در شبکه نمای کلی نمایش می دهد. اگر کاربر در حال خرید ملک اجاره ای در مریخ باشد، داشتن نمادهایی برای نشان دادن اینکه کدام یک از املاک موجود برای فروش هستند مفید خواهد بود، اما هنوز املاک زیادی برای پیمایش در صفحه وجود دارد. در این کار، یک منوی گزینه‌ها را به بخش نمای کلی اضافه می‌کنید که به کاربر امکان می‌دهد فقط موارد اجاره‌ای، فقط دارایی‌های برای فروش را نشان دهد یا همه را نشان دهد.

یکی از راه هایی که می توانید این کار را انجام دهید این است که نوع هر MarsProperty را در شبکه نمای کلی آزمایش کنید و فقط ویژگی های تطبیق را نمایش دهید. با این حال، وب سرویس واقعی Mars دارای یک پارامتر یا گزینه پرس و جو (به نام filter ) است که به شما امکان می دهد فقط دارایی هایی از نوع rent یا buy را دریافت کنید. می توانید از این پرس و جو فیلتر با URL سرویس وب realestate در مرورگری مانند زیر استفاده کنید:

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

در این کار، کلاس MarsApiService را تغییر می دهید تا با Retrofit یک گزینه query به درخواست وب سرویس اضافه کنید. سپس منوی گزینه‌ها را وصل می‌کنید تا همه داده‌های ویژگی Mars را با استفاده از آن گزینه query دوباره دانلود کنید. از آنجایی که پاسخی که از وب سرویس دریافت می‌کنید فقط شامل ویژگی‌هایی است که به آن‌ها علاقه دارید، اصلاً نیازی به تغییر منطق نمایش نمایش برای شبکه نمای کلی ندارید.

مرحله 1: سرویس Mars API را به روز کنید

برای تغییر درخواست، باید دوباره کلاس MarsApiService را که در اولین کد لبه این سری پیاده سازی کردید، مشاهده کنید. شما کلاس را برای ارائه یک 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 (و در نتیجه Retrofit) می گوید که درخواست وب سرویس را با گزینه فیلتر انجام دهد. هر بار که getProperties() فراخوانی می شود، URL درخواست شامل بخش ?filter=type می شود که به وب سرویس هدایت می کند تا با نتایجی مطابق با آن پرس و جو پاسخ دهد.
fun getProperties(@Query("filter") type: String):  

مرحله 2: مدل نمای کلی را به روز کنید

شما اطلاعاتی را از MarsApiService در getMarsRealEstateProperties() در OverviewViewModel درخواست می کنید. اکنون باید آن درخواست را به روز کنید تا آرگومان فیلتر را بگیرید.

  1. overview/OverviewViewModel.kt را باز کنید. به دلیل تغییراتی که در مرحله قبل انجام دادید، در اندروید استودیو خطاهایی را مشاهده خواهید کرد. 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() در مدل view زمانی است که کاربر یک گزینه منو را انتخاب می کند.

  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() را در مدل view با فیلتر مناسب فراخوانی کنید. برای جابه‌جایی بین گزینه‌ها از یک Kotlin در when {} استفاده کنید. برای مقدار فیلتر پیش فرض از 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. از منوی گزینه ها خرید را انتخاب کنید. ویژگی ها دوباره بارگیری می شوند و همه آنها با نماد دلار ظاهر می شوند. (فقط املاک برای فروش نشان داده شده است.)

اکنون یک شبکه پیمایشی از نمادها برای ویژگی‌های مریخ دارید، اما زمان آن رسیده که جزئیات بیشتری را دریافت کنید. در این کار، شما یک قطعه جزئیات را برای نمایش جزئیات یک ویژگی خاص اضافه می کنید. قطعه جزئیات تصویر بزرگتر، قیمت و نوع ملک را نشان می دهد - خواه اجاره باشد یا برای فروش.

این قطعه زمانی راه اندازی می شود که کاربر روی یک تصویر در شبکه نمای کلی ضربه می زند. برای انجام این کار، باید یک شنونده onClick را به موارد شبکه RecyclerView اضافه کنید و سپس به قطعه جدید بروید. همانطور که در طول این درس ها انجام داده اید، با ایجاد تغییر LiveData در ViewModel حرکت می کنید. همچنین از افزونه Safe Args مؤلفه Navigation برای انتقال اطلاعات MarsProperty انتخاب شده از قسمت نمای کلی به قطعه جزئیات استفاده می کنید.

مرحله 1: مدل نمای جزئیات را ایجاد کنید و طرح جزئیات را به روز کنید

مشابه فرآیندی که برای مدل نمای کلی و قطعات استفاده کردید، اکنون باید مدل view و فایل های چیدمان را برای قطعه جزئیات پیاده سازی کنید.

  1. detail/DetailViewModel.kt را باز کنید. همانطور که فایل‌های Kotlin مربوط به network در پوشه شبکه و فایل‌های مرور کلی در overview وجود دارند، پوشه detail حاوی فایل‌های مرتبط با نمای جزئیات است. توجه داشته باشید که کلاس DetailViewModel (در حال حاضر خالی است) یک marsProperty را به عنوان پارامتر در سازنده می گیرد.
class DetailViewModel( marsProperty: MarsProperty,
                     app: Application) : AndroidViewModel(app) {
}
  1. در داخل تعریف کلاس، LiveData را برای ویژگی انتخاب شده Mars اضافه کنید تا آن اطلاعات در نمای جزئیات نمایش داده شود. از الگوی معمول ایجاد 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. برای چیدمان به تب Text بروید. در بالای طرح، درست قبل از عنصر <ScrollView> ، یک عنصر <data> اضافه کنید تا مدل نمای جزئیات را با طرح بندی مرتبط کنید.
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>
  1. ویژگی app:imageUrl را به عنصر ImageView اضافه کنید. آن را روی imgSrcUrl از ویژگی انتخاب شده مدل view تنظیم کنید.

    آداپتور صحافی که یک تصویر را با استفاده از Glide بارگیری می کند، به طور خودکار در اینجا نیز استفاده می شود، زیرا آن آداپتور تمام ویژگی های app:imageUrl را تماشا می کند.
 app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"

مرحله 2: مسیریابی را در مدل نمای کلی تعریف کنید

وقتی کاربر روی عکسی در مدل نمای کلی ضربه می‌زند، باید به قسمتی پیمایش کند که جزئیات مورد کلیک شده را نشان می‌دهد.

  1. overview/OverviewViewModel.kt را باز کنید. یک _navigateToSelectedProperty MutableLiveData اضافه کنید و آن را با یک LiveData غیرقابل تغییر در معرض دید قرار دهید.

    وقتی این LiveData به غیر تهی تغییر می کند، ناوبری فعال می شود. (به زودی کدی را برای مشاهده این متغیر و فعال کردن پیمایش اضافه خواهید کرد.)
private val _navigateToSelectedProperty = MutableLiveData<MarsProperty>()
val navigateToSelectedProperty: LiveData<MarsProperty>
   get() = _navigateToSelectedProperty
  1. در پایان کلاس، یک متد displayPropertyDetails() اضافه کنید که _ navigateToSelectedProperty را به ویژگی Mars انتخاب شده تنظیم می کند.
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 در مدل view برای ناوبری فعال می شود.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
   viewModel.displayPropertyDetails(it)
})

مرحله 4: نمودار ناوبری را تغییر دهید و MarsProperty را قابل بسته بندی کنید

وقتی کاربر روی عکسی در شبکه نمای کلی ضربه می‌زند، برنامه باید به قسمت جزئیات پیمایش کند و از جزئیات ویژگی انتخابی Mars عبور کند تا نمای جزئیات بتواند آن اطلاعات را نمایش دهد.

در حال حاضر شما یک شنونده کلیک از PhotoGridAdapter برای کنترل ضربه و راهی برای راه اندازی ناوبری از مدل view دارید. اما شما هنوز یک شی MarsProperty را ندارید که به بخش جزئیات ارسال شود. برای این کار از Safe Args از مؤلفه ناوبری استفاده می کنید.

  1. res/navigation/nav_graph.xml کنید. برای مشاهده کد XML برای نمودار ناوبری، روی تب Text کلیک کنید.
  2. داخل عنصر <fragment> برای قطعه جزئیات، عنصر <argument> را که در زیر نشان داده شده است اضافه کنید. این آرگومان که selectedProperty نام دارد دارای نوع MarsProperty است.
<argument
   android:name="selectedProperty"
   app:argType="com.example.android.marsrealestate.network.MarsProperty"
   />
  1. برنامه را کامپایل کنید. Navigation به شما یک خطا می دهد زیرا MarsProperty قابل حمل نیست. رابط Parcelable اشیا را قادر می سازد تا سریال شوند، به طوری که داده های اشیاء را می توان بین قطعات یا فعالیت ها منتقل کرد. در این حالت، برای اینکه داده‌های داخل شی MarsProperty از طریق Safe Args به قطعه جزئیات ارسال شوند، MarsProperty باید رابط Parcelable را پیاده‌سازی کند. خبر خوب این است که کاتلین یک میانبر آسان برای پیاده سازی آن رابط فراهم می کند.
  2. network/MarsProperty.kt باز کنید. حاشیه نویسی @Parcelize را به تعریف کلاس اضافه کنید.

    در صورت درخواست، kotlinx.android.parcel.Parcelize وارد کنید.

    حاشیه نویسی @Parcelize از برنامه های افزودنی اندروید Kotlin برای پیاده سازی خودکار متدها در رابط 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 در صورت درخواست وارد کنید.

    مشاهده‌گر آزمایش می‌کند که آیا MarsProperty - it در لامبدا - تهی نیست، و اگر چنین است، کنترل‌کننده ناوبری را از قطعه با findNavController() دریافت می‌کند. displayPropertyDetailsComplete() را فراخوانی کنید تا به مدل view بگویید که LiveData را به حالت تهی بازنشانی کند، بنابراین وقتی برنامه به OverviewFragment بازمی‌گردد، به‌طور تصادفی ناوبری را دوباره راه‌اندازی نکنید.
viewModel.navigateToSelectedProperty.observe(this, Observer {
   if ( null != it ) {   
      this.findNavController().navigate(
              OverviewFragmentDirections.actionShowDetail(it))             
      viewModel.displayPropertyDetailsComplete()
   }
})
  1. detail/DetailFragment.kt باز کنید. این خط را درست زیر فراخوانی به setLifecycleOwner() در onCreateView() اضافه کنید. این خط شی MarsProperty انتخاب شده را از Safe Args دریافت می کند.

    به استفاده از عملگر ادعای نه تهی کاتلین ( !! ) توجه کنید. اگر selectedProperty وجود نداشته باشد، اتفاق وحشتناکی رخ داده است و شما در واقع می خواهید که کد یک نشانگر تهی پرتاب کند. (در کد تولید، باید آن خطا را به نحوی مدیریت کنید.)
 val marsProperty = DetailFragmentArgs.fromBundle(arguments!!).selectedProperty
  1. این خط را در ادامه اضافه کنید تا یک DetailViewModelFactory جدید دریافت کنید. برای دریافت نمونه ای از DetailViewModel DetailViewModelFactory خواهید کرد. برنامه شروع کننده شامل پیاده سازی DetailViewModelFactory است، بنابراین تنها کاری که در اینجا باید انجام دهید این است که آن را مقداردهی اولیه کنید.
val viewModelFactory = DetailViewModelFactory(marsProperty, application)
  1. در نهایت این خط را اضافه کنید تا یک DetailViewModel از کارخانه دریافت کنید و همه قطعات را به هم وصل کنید.
      binding.viewModel = ViewModelProviders.of(
                this, viewModelFactory).get(DetailViewModel::class.java)
  1. برنامه را کامپایل و اجرا کنید و روی هر عکس دارایی مریخ ضربه بزنید. قطعه جزئیات برای جزئیات آن ویژگی ظاهر می شود. روی دکمه برگشت ضربه بزنید تا به صفحه نمای کلی بازگردید و متوجه شوید که صفحه جزئیات هنوز کمی کم است. در کار بعدی افزودن داده های دارایی را به آن صفحه جزئیات تمام می کنید.

در حال حاضر صفحه جزئیات فقط همان عکس مریخ را نشان می دهد که عادت داشتید در صفحه نمای کلی ببینید. کلاس MarsProperty نیز دارای نوع ملک (اجاره یا خرید) و قیمت ملک است. صفحه جزئیات باید شامل هر دو این مقادیر باشد، و اگر املاک اجاره ای نشان دهند که قیمت یک ارزش ماهانه است، مفید خواهد بود. شما از تبدیل‌های LiveData در مدل view برای پیاده‌سازی هر دو مورد استفاده می‌کنید.

  1. res/values/strings.xml باز کنید. کد شروع شامل منابع رشته ای است که در زیر نشان داده شده است تا به شما در ساخت رشته ها برای نمای جزئیات کمک کند. برای قیمت، بسته به نوع دارایی، از منبع display_price یا منبع display_price_monthly_rental استفاده خواهید کرد.
<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 را وارد کنید.

    این تبدیل با استفاده از همان تست از اولین کار آزمایش می کند که آیا ملک انتخاب شده اجاره ای است یا خیر. اگر ملک اجاره ای باشد، تبدیل رشته مناسب را از منابع با یک Kotlin در when {} انتخاب می کند. هر دوی این رشته ها به یک عدد در انتها نیاز دارند، بنابراین پس از آن property.price .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 ایجاد کردید) به نمای جزئیات است. برای انجام این کار، مقدار فیلد متنی برای نوع خاصیت text را روی 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. برنامه را کامپایل و اجرا کنید. اکنون تمام داده‌های دارایی در صفحه جزئیات ظاهر می‌شوند که به خوبی قالب‌بندی شده‌اند.

پروژه اندروید استودیو: MarsRealEstateFinal

عبارات الزام آور

  • از عبارات الزام آور در فایل های طرح بندی XML برای انجام عملیات برنامه نویسی ساده، مانند آزمون های ریاضی یا شرطی، روی داده های محدود استفاده کنید.
  • برای ارجاع به کلاس های داخل فایل طرح بندی خود، از تگ <import> در داخل تگ <data> کنید.

گزینه های درخواست وب سرویس

  • درخواست به سرویس های وب می تواند شامل پارامترهای اختیاری باشد.
  • برای تعیین پارامترهای پرس و جو در درخواست، از حاشیه نویسی @Query در Retrofit استفاده کنید.

دوره بی ادبی:

مستندات توسعه دهنده اندروید:

دیگر:

این بخش، تکالیف احتمالی را برای دانش‌آموزانی که در این آزمایشگاه کد به عنوان بخشی از دوره‌ای که توسط یک مربی هدایت می‌شود، فهرست می‌کند. این وظیفه مربی است که موارد زیر را انجام دهد:

  • در صورت نیاز تکالیف را تعیین کنید.
  • نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
  • تکالیف را درجه بندی کنید.

مربیان می‌توانند از این پیشنهادات به اندازه‌ای که می‌خواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر می‌کنند مناسب است به آنها اختصاص دهند.

اگر به تنهایی بر روی این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.

یه این سوالات پاسخ دهید

سوال 1

تگ <import> در یک فایل طرح بندی XML چه کاری انجام می دهد؟

▢ یک فایل طرح بندی را در دیگری قرار دهید.

▢ کد کاتلین را در فایل طرح بندی جاسازی کنید.

▢ دسترسی به ویژگی های داده محدود را فراهم کنید.

▢ شما را قادر می سازد تا به کلاس ها و اعضای کلاس در عبارات binding ارجاع دهید.

سوال 2

چگونه می‌توان یک گزینه پرس و جو را به تماس سرویس وب REST در Retrofit اضافه کرد؟

▢ پرس و جو را به انتهای URL درخواست اضافه کنید.

▢ یک پارامتر برای پرس و جو به تابعی که درخواست می دهد اضافه کنید و آن پارامتر را با @Query حاشیه نویسی کنید.

▢ از کلاس Query برای ساخت درخواست استفاده کنید.

▢ از addQuery() در سازنده Retrofit استفاده کنید.

درس بعدی را شروع کنید: 9.1: مخزن

برای پیوند به دیگر کدهای این دوره، به صفحه فرود کد لبه‌های کد پایه Android Kotlin Fundamentals مراجعه کنید.