Android Kotlin Fundamentals 08.2: بارگیری و نمایش تصاویر از اینترنت

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

مقدمه

در کد لبه قبلی، یاد گرفتید که چگونه داده ها را از یک وب سرویس دریافت کنید و پاسخ را به یک شی داده تجزیه کنید. در این نرم افزار کد، شما بر اساس آن دانش برای بارگیری و نمایش عکس ها از یک URL وب ایجاد می کنید. همچنین می‌توانید نحوه ساخت RecyclerView را مجدداً مرور کنید و از آن برای نمایش شبکه‌ای از تصاویر در صفحه نمای کلی استفاده کنید.

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

  • نحوه ایجاد و استفاده از قطعات
  • نحوه استفاده از مولفه‌های معماری از جمله مدل‌های مشاهده، کارخانه‌های مشاهده مدل، تبدیل‌ها و LiveData .
  • نحوه بازیابی JSON از یک وب سرویس REST و تجزیه آن داده ها به اشیاء Kotlin با استفاده از کتابخانه های Retrofit و Moshi .
  • نحوه ساخت یک طرح شبکه با RecyclerView .
  • Adapter ، ViewHolder و DiffUtil کار می کنند.

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

  • نحوه استفاده از کتابخانه Glide برای بارگیری و نمایش تصویر از URL وب.
  • نحوه استفاده از RecyclerView و آداپتور شبکه برای نمایش شبکه ای از تصاویر.
  • نحوه رسیدگی به خطاهای احتمالی هنگام دانلود و نمایش تصاویر.

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

  • برنامه MarsRealEstate را تغییر دهید تا URL تصویر را از داده های ویژگی Mars دریافت کنید و از Glide برای بارگیری و نمایش آن تصویر استفاده کنید.
  • یک انیمیشن بارگیری و نماد خطا به برنامه اضافه کنید.
  • از RecyclerView برای نمایش شبکه ای از تصاویر دارایی مریخ استفاده کنید.
  • وضعیت و رسیدگی به خطا را به RecyclerView اضافه کنید.

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

نسخه برنامه‌ای که در این لبه کد می‌سازید، صفحه نمای کلی را پر می‌کند که شبکه‌ای از تصاویر را نمایش می‌دهد. تصاویر بخشی از داده‌های دارایی هستند که برنامه شما از وب سرویس املاک و مستغلات Mars دریافت می‌کند. برنامه شما از کتابخانه Glide برای بارگیری و نمایش تصاویر و از RecyclerView برای ایجاد طرح بندی شبکه ای برای تصاویر استفاده می کند. برنامه شما همچنین خطاهای شبکه را به خوبی مدیریت می کند.

نمایش یک عکس از یک URL وب ممکن است ساده به نظر برسد، اما کمی مهندسی وجود دارد که آن را به خوبی کار کند. تصویر باید دانلود، بافر، و از فرمت فشرده آن به تصویری که اندروید می تواند استفاده کند رمزگشایی شود. تصویر باید در حافظه پنهان، حافظه نهان مبتنی بر ذخیره سازی یا هر دو ذخیره شود. همه اینها باید در رشته‌های پس‌زمینه با اولویت پایین اتفاق بیفتد تا رابط کاربری پاسخگو باقی بماند. همچنین، برای بهترین عملکرد شبکه و CPU، ممکن است بخواهید همزمان بیش از یک تصویر را واکشی و رمزگشایی کنید. یادگیری نحوه بارگذاری موثر تصاویر از شبکه می تواند به خودی خود یک رمز نگاری باشد.

خوشبختانه، شما می توانید از یک کتابخانه توسعه یافته توسط جامعه به نام Glide برای دانلود، بافر، رمزگشایی و کش کردن تصاویر خود استفاده کنید. گلاید کار بسیار کمتری را نسبت به زمانی که مجبور بودید همه این کارها را از ابتدا انجام دهید، برای شما باقی می گذارد.

Glide اساساً به دو چیز نیاز دارد:

  • نشانی اینترنتی تصویری که می‌خواهید بارگیری کنید و نشان دهید.
  • یک شی ImageView برای نمایش آن تصویر.

در این کار، نحوه استفاده از Glide را برای نمایش یک تصویر از وب سرویس املاک یاد می گیرید. شما تصویری را که نشان دهنده اولین ویژگی Mars در لیست ویژگی هایی است که وب سرویس برمی گرداند، نمایش می دهید. در اینجا اسکرین شات های قبل و بعد آمده است:

