این کد لبه بخشی از دوره آموزشی 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" است). شما از این منطق در بیش از یک مکان استفاده خواهید کرد، بنابراین بهتر است آن را در اینجا در کلاس داده داشته باشید تا اینکه آن را تکرار کنید.
- برنامه MarsRealEstate را از آخرین کد لبه باز کنید. (اگر برنامه را ندارید می توانید MarsRealEstateGrid را دانلود کنید.)
-
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 برای موارد شبکه انجام دهید.
-
res/layout/grid_view_item.xmlباز کنید. این فایل طرح بندی برای هر سلول جداگانه در طرح بندی شبکه ای برایRecyclerViewاست. در حال حاضر فایل فقط حاوی عنصر<ImageView>برای تصویر ویژگی است. - در عنصر
<data>، یک عنصر<import>برای کلاسViewاضافه کنید. هنگامی که می خواهید از اجزای یک کلاس در داخل یک عبارت اتصال داده در یک فایل طرح بندی استفاده کنید، از import استفاده می کنید. در این حالت، شما از ثابت های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> استفاده میکنند. عبارات 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>- برنامه را کامپایل و اجرا کنید، و توجه داشته باشید که املاکی که اجاره ای نیستند نماد علامت دلار را دارند.

در حال حاضر برنامه شما تمام ویژگی های 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 فیلتر کننده تغییر می دهید.
-
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باز کنید. به دلیل تغییراتی که در مرحله قبل انجام دادید در اندروید استودیو خطاهایی را مشاهده خواهید کرد.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() در مدل view زمانی است که کاربر یک گزینه منو را انتخاب می کند.
-
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()را در مدل view با فیلتر مناسب فراخوانی کنید. برای جابهجایی بین گزینهها از یک Kotlinwhen {}استفاده کنید. برای مقدار فیلتر پیش فرض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: مدل نمای جزئیات را ایجاد کنید و طرح جزئیات را به روز کنید
مشابه فرآیندی که برای مدل نمای کلی و قطعات استفاده کردید، اکنون باید مدل view و فایل های چیدمان را برای قطعه جزئیات پیاده سازی کنید.
-
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پیچیده شده است، بنابراین اگر نمای برای نمایش بیش از حد بزرگ شود، برای مثال زمانی که کاربر آن را در حالت افقی مشاهده می کند، به طور خودکار اسکرول می شود. - برای چیدمان به تب Text بروید. در بالای طرح، درست قبل از عنصر
<ScrollView>، یک عنصر<data>اضافه کنید تا مدل نمای جزئیات را با طرح بندی مرتبط کنید.
<data>
<variable
name="viewModel"
type="com.example.android.marsrealestate.detail.DetailViewModel" />
</data>- ویژگی
app:imageUrlرا به عنصرImageViewاضافه کنید. آن را از ویژگی انتخاب مدل view رویimgSrcUrlتنظیم کنید.
آداپتور صحافی که یک تصویر را با استفاده از Glide بارگیری می کند، به طور خودکار در اینجا نیز استفاده می شود، زیرا آن آداپتور تمام ویژگی هایapp:imageUrlتماشا می کند.
app:imageUrl="@{viewModel.selectedProperty.imgSrcUrl}"مرحله 2: مسیریابی را در مدل نمای کلی تعریف کنید
وقتی کاربر روی عکسی در مدل نمای کلی ضربه میزند، باید به قسمتی پیمایش کند که جزئیات مورد کلیک شده را نشان میدهد.
-
overview/OverviewViewModel.ktباز کنید. یک ویژگی_navigateToSelectedPropertyMutableLiveDataاضافه کنید و آن را با یک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سفارشی ایجاد کنید که یک لامبدا با پارامتر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در مدل view برای ناوبری فعال می شود.
binding.photosGrid.adapter = PhotoGridAdapter(PhotoGridAdapter.OnClickListener {
viewModel.displayPropertyDetails(it)
})مرحله 4: نمودار ناوبری را تغییر دهید و MarsProperty را قابل بسته بندی کنید
وقتی کاربر روی عکسی در شبکه نمای کلی ضربه میزند، برنامه باید به قسمت جزئیات پیمایش کند و از جزئیات ویژگی انتخابی مریخ عبور کند تا نمای جزئیات بتواند آن اطلاعات را نمایش دهد.

