این کد لبه بخشی از دوره آموزشی 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
را بهFrameLayout
تغییر دهید تاmatch_parent
والد جدید پر شود.
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()
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 با فیلتر مناسب فراخوانی کنید. برای جابهجایی بین گزینهها از یک 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
}
- برنامه را کامپایل و اجرا کنید. این برنامه اولین شبکه نمای کلی را با انواع اموال و املاک برای فروش که با نماد دلار مشخص شده اند راه اندازی می کند.
- از منوی گزینه ها، اجاره را انتخاب کنید. ویژگی ها دوباره بارگذاری می شوند و هیچ کدام با نماد دلار ظاهر نمی شوند. (فقط املاک اجاره ای نشان داده می شوند.) ممکن است لازم باشد چند لحظه صبر کنید تا صفحه نمایش تازه شود تا فقط ویژگی های فیلتر شده را نشان دهید.
- از منوی گزینه ها خرید را انتخاب کنید. ویژگی ها دوباره بارگیری می شوند و همه آنها با نماد دلار ظاهر می شوند. (فقط املاک برای فروش نشان داده شده است.)
اکنون یک شبکه پیمایشی از نمادها برای ویژگیهای مریخ دارید، اما زمان آن رسیده که جزئیات بیشتری را دریافت کنید. در این کار، شما یک قطعه جزئیات را برای نمایش جزئیات یک ویژگی خاص اضافه می کنید. قطعه جزئیات تصویر بزرگتر، قیمت و نوع ملک را نشان می دهد - خواه اجاره باشد یا برای فروش.
این قطعه زمانی راه اندازی می شود که کاربر روی یک تصویر در شبکه نمای کلی ضربه می زند. برای انجام این کار، باید یک شنونده 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
اضافه کنید. آن را رویimgSrcUrl
از ویژگی انتخاب شده مدل view تنظیم کنید.
آداپتور صحافی که یک تصویر را با استفاده از 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
سفارشی ایجاد کنید که یک لامبدا با پارامتر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 را قابل بسته بندی کنید
وقتی کاربر روی عکسی در شبکه نمای کلی ضربه میزند، برنامه باید به قسمت جزئیات پیمایش کند و از جزئیات ویژگی انتخابی Mars عبور کند تا نمای جزئیات بتواند آن اطلاعات را نمایش دهد.
در حال حاضر شما یک شنونده کلیک از 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"
/>
- برنامه را کامپایل کنید. Navigation به شما یک خطا می دهد زیرا
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
جدید دریافت کنید. برای دریافت نمونه ای ازDetailViewModel
DetailViewModelFactory
خواهید کرد. برنامه شروع کننده شامل پیاده سازی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
یا منبع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>
-
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)
}
- کلاس
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
چگونه میتوان یک گزینه پرس و جو را به تماس سرویس وب REST در Retrofit اضافه کرد؟
▢ پرس و جو را به انتهای URL درخواست اضافه کنید.
▢ یک پارامتر برای پرس و جو به تابعی که درخواست می دهد اضافه کنید و آن پارامتر را با @Query
حاشیه نویسی کنید.
▢ از کلاس Query
برای ساخت درخواست استفاده کنید.
▢ از addQuery()
در سازنده Retrofit استفاده کنید.
درس بعدی را شروع کنید:
برای پیوند به دیگر کدهای این دوره، به صفحه فرود کد لبههای کد پایه Android Kotlin Fundamentals مراجعه کنید.