مرحله 1: وابستگی Glide را اضافه کنید

  1. برنامه MarsRealEstate را از آخرین کد لبه باز کنید. (اگر برنامه را ندارید می توانید MarsRealEstateNetwork را از اینجا دانلود کنید.)
  2. برنامه را اجرا کنید تا ببینید چه کاری انجام می دهد. (جزئیات متنی یک ویژگی را نشان می دهد که به طور فرضی در مریخ موجود است.)
  3. build.gradle (ماژول: برنامه) را باز کنید.
  4. در بخش dependencies ها، این خط را برای کتابخانه Glide اضافه کنید:
implementation "com.github.bumptech.glide:glide:$version_glide"


توجه داشته باشید که شماره نسخه قبلاً به طور جداگانه در فایل Gradle پروژه تعریف شده است.

  1. روی Sync Now کلیک کنید تا پروژه با وابستگی جدید بازسازی شود.

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

سپس کلاس OverviewViewModel را به‌روزرسانی می‌کنید تا داده‌های زنده را برای یک ویژگی مریخ لحاظ کند.

  1. overview/OverviewViewModel.kt را باز کنید. درست در زیر LiveData برای _response ، داده‌های زنده داخلی (قابل تغییر) و خارجی (غیرقابل تغییر) را برای یک شیء MarsProperty کنید.

    کلاس MarsProperty ( com.example.android.marsrealestate.network.MarsProperty ) را در صورت درخواست وارد کنید.
private val _property = MutableLiveData<MarsProperty>()

val property: LiveData<MarsProperty>
   get() = _property
  1. در getMarsRealEstateProperties() ، خطی را در داخل بلوک try/catch {} بیابید که _response.value را با تعداد ویژگی ها تنظیم می کند. تست نشان داده شده در زیر را اضافه کنید. اگر اشیاء MarsProperty در دسترس باشند، این تست مقدار _property LiveData را به اولین ویژگی در listResult می کند.
if (listResult.size > 0) {   
    _property.value = listResult[0]
}

بلوک کامل try/catch {} اکنون به شکل زیر است:

try {
   var listResult = getPropertiesDeferred.await()
   _response.value = "Success: ${listResult.size} Mars properties retrieved"
   if (listResult.size > 0) {      
       _property.value = listResult[0]
   }
 } catch (e: Exception) {
    _response.value = "Failure: ${e.message}"
 }
  1. فایل res/layout/fragment_overview.xml را باز کنید. در عنصر <TextView> ، android:text را تغییر دهید تا به مولفه imgSrcUrl از property LiveData متصل شود:
android:text="@{viewModel.property.imgSrcUrl}"
  1. برنامه را اجرا کنید. TextView فقط URL تصویر را در اولین ویژگی Mars نمایش می دهد. تنها کاری که تاکنون انجام داده اید تنظیم مدل view و داده های زنده برای آن URL است.

مرحله 3: یک آداپتور اتصال ایجاد کنید و با Glide تماس بگیرید

اکنون URL یک تصویر را برای نمایش دارید و زمان آن رسیده است که برای بارگیری آن تصویر با Glide کار کنید. در این مرحله، از یک آداپتور binding برای گرفتن URL از یک ویژگی XML مرتبط با ImageView استفاده می‌کنید و از Glide برای بارگیری تصویر استفاده می‌کنید. آداپتورهای اتصال روش‌های توسعه‌ای هستند که بین یک view و داده‌های محدود قرار می‌گیرند تا رفتار سفارشی را هنگام تغییر داده ارائه دهند. در این مورد، رفتار سفارشی فراخوانی Glide برای بارگیری تصویر از URL در ImageView است.

  1. BindingAdapters.kt باز کنید. این فایل آداپتورهای اتصالی را که در سراسر برنامه استفاده می‌کنید نگه می‌دارد.
  2. یک bindImage() ایجاد کنید که یک ImageView و یک String را به عنوان پارامتر می گیرد. تابع را با @BindingAdapter حاشیه نویسی کنید. حاشیه‌نویسی @BindingAdapter به داده‌های binding می‌گوید که می‌خواهید زمانی که یک آیتم XML دارای ویژگی imageUrl باشد، این آداپتور binding اجرا شود.

    در صورت درخواست androidx.databinding.BindingAdapter و android.widget.ImageView را وارد کنید.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {

}
  1. در تابع bindImage() یک بلوک let {} برای آرگومان imgUrl کنید:
imgUrl?.let { 
}
  1. در داخل بلوک let {} ، خط نشان داده شده در زیر را اضافه کنید تا رشته URL (از XML) به یک شی Uri تبدیل شود. در صورت درخواست androidx.core.net.toUri وارد کنید.

    شما می‌خواهید که شی نهایی Uri از طرح HTTPS استفاده کند، زیرا سروری که تصاویر را از آن می‌کشید به آن طرح نیاز دارد. برای استفاده از طرح HTTPS، buildUpon.scheme("https") را به سازنده toUri کنید. toUri() یک تابع پسوند Kotlin از کتابخانه هسته Android KTX است، بنابراین به نظر می رسد که بخشی از کلاس String است.
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
  1. همچنان let {} ، Glide.with() را فراخوانی کند تا تصویر را از شی Uri در ImageView بارگیری کند. در صورت درخواست، com.bumptech.glide.Glide وارد کنید.
Glide.with(imgView.context)
       .load(imgUri)
       .into(imgView)

مرحله 4: طرح و قطعات را به روز کنید

اگرچه Glide تصویر را بارگذاری کرده است، هنوز چیزی برای دیدن وجود ندارد. مرحله بعدی این است که طرح و قطعات را با ImageView به روز کنید تا تصویر را نمایش دهید.

  1. res/layout/gridview_item.xml را باز کنید. این فایل منبع طرح بندی است که برای هر آیتم در RecyclerView بعداً در Codelab استفاده خواهید کرد. شما در اینجا به طور موقت از آن برای نشان دادن تنها یک تصویر استفاده می کنید.
  2. در بالای عنصر <ImageView> ، یک عنصر <data> برای اتصال داده اضافه کنید و به کلاس OverviewViewModel متصل شوید:
<data>
   <variable
       name="viewModel"
       type="com.example.android.marsrealestate.overview.OverviewViewModel" />
</data>
  1. برای استفاده از آداپتور اتصال بارگذاری تصویر جدید، یک ویژگی app:imageUrl را به عنصر ImageView اضافه کنید:
app:imageUrl="@{viewModel.property.imgSrcUrl}"
  1. overview/OverviewFragment.kt را باز کنید. در onCreateView() ، خطی را که کلاس FragmentOverviewBinding را افزایش می‌دهد و آن را به متغیر binding اختصاص می‌دهد، نظر دهید. این فقط موقتی است. شما بعداً به آن باز خواهید گشت
//val binding = FragmentOverviewBinding.inflate(inflater)
  1. به جای آن یک خط برای افزایش کلاس GridViewItemBinding کنید. com.example.android.marsrealestate. databinding.GridViewItemBinding در صورت درخواست.
val binding = GridViewItemBinding.inflate(inflater)
  1. برنامه را اجرا کنید. اکنون باید عکس تصویر اولین MarsProperty را در لیست نتایج ببینید.

مرحله 5: تصاویر بارگیری و خطای ساده را اضافه کنید

Glide می‌تواند تجربه کاربر را با نشان دادن یک تصویر نگهدارنده در حین بارگیری تصویر و یک تصویر خطا در صورت عدم بارگیری، به عنوان مثال اگر تصویر گم شده یا خراب باشد، بهبود بخشد. در این مرحله، این قابلیت را به آداپتور صحافی و طرح بندی اضافه می کنید.

  1. res/drawable/ic_broken_image.xml را باز کنید و روی برگه Preview در سمت راست کلیک کنید. برای تصویر خطا، از نماد تصویر شکسته استفاده می‌کنید که در کتابخانه نماد داخلی موجود است. این رسم برداری از ویژگی android:tint برای رنگ کردن نماد خاکستری استفاده می کند.

  1. res/drawable/loading_animation.xml کنید. این drawable یک انیمیشن است که با تگ <animate-rotate> تعریف شده است. انیمیشن یک تصویر قابل ترسیم، loading_img.xml را در اطراف نقطه مرکزی می چرخاند. (شما انیمیشن را در پیش نمایش نمی بینید.)

  1. به فایل BindingAdapters.kt . در bindImage() ، فراخوانی را به Glide.with() به روز کنید تا تابع application() بین load() و into() فراخوانی apply() . در صورت درخواست، com.bumptech.glide.request.RequestOptions وارد کنید.

    این کد تصویر بارگیری مکان‌نما را برای استفاده در حین بارگذاری تنظیم می‌کند (قابلیت loading_animation ). کد همچنین یک تصویر را تنظیم می کند تا در صورت عدم بارگیری تصویر از آن استفاده کند ( broken_image ). متد کامل bindImage() اکنون به شکل زیر است:
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
    imgUrl?.let {
        val imgUri = 
           imgUrl.toUri().buildUpon().scheme("https").build()
        Glide.with(imgView.context)
                .load(imgUri)
                .apply(RequestOptions()
                        .placeholder(R.drawable.loading_animation)
                        .error(R.drawable.ic_broken_image))
                .into(imgView)
    }
}
  1. برنامه را اجرا کنید. بسته به سرعت اتصال شبکه خود، ممکن است به طور خلاصه تصویر بارگیری را به عنوان Glide مشاهده کنید و تصویر ویژگی را نمایش دهید. اما شما هنوز نماد تصویر شکسته را نخواهید دید، حتی اگر شبکه خود را خاموش کنید - این مشکل را در آخرین قسمت از لبه کد رفع می کنید.

