Android Kotlin Fundamentals 07.2: DiffUtil و اتصال داده با RecyclerView

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

مقدمه

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

در این کد لبه، شما بر اساس برنامه ردیاب خواب از کد لبه قبلی ایجاد می کنید. روش مؤثرتری برای به‌روزرسانی فهرست داده‌های خواب را یاد می‌گیرید و نحوه استفاده از اتصال داده با RecyclerView را یاد می‌گیرید. (اگر اپلیکیشن نسخه کد قبلی را ندارید، می توانید کد شروع را برای این کد لبه دانلود کنید.)

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

  • ساخت یک رابط کاربری اولیه با استفاده از یک فعالیت، قطعات و نماها.
  • پیمایش بین قطعات و استفاده از safeArgs برای انتقال داده ها بین قطعات.
  • مشاهده مدل‌ها، مشاهده کارخانه‌های مدل، تبدیل‌ها، و LiveData و ناظران آن‌ها.
  • چگونه یک پایگاه داده Room ایجاد کنیم، یک DAO ایجاد کنیم و موجودیت ها را تعریف کنیم.
  • نحوه استفاده از کوروتین ها برای پایگاه داده و سایر کارهای طولانی مدت.
  • نحوه پیاده سازی RecyclerView اولیه با Adapter ، ViewHolder و طرح بندی آیتم.

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

  • نحوه استفاده از DiffUtil برای به روز رسانی موثر لیست نمایش داده شده توسط RecyclerView .
  • نحوه استفاده از data binding با RecyclerView .
  • نحوه استفاده از آداپتورهای اتصال برای تبدیل داده ها

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

  • بر اساس برنامه TrackMySleepQuality از کدهای قبلی در این سری ساخته شده است.
  • برای به روز رسانی کارآمد لیست با استفاده از DiffUtil ، SleepNightAdapter را به روز کنید.
  • اجرای اتصال داده برای RecyclerView ، با استفاده از آداپتورهای اتصال برای تبدیل داده ها.

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

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

این برنامه برای استفاده از کنترلر رابط کاربری، ViewModel و LiveData و پایگاه داده Room برای تداوم داده های خواب طراحی شده است.

داده های خواب در یک RecyclerView نمایش داده می شود. در این کد لبه، شما بخش DiffUtil و data-binding را برای RecyclerView می‌سازید. بعد از این کد لبه، برنامه شما دقیقاً یکسان به نظر می رسد، اما کارآمدتر و مقیاس بندی و نگهداری آن آسان تر خواهد بود.

می‌توانید به استفاده از برنامه SleepTracker از نسخه کد قبلی ادامه دهید، یا می‌توانید برنامه RecyclerViewDiffUtilDataBinding-Starter را از GitHub دانلود کنید.

  1. در صورت نیاز، برنامه RecyclerViewDiffUtilDataBinding-Starter را از GitHub دانلود کنید و پروژه را در Android Studio باز کنید.
  2. برنامه را اجرا کنید.
  3. فایل SleepNightAdapter.kt را باز کنید.
  4. کد را بررسی کنید تا با ساختار برنامه آشنا شوید. برای خلاصه ای از استفاده از RecyclerView با الگوی آداپتور برای نمایش داده های خواب به کاربر، به نمودار زیر مراجعه کنید.

  • از ورودی کاربر، برنامه لیستی از اشیاء SleepNight ایجاد می کند. هر شیء SleepNight نشان دهنده یک شب خواب، مدت زمان و کیفیت آن است.
  • SleepNightAdapter لیست اشیاء SleepNight را با چیزی تطبیق می دهد که RecyclerView می تواند استفاده کند و نمایش دهد.
  • آداپتور SleepNightAdapter ViewHolders تولید می کند که حاوی نماها، داده ها و اطلاعات متا برای نمای Recycler برای نمایش داده ها است.
  • RecyclerView از SleepNightAdapter برای تعیین تعداد آیتم برای نمایش ( getItemCount() ) استفاده می کند. RecyclerView از onCreateViewHolder() و onBindViewHolder() برای اتصال دارندگان view به داده ها برای نمایش استفاده می کند.

متد notifyDataSetChanged() ناکارآمد است

برای اینکه به RecyclerView بگوییم که یک مورد در لیست تغییر کرده و باید به‌روزرسانی شود، کد فعلی notifyDataSetChanged() را در SleepNightAdapter ، همانطور که در زیر نشان داده شده است.