در حال حاضر یک شنونده کلیک از PhotoGridAdapter برای کنترل ضربه و راهی برای راه اندازی ناوبری از مدل view دارید. اما شما هنوز یک شی MarsProperty را ندارید که به بخش جزئیات ارسال شود. برای این کار از Safe Args از مؤلفه ناوبری استفاده می کنید.
-
res/navigation/nav_graph.xmlباز کنید. برای مشاهده کد XML برای نمودار ناوبری، روی تب Text کلیک کنید. - در عنصر
<fragment>برای قطعه جزئیات، عنصر<argument>را که در زیر نشان داده شده است اضافه کنید. این آرگومان کهselectedPropertyنام دارد دارای نوعMarsPropertyاست.
<argument
android:name="selectedProperty"
app:argType="com.example.android.marsrealestate.network.MarsProperty"
/>- برنامه را کامپایل کنید. ناوبری به شما خطا می دهد زیرا
MarsPropertyقابل حمل نیست. رابطParcelableاشیاء را قادر می سازد تا سریال شوند، به طوری که داده های اشیاء را می توان بین قطعات یا فعالیت ها منتقل کرد. در این حالت، برای اینکه دادههای داخل شیMarsPropertyاز طریق Safe Args به قطعه جزئیات ارسال شوند،MarsPropertyباید رابطParcelableپیادهسازی کند. خبر خوب این است که کاتلین یک میانبر آسان برای پیاده سازی آن رابط فراهم می کند. -
network/MarsProperty.ktباز کنید. حاشیه نویسی@Parcelizeرا به تعریف کلاس اضافه کنید.
در صورت درخواستkotlinx.android.parcel.Parcelizeرا وارد کنید.
حاشیه نویسی@Parcelizeاز برنامه های افزودنی اندروید Kotlin برای پیاده سازی خودکار متدها در رابط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در لامبدا - تهی نیست، و اگر چنین است، کنترلکننده ناوبری را از قطعه باfindNavController()دریافت میکند.displayPropertyDetailsComplete()را فراخوانی کنید تا به مدل view بگویید که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 دریافت می کند.
به استفاده از عملگر ادعای نه تهی کاتلین (!!) توجه کنید. اگر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 در مدل view برای پیادهسازی هر دو مورد استفاده میکنید.
-
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را وارد کنید.
این تبدیل با استفاده از همان تست از اولین کار آزمایش می کند که آیا ملک انتخاب شده اجاره ای است یا خیر. اگر ملک اجاره ای باشد، تبدیل رشته مناسب را از منابع با یک Kotlinwhen {}انتخاب می کند. هر دوی این رشتهها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ایجاد کرده اید) به نمای جزئیات است. برای انجام این کار، مقدار فیلد متنی برای نوع خاصیت 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" />- برنامه را کامپایل و اجرا کنید. اکنون تمام دادههای دارایی در صفحه جزئیات ظاهر میشوند که به خوبی قالببندی شدهاند.

پروژه اندروید استودیو: MarsRealEstateFinal
عبارات الزام آور
- از عبارات الزام آور در فایل های طرح بندی XML برای انجام عملیات برنامه نویسی ساده، مانند آزمون های ریاضی یا شرطی، روی داده های محدود استفاده کنید.
- برای ارجاع به کلاس های داخل فایل طرح بندی خود، از تگ
<import>داخل تگ<data>استفاده کنید.
گزینه های درخواست وب سرویس
- درخواست به سرویس های وب می تواند شامل پارامترهای اختیاری باشد.
- برای تعیین پارامترهای پرس و جو در درخواست، از حاشیه نویسی
@Queryدر Retrofit استفاده کنید.
دوره بی ادبی:
مستندات توسعه دهنده اندروید:
- نمای کلی مدل ViewModel
- بررسی اجمالی LiveData
- آداپتورهای صحافی
- چیدمان ها و عبارات الزام آور
- ناوبری
- با مولفه Navigation شروع کنید
- انتقال داده ها بین مقصدها (همچنین Safe Args را توصیف می کند)
- کلاس
Transformations - کلاس
ViewModelProvider - کلاس
ViewModelProvider.Factory
دیگر:
این بخش، تکالیف احتمالی را برای دانشآموزانی که در این آزمایشگاه کد به عنوان بخشی از دورهای که توسط یک مربی هدایت میشود، کار میکنند، فهرست میکند. این وظیفه مربی است که موارد زیر را انجام دهد:
- در صورت نیاز تکالیف را تعیین کنید.
- نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
- تکالیف را نمره دهید.
مربیان میتوانند از این پیشنهادات به اندازهای که میخواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر میکنند مناسب است، محول کنند.
اگر به تنهایی از طریق این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.
به این سوالات پاسخ دهید
سوال 1
تگ <import> در یک فایل طرح بندی XML چه کاری انجام می دهد؟
▢ شامل یک فایل طرح بندی در دیگری.
▢ کد کاتلین را در فایل طرح بندی جاسازی کنید.
▢ دسترسی به ویژگی های داده محدود را فراهم کنید.
▢ شما را قادر می سازد تا به کلاس ها و اعضای کلاس در عبارات binding ارجاع دهید.
سوال 2
چگونه در Retrofit گزینه Query را به تماس سرویس وب REST اضافه کنید؟
▢ پرس و جو را به انتهای URL درخواست اضافه کنید.
▢ یک پارامتر برای پرس و جو به تابعی که درخواست می دهد اضافه کنید و آن پارامتر را با @Query حاشیه نویسی کنید.
▢ از کلاس Query برای ساخت درخواست استفاده کنید.
▢ از متد addQuery() در سازنده Retrofit استفاده کنید.
درس بعدی را شروع کنید:
برای پیوند به سایر کدهای این دوره، به صفحه فرود کد لبههای کد پایه Android Kotlin Fundamentals مراجعه کنید.