اکنون برنامه شما اطلاعات دارایی را از اینترنت بارگیری می کند. با استفاده از داده‌های اولین مورد فهرست MarsProperty ، یک ویژگی LiveData در مدل view ایجاد کرده‌اید، و از URL تصویر از داده‌های ویژگی برای پر کردن ImageView استفاده کرده‌اید. اما هدف این است که برنامه شما شبکه‌ای از تصاویر را نمایش دهد، بنابراین می‌خواهید از RecyclerView با GridLayoutManager استفاده کنید.

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

در حال حاضر مدل view دارای یک _property LiveData است که یک شی MarsProperty را در خود نگه می دارد—اولین مورد در لیست پاسخ از سرویس وب. در این مرحله، LiveData را تغییر می دهید تا کل لیست اشیاء MarsProperty را در خود جای دهد.

  1. overview/OverviewViewModel.kt را باز کنید.
  2. متغیر private _property را به _properties . نوع را به لیستی از اشیاء MarsProperty .
private val _properties = MutableLiveData<List<MarsProperty>>()
  1. داده های زنده property خارجی را با properties ها جایگزین کنید. لیست را در اینجا نیز به نوع LiveData اضافه کنید:
 val properties: LiveData<List<MarsProperty>>
        get() = _properties
  1. به سمت پایین به getMarsRealEstateProperties() بروید. در داخل بلوک try {} ، کل آزمایشی را که در کار قبلی اضافه کردید با خط زیر جایگزین کنید. از آنجایی که متغیر listResult لیستی از اشیاء MarsProperty را در خود جای می دهد، می توانید به جای آزمایش برای پاسخ موفقیت آمیز، آن را به _properties.value اختصاص دهید.
_properties.value = listResult

کل بلوک try/catch اکنون به شکل زیر است:

try {
   var listResult = getPropertiesDeferred.await()
   _response.value = "Success: ${listResult.size} Mars properties retrieved"
   _properties.value = listResult
} catch (e: Exception) {
   _response.value = "Failure: ${e.message}"
}

مرحله 2: چیدمان ها و قطعات را به روز کنید

گام بعدی این است که طرح‌بندی و قطعات برنامه را تغییر دهید تا از نمای بازیافت‌کننده و طرح‌بندی شبکه‌ای، به جای نمای تک تصویر استفاده کنید.

  1. res/layout/gridview_item.xml را باز کنید. اتصال داده را از OverviewViewModel به MarsProperty تغییر دهید و نام متغیر را به "property" تغییر دهید.
<variable
   name="property"
   type="com.example.android.marsrealestate.network.MarsProperty" />
  1. در <ImageView> ، ویژگی app:imageUrl را تغییر دهید تا به URL تصویر در شیء MarsProperty شود:
app:imageUrl="@{property.imgSrcUrl}"
  1. overview/OverviewFragment.kt را باز کنید. در onCreateview() خطی را که FragmentOverviewBinding را افزایش می‌دهد از نظر خارج کنید. خطی را که GridViewBinding را افزایش می‌دهد، حذف یا نظر دهید. این تغییرات تغییرات موقتی را که در آخرین کار انجام داده اید، خنثی می کند.
val binding = FragmentOverviewBinding.inflate(inflater)
 // val binding = GridViewItemBinding.inflate(inflater)
  1. res/layout/fragment_overview.xml را باز کنید. کل عنصر <TextView> را حذف کنید.
  2. به جای آن این عنصر <RecyclerView> را اضافه کنید، که از یک GridLayoutManager و طرح grid_view_item برای یک مورد استفاده می کند:
<androidx.recyclerview.widget.RecyclerView
            android:id="@+id/photos_grid"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:padding="6dp"
            android:clipToPadding="false"
            app:layoutManager=
               "androidx.recyclerview.widget.GridLayoutManager"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:spanCount="2"
            tools:itemCount="16"
            tools:listitem="@layout/grid_view_item" />

مرحله 3: آداپتور شبکه عکس را اضافه کنید

اکنون طرح fragment_overview دارای RecyclerView است در حالی که طرح grid_view_item دارای یک ImageView است. در این مرحله، داده ها را از طریق یک آداپتور RecyclerView به RecyclerView متصل می کنید.

  1. overview/PhotoGridAdapter.kt را باز کنید.
  2. کلاس PhotoGridAdapter را با پارامترهای سازنده نشان داده شده در زیر ایجاد کنید. کلاس PhotoGridAdapter ListAdapter را گسترش می دهد که سازنده آن به نوع آیتم لیست، نگهدارنده view و اجرای DiffUtil.ItemCallback دارد.

    در صورت درخواست، کلاس‌های androidx.recyclerview.widget.ListAdapter و com.example.android.marsrealestate.network.MarsProperty را وارد کنید. در مراحل زیر سایر قسمت های گمشده این سازنده که خطا تولید می کنند را پیاده سازی می کنید.
class PhotoGridAdapter : ListAdapter<MarsProperty,
        PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {

}
  1. روی هر نقطه از کلاس PhotoGridAdapter کلیک کنید و Control+i را فشار دهید تا متدهای ListAdapter را پیاده سازی کنید، که عبارتند از onCreateViewHolder() onBindViewHolder() .
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
   TODO("not implemented") 
}

override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
   TODO("not implemented") 
}
  1. در پایان تعریف کلاس PhotoGridAdapter ، پس از متدهایی که به تازگی اضافه کرده اید، یک تعریف شی همراه برای DiffCallback کنید، همانطور که در زیر نشان داده شده است.

    در صورت درخواست androidx.recyclerview.widget.DiffUtil وارد کنید.

    شی DiffCallback DiffUtil.ItemCallback را با نوع شی ای که می خواهید مقایسه کنید گسترش می دهد - MarsProperty .
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
}
  1. برای پیاده سازی متدهای مقایسه کننده برای این شیء، که عبارتند از areItemsTheSame() و areContentsTheSame() ، Control+i را فشار دهید.
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") 
}

override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
   TODO("not implemented") }
  1. برای areItemsTheSame() ، TODO را حذف کنید. از عملگر برابری ارجاعی کاتلین ( === ) استفاده کنید، که اگر ارجاعات شیء oldItem و newItem یکسان باشد، مقدار true را برمی گرداند.
override fun areItemsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem === newItem
}
  1. برای areContentsTheSame() از عملگر برابری استاندارد فقط روی شناسه oldItem و newItem استفاده کنید.
override fun areContentsTheSame(oldItem: MarsProperty, 
                  newItem: MarsProperty): Boolean {
   return oldItem.id == newItem.id
}
  1. هنوز در داخل کلاس PhotoGridAdapter ، در زیر شیء همراه، یک تعریف کلاس داخلی برای MarsPropertyViewHolder کنید، که RecyclerView.ViewHolder را گسترش می دهد.

    androidx.recyclerview.widget.RecyclerView و com.example.android.marsrealestate.databinding.GridViewItemBinding را در صورت درخواست وارد کنید.

    برای اتصال MarsProperty به چیدمان به متغیر GridViewItemBinding نیاز دارید، بنابراین متغیر را به MarsPropertyViewHolder کنید. از آنجایی که کلاس ViewHolder پایه به یک view در سازنده خود نیاز دارد، شما نمای ریشه binding را به آن ارسال می کنید.
class MarsPropertyViewHolder(private var binding: 
                   GridViewItemBinding):
       RecyclerView.ViewHolder(binding.root) {

}
  1. در MarsPropertyViewHolder ، یک متد bind() ایجاد کنید که یک شی MarsProperty را به عنوان آرگومان می گیرد و binding.property را برای آن شی تنظیم می کند. پس از تنظیم ویژگی، executePendingBindings() را فراخوانی کنید، که باعث می شود به روز رسانی بلافاصله اجرا شود.