var data =  listOf<SleepNight>()
   set(value) {
       field = value
       notifyDataSetChanged()
   }

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

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

RecyclerView یک API غنی برای به روز رسانی یک عنصر دارد. می‌توانید از notifyItemChanged() برای اطلاع به RecyclerView استفاده کنید که یک مورد تغییر کرده است، و می‌توانید از توابع مشابه برای مواردی که اضافه، حذف یا منتقل می‌شوند استفاده کنید. شما می توانید همه این کارها را به صورت دستی انجام دهید، اما این کار بی اهمیت است و ممکن است شامل مقدار زیادی کد باشد.

خوشبختانه، راه بهتری وجود دارد.

DiffUtil کارآمد است و کار سخت را برای شما انجام می دهد

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

هنگامی که DiffUtil متوجه شد چه چیزی تغییر کرده است، RecyclerView می تواند از آن اطلاعات برای به روز رسانی فقط مواردی که تغییر، اضافه، حذف یا جابجا شده اند استفاده کند، که بسیار کارآمدتر از انجام مجدد کل لیست است.

در این کار، SleepNightAdapter را ارتقا می دهید تا از DiffUtil برای بهینه سازی RecyclerView برای تغییرات داده ها استفاده کند.

مرحله 1: اجرای SleepNightDiffCallback

به منظور استفاده از عملکرد کلاس DiffUtil ، DiffUtil.ItemCallback را گسترش دهید.

  1. SleepNightAdapter.kt باز کنید.
  2. در زیر تعریف کامل کلاس برای SleepNightAdapter ، یک کلاس سطح بالای جدید به نام SleepNightDiffCallback که DiffUtil.ItemCallback را گسترش می دهد. SleepNight را به عنوان یک پارامتر عمومی ارسال کنید.
class SleepNightDiffCallback : DiffUtil.ItemCallback<SleepNight>() {
}
  1. مکان نما را در نام کلاس SleepNightDiffCallback قرار دهید.
  2. Alt+Enter فشار دهید ( Option+Enter در Mac) و Implement Members را انتخاب کنید.
  3. در محاوره ای که باز می شود، برای انتخاب areItemsTheSame() و areContentsTheSame() شیفت چپ کلیک کنید، سپس روی OK کلیک کنید.

    همانطور که در زیر نشان داده شده است، این کار در داخل SleepNightDiffCallback برای دو روش ایجاد می کند. DiffUtil از این دو روش استفاده می کند تا بفهمد لیست و آیتم ها چگونه تغییر کرده اند.
    override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
  1. در داخل areItemsTheSame() ، کد TODO را با کدی جایگزین کنید که آزمایش می کند آیا دو آیتم SleepNight ، oldItem و newItem یکسان هستند یا خیر. اگر موارد دارای nightId یکسان هستند، آنها همان مورد هستند، بنابراین true را برگردانید. در غیر این صورت، false را برگردانید. DiffUtil از این تست برای کمک به کشف اینکه آیا یک مورد اضافه، حذف یا منتقل شده است استفاده می کند.
override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem.nightId == newItem.nightId
}
  1. در داخل areContentsTheSame() ، بررسی کنید که آیا oldItem و newItem دارای داده های یکسانی هستند یا خیر. یعنی برابرند یا نه. این بررسی برابری همه فیلدها را بررسی می کند، زیرا SleepNight یک کلاس داده است. کلاس های Data به طور خودکار equals و چند روش دیگر را برای شما تعریف می کنند. اگر بین oldItem و newItem تفاوت هایی وجود داشته باشد، این کد به DiffUtil می گوید که مورد به روز شده است.
override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
   return oldItem == newItem
}

استفاده از RecyclerView برای نمایش لیستی که تغییر می کند یک الگوی رایج است. RecyclerView یک کلاس آداپتور به نام ListAdapter می دهد که به شما کمک می کند یک آداپتور RecyclerView بسازید که توسط یک لیست پشتیبانی می شود.

ListAdapter لیست را برای شما پیگیری می کند و هنگامی که لیست به روز شد به آداپتور اطلاع می دهد.

مرحله 1: آداپتور خود را برای گسترش ListAdapter تغییر دهید

  1. در فایل SleepNightAdapter.kt ، امضای کلاس SleepNightAdapter را به گسترش ListAdapter .
  2. اگر از شما خواسته شد، androidx.recyclerview.widget.ListAdapter وارد کنید.
  3. SleepNight را به عنوان اولین آرگومان به ListAdapter قبل از SleepNightAdapter.ViewHolder کنید.
  4. SleepNightDiffCallback() را به عنوان یک پارامتر به سازنده اضافه کنید. ListAdapter از این استفاده می کند تا بفهمد چه چیزی در لیست تغییر کرده است. امضای کلاس SleepNightAdapter تمام شده شما باید مانند شکل زیر باشد.