fun bind(marsProperty: MarsProperty) {
   binding.property = marsProperty
   binding.executePendingBindings()
}
  1. در onCreateViewHolder() TODO را حذف کرده و خط زیر را اضافه کنید. در صورت درخواست android.view.LayoutInflater را وارد کنید.

    onCreateViewHolder() باید یک MarsPropertyViewHolder جدید را برگرداند که با باد کردن GridViewItemBinding و استفاده از LayoutInflater از زمینه ViewGroup والدین شما ایجاد شده است.
   return MarsPropertyViewHolder(GridViewItemBinding.inflate(
      LayoutInflater.from(parent.context)))
  1. در onBindViewHolder() TODO را حذف کرده و خطوط زیر را اضافه کنید. در اینجا شما getItem() را فراخوانی می‌کنید تا شی MarsProperty مرتبط با موقعیت فعلی RecyclerView را دریافت کنید و سپس آن ویژگی را به متد bind() در MarsPropertyViewHolder کنید.
val marsProperty = getItem(position)
holder.bind(marsProperty)

مرحله 4: آداپتور صحافی را اضافه کنید و قطعات را وصل کنید

در نهایت، از یک BindingAdapter برای مقداردهی اولیه PhotoGridAdapter با لیست اشیاء MarsProperty استفاده کنید. استفاده از BindingAdapter برای تنظیم داده های RecyclerView باعث می شود که اتصال داده ها به طور خودکار LiveData را برای لیست اشیاء MarsProperty کند. سپس با تغییر لیست MarsProperty ، آداپتور binding به طور خودکار فراخوانی می شود.

  1. BindingAdapters.kt باز کنید.
  2. در انتهای فایل، یک bindRecyclerView() اضافه کنید که یک RecyclerView و لیستی از اشیاء MarsProperty را به عنوان آرگومان می گیرد. آن روش را با @BindingAdapter حاشیه نویسی کنید.

    androidx.recyclerview.widget.RecyclerView و com.example.android.marsrealestate.network.MarsProperty را در صورت درخواست وارد کنید.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, 
    data: List<MarsProperty>?) {
}
  1. در داخل تابع bindRecyclerView() ، recyclerView.adapter را به PhotoGridAdapter فرستاده و با داده ها adapter.submitList() کنید. این به RecyclerView می گوید که یک لیست جدید در دسترس است.

در صورت درخواست، com.example.android.marsrealestate.overview.PhotoGridAdapter را وارد کنید.

val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
  1. res/layout/fragment_overview.xml را باز کنید. ویژگی app:listData را به عنصر RecyclerView اضافه کنید و با استفاده از data binding آن را روی viewmodel.properties تنظیم کنید.
app:listData="@{viewModel.properties}"
  1. overview/OverviewFragment.kt را باز کنید. در onCreateView() ، درست قبل از فراخوانی setHasOptionsMenu() ، آداپتور RecyclerView را در binding.photosGrid به یک شی PhotoGridAdapter راه اندازی کنید.
binding.photosGrid.adapter = PhotoGridAdapter()
  1. برنامه را اجرا کنید. شما باید شبکه ای از تصاویر MarsProperty را ببینید. همانطور که برای دیدن تصاویر جدید پیمایش می کنید، برنامه قبل از نمایش خود تصویر، نماد پیشرفت بارگذاری را نشان می دهد. اگر حالت هواپیما را روشن کنید، تصاویری که هنوز بارگذاری نشده اند به عنوان نمادهای تصویر شکسته ظاهر می شوند.

برنامه MarsRealEstate زمانی که تصویری قابل واکشی نیست، نماد تصویر شکسته را نمایش می دهد. اما وقتی شبکه وجود ندارد، برنامه یک صفحه خالی را نشان می دهد.

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

مرحله 1: اضافه کردن وضعیت به مدل view

برای شروع، یک LiveData در مدل view ایجاد می کنید تا وضعیت درخواست وب را نشان دهد. سه حالت وجود دارد که باید در نظر گرفت: بارگیری، موفقیت و شکست. حالت بارگیری زمانی اتفاق می‌افتد که منتظر داده‌های موجود در فراخوانی await() هستید.

  1. overview/OverviewViewModel.kt را باز کنید. در بالای فایل (بعد از وارد کردن، قبل از تعریف کلاس)، یک enum اضافه کنید تا تمام وضعیت های موجود را نشان دهد:
enum class MarsApiStatus { LOADING, ERROR, DONE }
  1. تعاریف داده های زنده _response داخلی و خارجی در کلاس OverviewViewModel را به _status . از آنجایی که قبلاً در این کد لبه از _properties LiveData پشتیبانی کرده‌اید، پاسخ سرویس وب کامل استفاده نشده است. برای پیگیری وضعیت فعلی به LiveData در اینجا نیاز دارید، بنابراین می توانید نام متغیرهای موجود را تغییر دهید.

همچنین انواع را از String به MarsApiStatus.

private val _status = MutableLiveData<MarsApiStatus>()

val status: LiveData<MarsApiStatus>
   get() = _status
  1. به سمت پایین به getMarsRealEstateProperties() بروید و _response به _status را در اینجا نیز به روز کنید. رشته "Success" را به حالت MarsApiStatus.DONE و رشته "Failure" را به MarsApiStatus.ERROR .
  2. یک وضعیت MarsApiStatus.LOADING را به بالای بلوک try {} ، قبل از تماس to await() اضافه کنید. این وضعیت اولیه در زمانی است که کوروتین در حال اجرا است و شما منتظر داده هستید. بلوک کامل try/catch {} اکنون به شکل زیر است:
try {
    _status.value = MarsApiStatus.LOADING
   var listResult = getPropertiesDeferred.await()
   _status.value = MarsApiStatus.DONE
   _properties.value = listResult
} catch (e: Exception) {
   _status.value = MarsApiStatus.ERROR
}
  1. پس از حالت خطا در بلوک catch {} ، _properties LiveData را روی یک لیست خالی تنظیم کنید. با این کار RecyclerView پاک می شود.
} catch (e: Exception) {
   _status.value = MarsApiStatus.ERROR
   _properties.value = ArrayList()
}

مرحله 2: یک آداپتور اتصال برای وضعیت ImageView اضافه کنید

اکنون شما یک وضعیت در مدل view دارید، اما این فقط مجموعه ای از حالت ها است. چگونه آن را در خود برنامه نشان می دهید؟ در این مرحله، از ImageView ، متصل به داده‌ها، برای نمایش نمادها برای حالت‌های بارگیری و خطا استفاده می‌کنید. هنگامی که برنامه در حالت بارگیری یا خطا است، ImageView باید قابل مشاهده باشد. وقتی بارگیری برنامه تمام شد، ImageView باید نامرئی باشد.

  1. BindingAdapters.kt باز کنید. یک آداپتور اتصال جدید به نام bindStatus() اضافه کنید که یک ImageView و یک مقدار MarsApiStatus را به عنوان آرگومان می گیرد. com.example.android.marsrealestate.overview.MarsApiStatus را در صورت درخواست وارد کنید.
@BindingAdapter("marsApiStatus")
fun bindStatus(statusImageView: ImageView, 
          status: MarsApiStatus?) {
}
  1. برای جابجایی بین وضعیت‌های مختلف، یک when {} را در bindStatus() اضافه کنید.
when (status) {

}
  1. در داخل when {} ، یک مورد برای وضعیت بارگیری اضافه کنید ( MarsApiStatus.LOADING ). برای این حالت، ImageView را روی قابل مشاهده تنظیم کنید و انیمیشن بارگیری را به آن اختصاص دهید. این همان انیمیشن قابل ترسیمی است که برای Glide در کار قبلی استفاده کردید. در صورت درخواست android.view.View را وارد کنید.
when (status) {
   MarsApiStatus.LOADING -> {
      statusImageView.visibility = View.VISIBLE
      statusImageView.setImageResource(R.drawable.loading_animation)
   }
}
  1. یک مورد برای حالت خطا اضافه کنید که MarsApiStatus.ERROR است. مشابه کاری که برای حالت LOADING انجام دادید، وضعیت ImageView را روی قابل مشاهده تنظیم کنید و از خطای اتصال قابل ترسیم مجدد استفاده کنید.
MarsApiStatus.ERROR -> {
   statusImageView.visibility = View.VISIBLE
   statusImageView.setImageResource(R.drawable.ic_connection_error)
}
  1. یک مورد برای حالت انجام شده اضافه کنید که MarsApiStatus.DONE است. در اینجا شما یک پاسخ موفق دارید، بنابراین نمایان بودن وضعیت ImageView را خاموش کنید تا آن را پنهان کنید.
MarsApiStatus.DONE -> {
   statusImageView.visibility = View.GONE
}

مرحله 3: وضعیت ImageView را به طرح اضافه کنید

  1. res/layout/fragment_overview.xml را باز کنید. در زیر عنصر RecyclerView ، داخل ConstraintLayout ، ImageView را که در زیر نشان داده شده است اضافه کنید.

    این ImageView دارای محدودیت های مشابه با RecyclerView است. با این حال، عرض و ارتفاع از wrap_content برای وسط تصویر استفاده می‌کنند تا اینکه تصویر را کشیده و نمای را پر کند. همچنین به صفت app:marsApiStatus توجه کنید که وقتی ویژگی وضعیت در مدل view تغییر می‌کند، نمای BindingAdapter شما را فراخوانی می‌کند.
<ImageView
   android:id="@+id/status_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:marsApiStatus="@{viewModel.status}" />
  1. برای شبیه سازی اتصال شبکه از دست رفته، حالت هواپیما را در شبیه ساز یا دستگاه خود روشن کنید. برنامه را کامپایل و اجرا کنید و متوجه شوید که تصویر خطا ظاهر می شود:

  1. برای بستن برنامه روی دکمه برگشت ضربه بزنید و حالت هواپیما را خاموش کنید. برای بازگرداندن برنامه از صفحه نمایش اخیر استفاده کنید. بسته به سرعت اتصال شبکه شما، ممکن است زمانی که برنامه قبل از شروع بارگیری تصاویر، سرویس وب را درخواست می کند، یک چرخش بارگذاری بسیار کوتاه مشاهده کنید.

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

  • برای ساده‌سازی فرآیند مدیریت تصاویر، از کتابخانه Glide برای دانلود، بافر، رمزگشایی و ذخیره‌سازی تصاویر در برنامه خود استفاده کنید.
  • Glide برای بارگذاری یک تصویر از اینترنت به دو چیز نیاز دارد: URL یک تصویر، و یک شی ImageView برای قرار دادن تصویر. برای مشخص کردن این گزینه‌ها، از متدهای load() و into() با Glide استفاده کنید.
  • آداپتورهای صحافی روش‌های توسعه‌ای هستند که بین یک view و داده‌های محدود آن view قرار می‌گیرند. آداپتورهای اتصال رفتار سفارشی را هنگام تغییر داده ها ارائه می دهند، به عنوان مثال، برای بارگیری تصویر از URL در ImageView ، Glide را فراخوانی کنید.
  • آداپتورهای صحافی روش‌های پسوندی هستند که با حاشیه‌نویسی @BindingAdapter مشروح شده‌اند.
  • برای افزودن گزینه به درخواست Glide، از متد apply() استفاده کنید. به عنوان مثال، از apply() با placeholder() برای مشخص کردن یک drawable در حال بارگذاری، و از apply() با error() برای تعیین خطایی قابل ترسیم استفاده کنید.
  • برای تولید شبکه ای از تصاویر، از RecyclerView با GridLayoutManager استفاده کنید.
  • برای به‌روزرسانی لیست ویژگی‌ها هنگام تغییر، از یک آداپتور اتصال بین RecyclerView و طرح‌بندی استفاده کنید.

دوره بی ادبی:

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

دیگر:

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

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

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

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

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

سوال 1

از کدام روش Glide برای نشان دادن ImageView که حاوی تصویر بارگذاری شده است استفاده می کنید؟

into()

with()

▢ مشاهده تصویر imageview()

apply()

سوال 2

چگونه می‌توانید یک تصویر مکان‌نما را مشخص کنید تا هنگام بارگیری Glide نشان داده شود؟

▢ از متد into() با یک drawable استفاده کنید.

▢ از RequestOptions() استفاده کنید و متد placeholder() را با یک drawable فراخوانی کنید.

▢ ویژگی Glide.placeholder را به یک drawable اختصاص دهید.

▢ از RequestOptions() استفاده کنید و loadingImage() را با یک drawable فراخوانی کنید.

سوال 3

چگونه نشان می دهید که یک روش یک آداپتور اتصال است؟

▢ متد setBindingAdapter setBindingAdapter() را در LiveData فراخوانی کنید.

▢ متد را در یک فایل Kotlin به نام BindingAdapters.kt قرار دهید.

▢ از ویژگی android:adapter در طرح XML استفاده کنید.

▢ روش را با @BindingAdapter حاشیه نویسی کنید.

درس بعدی را شروع کنید: 8.3 فیلتر کردن و نمایش جزئیات با داده های اینترنتی

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