class SleepNightAdapter : ListAdapter<SleepNight, SleepNightAdapter.ViewHolder>(SleepNightDiffCallback()) {
  1. در داخل کلاس SleepNightAdapter ، فیلد data ، از جمله تنظیم کننده را حذف کنید. شما دیگر به آن نیاز ندارید، زیرا ListAdapter لیست را برای شما پیگیری می کند.
  2. نادیده گرفتن getItemCount() را حذف کنید، زیرا ListAdapter این روش را برای شما پیاده سازی می کند.
  3. برای خلاص شدن از شر خطای onBindViewHolder() متغیر item را تغییر دهید. به جای استفاده از data برای دریافت یک item ، getItem(position) که ListAdapter ارائه می دهد فراخوانی کنید.
val item = getItem(position)

مرحله 2: از submitList() برای به روز نگه داشتن لیست استفاده کنید

کد شما باید به ListAdapter که لیست تغییر یافته در دسترس است. ListAdapter متدی به نام submitList() ارائه می دهد تا به ListAdapter بگوید که نسخه جدیدی از لیست موجود است. هنگامی که این روش فراخوانی می شود، ListAdapter لیست جدید را با لیست قدیمی متفاوت می کند و موارد اضافه، حذف، جابجایی یا تغییر را تشخیص می دهد. سپس ListAdapter موارد نشان داده شده توسط RecyclerView را به روز می کند.

  1. SleepTrackerFragment.kt را باز کنید.
  2. در onCreateView() ، در مشاهدهگر در sleepTrackerViewModel ، خطا را پیدا کنید که در آن متغیر data ای که حذف کرده اید ارجاع داده شده است.
  3. جایگزین adapter.data = it با یک فراخوانی به adapter.submitList(it) جایگزین کنید. کد به روز شده در زیر نشان داده شده است.

sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
   it?.let {
       adapter.submitList(it)
   }
})
  1. برنامه خود را اجرا کنید سریع‌تر اجرا می‌شود، شاید اگر لیست شما کوچک باشد به‌طور محسوسی کار نمی‌کند.

در این کار، از همان تکنیکی که در codelab های قبلی وجود داشت برای تنظیم اتصال داده ها استفاده می کنید و تماس های findViewById() را حذف می کنید.

مرحله 1: پیوند داده را به فایل طرح بندی اضافه کنید

  1. فایل طرح بندی list_item_sleep_night.xml را در تب Text باز کنید.
  2. مکان نما را روی تگ ConstraintLayout قرار دهید و Alt+Enter را فشار دهید ( Option+Enter در مک). منوی قصد (منوی "رفع سریع") باز می شود.
  3. Convert to data binding layout را انتخاب کنید. این طرح را در <layout> می پیچد و یک تگ <data> را به داخل اضافه می کند.
  4. در صورت لزوم به بالا بروید و در داخل تگ <data> ، متغیری به نام sleep را اعلام کنید.
  5. type آن را به نام کاملاً واجد شرایط SleepNight ، com.example.android.trackmysleepquality.database.SleepNight تبدیل کنید. تگ <data> تمام شده شما باید مانند شکل زیر باشد.
   <data>
        <variable
            name="sleep"
            type="com.example.android.trackmysleepquality.database.SleepNight"/>
    </data>
  1. برای ایجاد اجباری شی Binding ، Build > Clean Project را انتخاب کنید، سپس Build > Rebuild Project را انتخاب کنید. (اگر همچنان مشکل دارید، File > Invalidate Caches / Restart را انتخاب کنید.) شیء اتصال ListItemSleepNightBinding به همراه کدهای مرتبط به فایل های تولید شده پروژه اضافه می شود.

مرحله 2: طرح بندی مورد را با استفاده از اتصال داده باد کنید

  1. SleepNightAdapter.kt باز کنید.
  2. در کلاس ViewHolder متد from() را پیدا کنید.
  3. اعلان متغیر view را حذف کنید.

کد برای حذف :

val view = layoutInflater
       .inflate(R.layout.list_item_sleep_night, parent, false)
  1. در جایی که متغیر view بود، یک متغیر جدید به نام binding تعریف کنید که شیء اتصال ListItemSleepNightBinding را مانند شکل زیر باد می کند. وارد کردن لازم شیء binding را انجام دهید.
val binding =
ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
  1. در پایان تابع، به جای برگرداندن view ، binding را برگردانید.
return ViewHolder(binding)
  1. برای خلاص شدن از خطا، مکان نما را روی کلمه binding قرار دهید. Alt+Enter ( Option+Enter در مک) را فشار دهید تا منوی قصد باز شود.
  1. تغییر پارامتر «itemView» نوع سازنده اصلی کلاس «ViewHolder» به «ListItemSleepNightBinding» را انتخاب کنید . این نوع پارامتر کلاس ViewHolder را به روز می کند.

  1. برای مشاهده تغییر در امضا، به سمت تعریف کلاس ViewHolder . شما یک خطا برای itemView می بینید، زیرا itemView را در متد from() به binding تغییر داده اید.

    در تعریف کلاس ViewHolder ، روی یکی از موارد itemView کلیک راست کرده و Refactor > Rename را انتخاب کنید. نام را به binding تغییر دهید.
  2. پیشوند binding پارامتر سازنده را با val قرار دهید تا به یک ویژگی تبدیل شود.
  3. در فراخوانی کلاس والد، RecyclerView.ViewHolder ، پارامتر را از binding به binding.root تغییر دهید. شما باید یک View را ارسال کنید و binding.root ریشه ConstraintLayout در طرح بندی آیتم شما است.
  4. اعلان کلاس تمام شده شما باید مانند کد زیر باشد.
class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){

همچنین یک خطا برای فراخوانی findViewById() می بینید و بعداً این مشکل را برطرف می کنید.

مرحله 3: جایگزین findViewById()

اکنون می‌توانید sleepLength ، quality و qualityImage را به‌روزرسانی کنید تا از شی binding به جای findViewById() استفاده کنید.

  1. مقداردهی اولیه sleepLength ، qualityString ، و qualityImage را برای استفاده از نماهای شی binding ، همانطور که در زیر نشان داده شده است، تغییر دهید. پس از این، کد شما نباید دیگر خطا نشان دهد.
val sleepLength: TextView = binding.sleepLength
val quality: TextView = binding.qualityString
val qualityImage: ImageView = binding.qualityImage

با قرار دادن شی binding، دیگر نیازی به تعریف sleepLength ، quality و qualityImage . DataBinding جستجوها را کش می کند، بنابراین نیازی به اعلام این ویژگی ها نیست.

  1. روی نام sleepLength ، quality و qualityImage کلیک راست کنید. Refactor > Inline را انتخاب کنید یا Control+Command+N را فشار دهید ( Option+Command+N در Mac).
  2. برنامه خود را اجرا کنید (ممکن است نیاز داشته باشید که پروژه خود را پاکسازی و بازسازی کنید اگر دارای خطا باشد.)

در این کار، برنامه خود را ارتقا می دهید تا از اتصال داده با آداپتورهای اتصال برای تنظیم داده ها در نماهای خود استفاده کند.

در یک کد لبه قبلی، از کلاس Transformations برای گرفتن LiveData و تولید رشته های قالب بندی شده برای نمایش در نمای متن استفاده می کردید. با این حال، اگر نیاز به اتصال انواع مختلف یا انواع پیچیده دارید، می‌توانید آداپتورهای اتصال را برای کمک به اتصال داده‌ها در استفاده از آن انواع ارائه کنید. آداپتورهای صحافی، آداپتورهایی هستند که داده‌های شما را می‌گیرند و آن‌ها را با چیزی تطبیق می‌دهند که با اتصال داده می‌تواند برای پیوند دادن یک نما، مانند متن یا تصویر، استفاده کند.

شما قرار است سه آداپتور صحافی را پیاده سازی کنید، یکی برای تصویر با کیفیت و یکی برای هر فیلد متنی. به طور خلاصه، برای اعلام یک آداپتور binding، روشی را تعریف می‌کنید که یک آیتم و یک view را می‌گیرد و آن را با @BindingAdapter . در بدنه روش، تبدیل را اجرا می کنید. در Kotlin، می‌توانید یک آداپتور binding را به عنوان یک تابع افزونه روی کلاس view که داده‌ها را دریافت می‌کند، بنویسید.

مرحله 1: آداپتورهای اتصال ایجاد کنید

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

  1. SleepNightAdapater.kt باز کنید.
  2. در داخل کلاس ViewHolder ، متد bind() را پیدا کنید و به خودتان یادآوری کنید که این متد چه کاری انجام می دهد. کدی را که مقادیر binding.sleepLength ، binding.quality و binding.qualityImage را محاسبه می کند، دریافت کرده و به جای آن در آداپتور استفاده کنید. (در حال حاضر، کد را همانطور که هست بگذارید؛ در مرحله بعد آن را منتقل می کنید.)
  3. در بسته sleeptracker ، فایلی به نام BindingUtils.kt ایجاد و باز کنید.
  4. یک تابع افزونه در TextView به نام setSleepDurationFormatted و در یک SleepNight عبور دهید. این تابع آداپتور شما برای محاسبه و قالب بندی مدت زمان خواب خواهد بود.
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
  1. در بدنه setSleepDurationFormatted ، داده ها را به view متصل کنید، همانطور که در ViewHolder.bind() انجام دادید. convertDurationToFormatted() را فراخوانی کنید و سپس text TextView را روی متن فرمت شده تنظیم کنید. (از آنجایی که این یک تابع پسوندی در TextView است، می توانید مستقیماً به ویژگی text دسترسی داشته باشید.)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
  1. برای اطلاع از اتصال داده ها در مورد این آداپتور اتصال، تابع را با @BindingAdapter حاشیه نویسی کنید.
  2. این تابع آداپتور ویژگی sleepDurationFormatted است، بنابراین sleepDurationFormatted را به عنوان آرگومان به @BindingAdapter کنید.
@BindingAdapter("sleepDurationFormatted")
  1. آداپتور دوم کیفیت خواب را بر اساس مقدار موجود در یک شی SleepNight می کند. یک تابع افزونه به نام setSleepQualityString() در TextView ایجاد کنید و در یک SleepNight کنید.
  2. در بدنه، داده‌ها را مانند ViewHolder.bind() به view متصل کنید. convertNumericQualityToString را فراخوانی کرده و text را تنظیم کنید.
  3. تابع را با @BindingAdapter("sleepQualityString") حاشیه نویسی کنید.
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
   text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
  1. سومین آداپتور صحافی تصویر را در نمای تصویر تنظیم می کند. تابع افزونه را در ImageView ایجاد کنید، setSleepImage کنید و از کد ViewHolder.bind() استفاده کنید، همانطور که در زیر نشان داده شده است.
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
   setImageResource(when (item.sleepQuality) {
       0 -> R.drawable.ic_sleep_0
       1 -> R.drawable.ic_sleep_1
       2 -> R.drawable.ic_sleep_2
       3 -> R.drawable.ic_sleep_3
       4 -> R.drawable.ic_sleep_4
       5 -> R.drawable.ic_sleep_5
       else -> R.drawable.ic_sleep_active
   })
}

مرحله ۲: SleepNightAdapter را به‌روزرسانی کنید

  1. SleepNightAdapter.kt باز کنید.
  2. همه موارد موجود در متد bind() را حذف کنید، زیرا اکنون می توانید از اتصال داده و آداپتورهای جدید خود برای انجام این کار برای شما استفاده کنید.
fun bind(item: SleepNight) {
}
  1. Inside bind() ، خواب را به item اختصاص دهید، زیرا باید به شی binding در مورد SleepNight جدید خود بگویید.
binding.sleep = item
  1. در زیر آن خط، binding.executePendingBindings() را اضافه کنید. این فراخوانی یک بهینه‌سازی است که از binding داده‌ها می‌خواهد تا هر اتصال معلق را فوراً اجرا کند. هنگامی که از آداپتورهای binding در RecyclerView استفاده می کنید، همیشه ایده خوبی است که executePendingBindings() را فراخوانی کنید، زیرا می تواند اندازه نماها را کمی افزایش دهد.
 binding.executePendingBindings()

مرحله 3: پیوندها را به طرح XML اضافه کنید

  1. list_item_sleep_night.xml باز کنید.
  2. در ImageView ، یک ویژگی app با همان نام آداپتور اتصال که تصویر را تنظیم می کند، اضافه کنید. همانطور که در زیر نشان داده شده است، متغیر sleep را پاس کنید.

    این ویژگی از طریق آداپتور، ارتباط بین view و شی binding را ایجاد می کند. هر زمان که به sleepImage ارجاع داده شود، آداپتور داده های SleepNight را تطبیق می دهد.
app:sleepImage="@{sleep}"
  1. همین کار را برای نمای متن sleep_length و quality_string دهید. هر زمان sleepDurationFormatted یا sleepQualityString ارجاع داده شود، آداپتورها داده‌های SleepNight را تطبیق می‌دهند.
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
  1. برنامه خود را اجرا کنید دقیقاً مانند قبل عمل می کند. آداپتورهای صحافی تمام کارهای قالب‌بندی و به‌روزرسانی نماها را با تغییر داده‌ها انجام می‌دهند، ViewHolder را ساده می‌کنند و به کد ساختار بسیار بهتری نسبت به قبل می‌دهند.

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

تبریک میگم در این مرحله شما به خوبی در راه تسلط بر RecyclerView در اندروید هستید.

پروژه Android Studio: RecyclerViewDiffUtilDataBinding .

DiffUtil :

  • RecyclerView یک کلاس به نام DiffUtil دارد که برای محاسبه تفاوت بین دو لیست است.
  • DiffUtil کلاسی به نام ItemCallBack که شما آن را گسترش می دهید تا تفاوت بین دو لیست را بفهمید.
  • در کلاس ItemCallback ، باید areItemsTheSame() و areContentsTheSame() را لغو کنید.

ListAdapter :

  • برای دریافت مدیریت لیست به صورت رایگان، می توانید از کلاس ListAdapter به جای RecyclerView.Adapter استفاده کنید. با این حال، اگر از ListAdapter استفاده می‌کنید، باید آداپتور خود را برای طرح‌بندی‌های دیگر بنویسید، به همین دلیل است که این کد لبه به شما نشان می‌دهد که چگونه این کار را انجام دهید.
  • برای باز کردن منوی قصد در Android Studio، مکان نما را روی هر کدی از کد قرار دهید و Alt+Enter ( Option+Enter در مک) را فشار دهید. این منو به ویژه برای بازآفرینی کد و ایجاد خرد برای پیاده سازی روش ها مفید است. منو به زمینه حساس است، بنابراین برای دریافت منوی صحیح باید مکان نما را دقیقاً قرار دهید.

اتصال داده ها:

  • برای اتصال داده ها به نماها، از اتصال داده در طرح بندی مورد استفاده کنید.

آداپتورهای اتصال:

  • شما قبلاً از Transformations برای ایجاد رشته ها از داده ها استفاده کرده اید. اگر نیاز به اتصال داده‌ها از انواع مختلف یا پیچیده دارید، آداپتورهای اتصال را برای کمک به اتصال داده‌ها در استفاده از آنها فراهم کنید.
  • برای اعلام یک آداپتور binding، متدی را تعریف کنید که یک آیتم و یک view را می گیرد و روی روش را با @BindingAdapter حاشیه نویسی کنید. در Kotlin، می توانید آداپتور binding را به عنوان یک تابع افزونه در View بنویسید. به نام ویژگی که آداپتور تطبیق می دهد، عبور دهید. مثلا:
@BindingAdapter("sleepDurationFormatted")
  • در طرح XML، یک ویژگی app را با همان نام آداپتور binding تنظیم کنید. یک متغیر را با داده ارسال کنید. مثلا:
.app:sleepDurationFormatted="@{sleep}"

دوره های بی ادبی:

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

سایر منابع:

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

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

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

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

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

سوال 1

کدام یک از موارد زیر برای استفاده از DiffUtil ؟ همه موارد اعمال شده را انتخاب کنید.

ItemCallBack کلاس ItemCallBack را گسترش دهید.

areItemsTheSame() را لغو کنید.

areContentsTheSame() را لغو کنید.

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

سوال 2

کدام یک از موارد زیر در مورد آداپتورهای اتصال درست است؟

▢ آداپتور binding تابعی است که با @BindingAdapter مشروح شده است.

▢ استفاده از یک آداپتور صحافی به شما امکان می دهد قالب بندی داده ها را از نگهدارنده نمایش جدا کنید.

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

▢ آداپتورهای صحافی راه حل خوبی برای زمانی هستند که نیاز به تبدیل داده های پیچیده دارید.

سوال 3

چه زمانی باید از Transformations به جای آداپتور اتصال استفاده کنید؟ همه موارد اعمال شده را انتخاب کنید.

▢ داده های شما ساده است.

▢ شما در حال قالب بندی یک رشته هستید.

▢ لیست شما بسیار طولانی است.

ViewHolder شما فقط دارای یک نمای است.

درس بعدی را شروع کنید: 7.3: GridLayout با RecyclerView