این کد لبه بخشی از دوره آموزشی Android Kotlin Fundamentals است. اگر به ترتیب روی کدها کار کنید، بیشترین ارزش را از این دوره خواهید گرفت. همه کد لبه های دوره در صفحه فرود کد لبه های Android Kotlin Fundamentals فهرست شده اند.
مقدمه
این کد لبه به شما می آموزد که چگونه از RecyclerView برای نمایش لیست اقلام استفاده کنید. با استفاده از برنامه ردیاب خواب سری قبلی کد لبه ها، با استفاده از RecyclerView با معماری توصیه شده، روشی بهتر و همه کاره تر برای نمایش داده ها را یاد می گیرید.
آنچه از قبل باید بدانید
باید با:
- ساخت یک رابط کاربری اولیه (UI) با استفاده از یک فعالیت، قطعات و نماها.
- پیمایش بین قطعات و استفاده از
safeArgsبرای انتقال داده ها بین قطعات. - با استفاده از مدلهای view، کارخانههای مدل، تبدیلها و
LiveDataو ناظران آنها را مشاهده کنید. - ایجاد پایگاه داده
Room، ایجاد DAO و تعریف موجودیت ها. - استفاده از کوروتین ها برای وظایف پایگاه داده و سایر کارهای طولانی مدت.
چیزی که یاد خواهید گرفت
- نحوه استفاده از
RecyclerViewباAdapterوViewHolderبرای نمایش لیستی از موارد.
کاری که خواهی کرد
- برنامه TrackMySleepQuality را از درس قبلی تغییر دهید تا از
RecyclerViewبرای نمایش دادههای با کیفیت خواب استفاده کنید.
در این نرم افزار کد، بخش RecyclerView یک برنامه را می سازید که کیفیت خواب را ردیابی می کند. این برنامه از پایگاه داده Room برای ذخیره داده های خواب در طول زمان استفاده می کند.
برنامه ردیاب خواب آغازگر دارای دو صفحه است که با قطعات نشان داده شده است، همانطور که در شکل زیر نشان داده شده است.

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

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

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

بزرگترین مزیت RecyclerView این است که برای لیست های بزرگ بسیار کارآمد است:
- به طور پیش فرض،
RecyclerViewفقط برای پردازش یا ترسیم مواردی کار می کند که در حال حاضر روی صفحه قابل مشاهده هستند. به عنوان مثال، اگر لیست شما دارای هزار عنصر است اما فقط 10 عنصر قابل مشاهده است،RecyclerViewفقط به اندازه کافی کار می کند تا 10 مورد را روی صفحه بکشد. هنگامی که کاربر پیمایش می کند،RecyclerViewمتوجه می شود که چه آیتم های جدیدی باید روی صفحه باشند و به اندازه کافی برای نمایش آن موارد کار می کند. - هنگامی که یک مورد از صفحه خارج می شود، نماهای مورد بازیافت می شود. این بدان معناست که مورد با محتوای جدیدی پر شده است که روی صفحه نمایش می رود. این رفتار
RecyclerViewدر زمان پردازش بسیار صرفه جویی می کند و به پیمایش روان لیست ها کمک می کند. - هنگامی که یک مورد تغییر می کند، به جای ترسیم مجدد کل لیست،
RecyclerViewمی تواند آن یک مورد را به روز کند. این یک افزایش کارایی بزرگ در هنگام نمایش لیست موارد پیچیده است!
در دنباله نشان داده شده در زیر، می بینید که یک نما با داده پر شده است، ABC . پس از اینکه این نما از صفحه خارج شد، RecyclerView از نمای برای داده های جدید، XYZ استفاده مجدد می کند.

الگوی آداپتور
اگر تا به حال بین کشورهایی سفر می کنید که از پریزهای برق مختلف استفاده می کنند، احتمالاً می دانید که چگونه می توانید دستگاه های خود را با استفاده از یک آداپتور به پریزها وصل کنید. آداپتور به شما امکان می دهد یک نوع پلاگین را به دیگری تبدیل کنید، که واقعاً یک رابط را به رابط دیگر تبدیل می کند.
الگوی آداپتور در مهندسی نرم افزار به یک شی کمک می کند تا با API دیگری کار کند. RecyclerView از یک آداپتور برای تبدیل داده های برنامه به چیزی که RecyclerView می تواند نمایش دهد، بدون تغییر نحوه ذخیره و پردازش داده ها توسط برنامه استفاده می کند. برای برنامه ردیاب خواب، آداپتوری میسازید که دادههای پایگاه داده Room را به چیزی که RecyclerView میداند چگونه نمایش دهد، بدون تغییر ViewModel تطبیق میدهد.
پیاده سازی RecyclerView

برای نمایش داده های خود در RecyclerView ، به بخش های زیر نیاز دارید:
- داده ها برای نمایش
- یک نمونه
RecyclerViewکه در فایل طرح بندی شما تعریف شده است تا به عنوان محفظه نماها عمل کند. - یک طرح برای یک مورد از داده ها.
اگر همه آیتم های لیست یکسان به نظر می رسند، می توانید از طرح یکسانی برای همه آنها استفاده کنید، اما این اجباری نیست. طرح بندی آیتم باید جدا از طرح بندی قطعه ایجاد شود، به طوری که بتوان یک نمای آیتم را در هر زمان ایجاد کرد و با داده ها پر کرد. - یک مدیر چیدمان
مدیر طرحبندی، سازماندهی (طرحبندی) اجزای رابط کاربری را در یک نما مدیریت میکند. - نگهدارنده دید
نگهدارنده view کلاسViewHolderرا گسترش می دهد. این شامل اطلاعات نمایش برای نمایش یک مورد از طرح بندی مورد است. دارندگان نمایش همچنین اطلاعاتی را اضافه می کنند کهRecyclerViewاز آنها برای جابجایی موثر نماها در سراسر صفحه استفاده می کند. - یک آداپتور
آداپتور داده های شما را بهRecyclerViewمتصل می کند. این داده ها را به گونه ای تطبیق می دهد که بتوان آنها را درViewHolderنمایش داد. یکRecyclerViewاز آداپتور برای نحوه نمایش داده ها بر روی صفحه استفاده می کند.
در این کار، یک RecyclerView را به فایل طرح بندی خود اضافه می کنید و یک Adapter تنظیم می کنید تا داده های خواب را در معرض RecyclerView قرار دهد.
مرحله 1: RecyclerView را با LayoutManager اضافه کنید
در این مرحله، ScrollView با RecyclerView در فایل fragment_sleep_tracker.xml جایگزین میکنید.
- برنامه RecyclerViewFundamentals-Starter را از GitHub دانلود کنید.
- برنامه را بسازید و اجرا کنید. توجه کنید که چگونه داده ها به صورت متن ساده نمایش داده می شوند.
- فایل طرح بندی
fragment_sleep_tracker.xmlرا در تب Design در Android Studio باز کنید. - در قسمت Component Tree ،
ScrollViewحذف کنید. این عمل همچنینTextViewکه در داخلScrollViewقرار دارد حذف می کند. - در صفحه پالت ، در لیست انواع مؤلفه در سمت چپ حرکت کنید تا Containers را پیدا کنید، سپس آن را انتخاب کنید.
- یک
RecyclerViewاز قسمت پالت به قسمت Component Tree بکشید.RecyclerViewدر داخلConstraintLayoutقرار دهید.

- اگر گفتگوی باز شد که از شما میپرسد آیا میخواهید یک وابستگی اضافه کنید، روی OK کلیک کنید تا به Android Studio اجازه دهید وابستگی
recyclerviewرا به فایل Gradle شما اضافه کند. ممکن است چند ثانیه طول بکشد و سپس برنامه شما همگامسازی شود.

- فایل
build.gradleماژول را باز کنید، به انتها بروید و به وابستگی جدید توجه داشته باشید که شبیه کد زیر است:
implementation 'androidx.recyclerview:recyclerview:1.0.0'
- به
fragment_sleep_tracker.xmlبرگردید. - در تب Text به دنبال کد
RecyclerViewکه در زیر نشان داده شده است بگردید:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent" />- به
RecyclerViewیکidsleep_listبدهید.
android:id="@+id/sleep_list"-
RecyclerViewرا قرار دهید تا قسمت باقی مانده از صفحه در داخلConstraintLayoutاشغال کند. برای انجام این کار، بالایRecyclerViewرا به دکمه Start ، پایین را به دکمه Clear و هر طرف را به والد محدود کنید. با استفاده از کد زیر، عرض و ارتفاع طرح را در ویرایشگر Layout یا در XML روی 0 dp تنظیم کنید:
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button"- یک مدیر طرح به
RecyclerViewXML اضافه کنید. هرRecyclerViewبه یک مدیر طرح نیاز دارد که به او بگوید چگونه موارد را در لیست قرار دهد. Android یکLinearLayoutManagerارائه میکند که بهطور پیشفرض موارد را در یک لیست عمودی از ردیفهای عرض کامل قرار میدهد.
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"- به تب Design بروید و توجه کنید که محدودیت های اضافه شده باعث شده است که
RecyclerViewبرای پر کردن فضای موجود گسترش یابد.

مرحله 2: چیدمان آیتم لیست و نگهدارنده نمای متن را ایجاد کنید
RecyclerView فقط یک ظرف است. در این مرحله، طرح و زیرساختی را برای مواردی که قرار است در داخل RecyclerView نمایش داده شوند ایجاد میکنید.
برای رسیدن به یک RecyclerView در سریع ترین زمان ممکن، ابتدا از یک آیتم فهرست ساده استفاده می کنید که کیفیت خواب را فقط به صورت عدد نشان می دهد. برای این کار، به یک view نگهدار، TextItemViewHolder نیاز دارید. شما همچنین به یک نمای، یک TextView ، برای داده ها نیاز دارید. (در مرحله بعد، درباره نگهدارندههای view و نحوه چیدمان همه دادههای خواب بیشتر میآموزید.)
- یک فایل طرح بندی به نام
text_item_view.xmlایجاد کنید. مهم نیست از چه چیزی به عنوان عنصر اصلی استفاده می کنید، زیرا کد قالب را جایگزین خواهید کرد. - در
text_item_view.xml، تمام کدهای داده شده را حذف کنید. - یک
TextViewبا بالشتک16dpدر ابتدا و انتها و اندازه متن24spاضافه کنید. بگذارید عرض با والد مطابقت داشته باشد و ارتفاع محتوا را بپوشاند. از آنجایی که این نما در داخلRecyclerViewنمایش داده می شود، لازم نیست نمای را در یکViewGroupقرار دهید.
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="24sp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />-
Util.ktباز کنید. به انتها بروید و تعریفی را که در زیر نشان داده شده است اضافه کنید، که کلاسTextItemViewHolderایجاد می کند. کد را در پایین فایل، پس از آخرین بسته شدن پرانتز قرار دهید. کد درUtil.ktمی رود زیرا این نگهدارنده view موقتی است و بعداً آن را جایگزین می کنید.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)- اگر از شما خواسته شد،
android.widget.TextViewوandroidx.recyclerview.widget.RecyclerViewرا وارد کنید.
مرحله 3: SleepNightAdapter را ایجاد کنید
وظیفه اصلی در پیاده سازی RecyclerView ایجاد آداپتور است. شما یک نگهدارنده نمای ساده برای نمای آیتم و یک طرح برای هر مورد دارید. اکنون می توانید یک آداپتور ایجاد کنید. آداپتور یک نگهدارنده view ایجاد می کند و آن را با داده هایی برای نمایش RecyclerView پر می کند.
- در بسته
sleeptracker، یک کلاس Kotlin جدید به نامSleepNightAdapterایجاد کنید. - کلاس
SleepNightAdapterرا گسترش دهیدRecyclerView.Adapter. این کلاسSleepNightAdapterنامیده می شود زیرا یک شیSleepNightرا با چیزی کهRecyclerViewمی تواند استفاده کند تطبیق می دهد. آداپتور باید بداند که از چه نگهدارنده view استفاده کند، بنابراینTextItemViewHolderوارد کنید. هنگامی که از شما خواسته شد، اجزای لازم را وارد کنید، و سپس با خطا مواجه خواهید شد، زیرا روشهای اجباری برای پیادهسازی وجود دارد.
class SleepNightAdapter: RecyclerView.Adapter<TextItemViewHolder>() {}- در سطح بالای
SleepNightAdapter، یک متغیرlistOfSleepNightبرای نگهداری داده ها ایجاد کنید.
var data = listOf<SleepNight>()- در
SleepNightAdapter،getItemCount()لغو کنید تا اندازه لیست شبهای خواب را درdataبرگردانید.RecyclerViewباید بداند که آداپتور چند آیتم برای نمایش دارد و این کار را با فراخوانیgetItemCount()انجام می دهد.
override fun getItemCount() = data.size- در
SleepNightAdapterتابعonBindViewHolder()را مطابق شکل زیر نادیده بگیرید.
تابعonBindViewHolder()توسطRecyclerViewفراخوانی می شود تا داده های یک آیتم لیست را در موقعیت مشخص شده نمایش دهد. بنابراین متدonBindViewHolder()دو آرگومان می گیرد: یک view holder و یک موقعیت داده برای اتصال. برای این برنامه، دارندهTextItemViewHolderاست و موقعیت موقعیت در لیست است.
override fun onBindViewHolder(holder: TextItemViewHolder, position: Int) {
}- در داخل
onBindViewHolder()یک متغیر برای یک آیتم در یک موقعیت مشخص در داده ایجاد کنید.
val item = data[position]-
ViewHolderکه ایجاد کردید دارای خاصیتی به نامtextViewاست. در داخلonBindViewHolder()،texttextViewرا روی عدد با کیفیت خواب تنظیم کنید. این کد فقط لیستی از اعداد را نمایش می دهد، اما این مثال ساده به شما امکان می دهد ببینید که چگونه آداپتور داده ها را به نگهدارنده view و روی صفحه نمایش می برد.
holder.textView.text = item.sleepQuality.toString()- در
SleepNightAdapter،onCreateViewHolder()نادیده گرفته و پیاده سازی کنید، که زمانی فراخوانی می شود کهRecyclerViewبرای نمایش یک آیتم به یک view نگهدارنده نیاز دارد.
این تابع دو پارامتر می گیرد و یکViewHolderبرمی گرداند. پارامترparent، که گروه view است که نگهدارنده view را نگه می دارد، همیشهRecyclerViewاست. پارامترviewTypeزمانی استفاده می شود که چندین نما در یکRecyclerViewوجود داشته باشد. برای مثال، اگر فهرستی از نماهای متن، یک تصویر و یک ویدیو را در همانRecyclerViewقرار دهید، تابعonCreateViewHolder()باید بداند که از چه نوع نما استفاده میکند.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TextItemViewHolder {
}- در
onCreateViewHolder()یک نمونه ازLayoutInflaterایجاد کنید.
بادکننده layout میداند که چگونه از طرحبندیهای XML نما ایجاد کند.contextحاوی اطلاعاتی در مورد نحوه درست کردن نما است. در یک آداپتور برای نمای Recycler، شما همیشه در زمینه گروه نمایparent، کهRecyclerViewاست، عبور می کنید.
val layoutInflater = LayoutInflater.from(parent.context)- در
onCreateViewHolder()با درخواست ازlayoutinflaterآن راviewکنید.
برای نما از طرح XML و برای نمای گروه نمایparentعبور کنید. آرگومان سوم، بولی،attachToRootاست. این آرگومان بایدfalseباشد، زیراRecyclerViewاین مورد را به سلسلهمراتب view برای شما اضافه میکند.
val view = layoutInflater
.inflate(R.layout.text_item_view, parent, false) as TextView- در
onCreateViewHolder()یکTextItemViewHolderساخته شده باviewرا برگردانید.
return TextItemViewHolder(view)- آداپتور باید به
RecyclerViewاطلاع دهد کهdataتغییر کرده اند، زیراRecyclerViewچیزی در مورد داده ها نمی داند. فقط در مورد نگهدارنده های view که آداپتور به آن می دهد می داند.
برای اینکه بهRecyclerViewبگویید چه زمانی داده هایی که نمایش می دهد تغییر کرده است، یک تنظیم کننده سفارشی به متغیرdataکه در بالای کلاسSleepNightAdapterقرار دارد اضافه کنید. در تنظیم کننده، بهdataیک مقدار جدید بدهید، سپسnotifyDataSetChanged()را فراخوانی کنید تا فهرست دوباره با داده های جدید ترسیم شود.
var data = listOf<SleepNight>()
set(value) {
field = value
notifyDataSetChanged()
}مرحله 4: به RecyclerView در مورد آداپتور بگویید
RecyclerView باید در مورد آداپتور مورد استفاده برای دریافت دارندگان view اطلاعات داشته باشد.
-
SleepTrackerFragment.ktرا باز کنید. - در
onCreateview()یک آداپتور ایجاد کنید. این کد را بعد از ایجاد مدلViewModelو قبل از عبارتreturnقرار دهید.
val adapter = SleepNightAdapter()-
adapterباRecyclerViewمرتبط کنید.
binding.sleepList.adapter = adapter- پروژه خود را تمیز کرده و دوباره بسازید تا شیء
bindingبه روز کنید.
اگر همچنان خطاهایی در اطرافbinding.sleepListیاbinding.FragmentSleepTrackerBindingمی بینید، حافظه پنهان را باطل کرده و مجددا راه اندازی کنید. ( File > Invalidate Caches / Restart را انتخاب کنید.)
اگر اکنون برنامه را اجرا کنید، هیچ خطایی وجود ندارد، اما با ضربه زدن روی Start و سپس Stop هیچ داده ای نمایش داده نمی شود.
مرحله 5: داده ها را در آداپتور دریافت کنید
تاکنون یک آداپتور و راهی برای دریافت داده ها از آداپتور به RecyclerView دارید. اکنون باید داده ها را از ViewModel وارد آداپتور کنید.
-
SleepTrackerViewModelباز کنید. - متغیر
nightsرا پیدا کنید، که تمام شبهای خواب را ذخیره میکند، که دادههایی برای نمایش است. متغیرnightsبا فراخوانیgetAllNights()در پایگاه داده تنظیم می شود. -
privateازnightsحذف کنید، زیرا ناظری ایجاد میکنید که باید به این متغیر دسترسی داشته باشد. بیانیه شما باید به شکل زیر باشد:
val nights = database.getAllNights()- در بسته
database،SleepDatabaseDaoرا باز کنید. - تابع
getAllNights()را پیدا کنید. توجه داشته باشید که این تابع لیستی از مقادیرSleepNightرا به عنوانLiveDataبرمی گرداند. این بدان معناست که متغیرnightsحاویLiveDataاست که توسطRoomبهروزرسانی میشود و میتوانیدnightsمشاهده کنید تا بدانید چه زمانی تغییر میکند. -
SleepTrackerFragmentرا باز کنید. - در
onCreateView()، در زیر ایجادadapter، یک مشاهده گر روی متغیرnightsایجاد کنید.
با ارائهviewLifecycleOwnerقطعه به عنوان مالک چرخه حیات، می توانید مطمئن شوید که این ناظر تنها زمانی فعال است کهRecyclerViewروی صفحه باشد.
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
})- در داخل مشاهدهگر، هر زمان که یک مقدار غیر تهی دریافت کردید (برای
nights)، مقدار را بهdataآداپتور اختصاص دهید. این کد تکمیل شده برای مشاهده گر و تنظیم داده ها است:
sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
it?.let {
adapter.data = it
}
})- کد خود را بسازید و اجرا کنید.
اگر آداپتور شما کار می کند، اعداد کیفیت خواب را به صورت فهرستی مشاهده خواهید کرد. بعد از اینکه روی Start ضربه بزنید، اسکرین شات سمت چپ -1 را نشان میدهد. تصویر صفحه سمت راست بعد از ضربه زدن روی Stop و انتخاب یک رتبه بندی کیفیت، عدد به روز شده کیفیت خواب را نشان می دهد.

مرحله 6: نحوه بازیافت دارندگان view را بررسی کنید
RecyclerView دارندگان view را بازیافت می کند ، به این معنی که از آنها مجددا استفاده می کند. هنگامی که یک نما از صفحه خارج می شود، RecyclerView از نما برای نمایی که قرار است روی صفحه اسکرول شود، دوباره استفاده می کند.
از آنجایی که این نگهدارندههای view بازیافت میشوند، مطمئن شوید که onBindViewHolder() هر گونه سفارشیسازیهایی را که آیتمهای قبلی ممکن است روی یک view دارنده تنظیم کرده باشند، تنظیم یا بازنشانی کند.
برای مثال، میتوانید رنگ متن را در نگهدارندههایی که دارای رتبهبندی کیفیت کمتر یا مساوی ۱ هستند و نشاندهنده خواب ضعیف هستند، روی قرمز تنظیم کنید.
- در کلاس
SleepNightAdapter، کد زیر را در انتهایonBindViewHolder()اضافه کنید.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
}- برنامه را اجرا کنید.
- مقداری داده با کیفیت خواب پایین اضافه کنید و عدد قرمز است.
- رتبهبندیهای بالا را برای کیفیت خواب اضافه کنید تا زمانی که یک عدد قرمز قرمز روی صفحه مشاهده کنید.
همانطور کهRecyclerViewاز نگهدارندههای view استفاده مجدد میکند، در نهایت از یکی از دارندگان نمای قرمز برای رتبهبندی با کیفیت بالا استفاده مجدد میکند. امتیاز بالا به اشتباه به رنگ قرمز نمایش داده می شود.

- برای رفع این مشکل، یک عبارت
elseاضافه کنید تا اگر کیفیت آن کمتر یا مساوی یک نباشد، رنگ را روی مشکی تنظیم کنید.
با هر دو شرط صریح، دارنده view از رنگ متن صحیح برای هر مورد استفاده می کند.
if (item.sleepQuality <= 1) {
holder.textView.setTextColor(Color.RED) // red
} else {
// reset
holder.textView.setTextColor(Color.BLACK) // black
}- برنامه را اجرا کنید و اعداد باید همیشه رنگ مناسبی داشته باشند.
تبریک می گویم! اکنون یک RecyclerView اساسی کاملاً کاربردی دارید.
در این کار، نگهدارنده نمای ساده را با نگهدارنده ای جایگزین می کنید که می تواند داده های بیشتری را برای یک شب خواب نمایش دهد.
ViewHolder ساده ای که به Util.kt اضافه کردید فقط یک TextView در یک TextItemViewHolder قرار می دهد.
class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)پس چرا RecyclerView فقط مستقیماً از TextView استفاده نمی کند؟ این یک خط کد قابلیت های زیادی را ارائه می دهد. ViewHolder یک نمای آیتم و ابرداده در مورد مکان آن در RecyclerView را توصیف می کند. RecyclerView به این قابلیت متکی است تا نمایش را بهعنوان اسکرول لیست به درستی قرار دهد و کارهای جالبی مانند متحرک کردن نماها هنگام افزودن یا حذف موارد در Adapter انجام دهد.
اگر RecyclerView نیاز به دسترسی به نماهای ذخیره شده در ViewHolder داشته باشد، می تواند این کار را با استفاده از ویژگی itemView دارنده view انجام دهد. RecyclerView از itemView زمانی استفاده میکند که یک آیتم را برای نمایش روی صفحه نمایش میدهد، هنگام ترسیم تزئینات اطراف یک نما مانند یک حاشیه، و برای پیادهسازی قابلیت دسترسی.
مرحله 1: طرح بندی مورد را ایجاد کنید
در این مرحله فایل layout را برای یک آیتم ایجاد می کنید. این طرح شامل یک ConstraintLayout با ImageView برای کیفیت خواب، یک TextView برای طول خواب و یک TextView برای کیفیت به عنوان متن است. چون قبلاً طرحبندیها را انجام دادهاید، کد XML ارائهشده را کپی و جایگذاری کنید.
- یک فایل منبع طرح بندی جدید ایجاد کنید و نام آن را
list_item_sleep_nightبگذارید. - تمام کدهای موجود در فایل را با کد زیر جایگزین کنید. سپس با طرحی که ایجاد کرده اید آشنا شوید.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/quality_image"
android:layout_width="@dimen/icon_size"
android:layout_height="60dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@drawable/ic_sleep_5" />
<TextView
android:id="@+id/sleep_length"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_image"
app:layout_constraintTop_toTopOf="@+id/quality_image"
tools:text="Wednesday" />
<TextView
android:id="@+id/quality_string"
android:layout_width="0dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="@+id/sleep_length"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/sleep_length"
app:layout_constraintTop_toBottomOf="@+id/sleep_length"
tools:text="Excellent!!!" />
</androidx.constraintlayout.widget.ConstraintLayout>- به تب Design در Android Studio بروید. در نمای طراحی، چیدمان شما مانند عکس صفحه سمت چپ زیر است. در نمای نقشه، مانند تصویر سمت راست به نظر می رسد.

مرحله 2: ViewHolder را ایجاد کنید
-
SleepNightAdapter.ktرا باز کنید. - یک کلاس در داخل
SleepNightAdapterبه نامViewHolderایجاد کنید و آن را توسعه دهیدRecyclerView.ViewHolder.
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){}- در داخل
ViewHolder، ارجاع به نماها را دریافت کنید. شما به نماهایی که اینViewHolderبه روز می کند نیاز دارید. هر بار که اینViewHolderمتصل می کنید، باید به تصویر و هر دو نمای متن دسترسی داشته باشید. (شما این کد را تبدیل می کنید تا بعداً از اتصال داده استفاده کند.)
val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
val quality: TextView = itemView.findViewById(R.id.quality_string)
val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)مرحله 3: از ViewHolder در SleepNightAdapter استفاده کنید
- در تعریف
SleepNightAdapter، به جایTextItemViewHolder، ازSleepNightAdapter.ViewHolderکه به تازگی ایجاد کرده اید استفاده کنید.
class SleepNightAdapter: RecyclerView.Adapter<SleepNightAdapter.ViewHolder>() {به روز رسانی onCreateViewHolder() :
- امضای
onCreateViewHolder()را تغییر دهید تاViewHolderبرگردد. - برای استفاده از منبع چیدمان درست،
list_item_sleep_nightبادکننده طرحبندی را تغییر دهید . - بازیگران را به
TextViewحذف کنید. - به جای برگرداندن یک
TextItemViewHolder، یکViewHolderبرگردانید.
در اینجا تابعonCreateViewHolder()بهروزرسانی شده است:
override fun onCreateViewHolder(
parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater =
LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night,
parent, false)
return ViewHolder(view)
}به روز رسانی onBindViewHolder() :
- امضای
onBindViewHolder()را تغییر دهید تا پارامترholderبه جایTextItemViewHolderیکViewHolderباشد. - در داخل
onBindViewHolder()تمام کدها را به جز تعریفitemحذف کنید. - یک
valresتعریف کنید که مرجعی بهresourcesاین نما دارد.
val res = holder.itemView.context.resources- متن نمای متن
sleepLengthرا روی مدت زمان تنظیم کنید. کد زیر را کپی کنید، که تابع قالب بندی ارائه شده با کد شروع را فراخوانی می کند.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)- این یک خطا می دهد، زیرا
convertDurationToFormatted()باید تعریف شود.Util.ktرا باز کنید و کد و واردات مربوط به آن را از نظر خارج کنید. ( کد > نظر با نظرات خط را انتخاب کنید.) - به
onBindViewHolder()برگردید، ازconvertNumericQualityToString()برای تنظیم کیفیت استفاده کنید.
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)- ممکن است لازم باشد این توابع را به صورت دستی وارد کنید.
import com.example.android.trackmysleepquality.convertDurationToFormatted
import com.example.android.trackmysleepquality.convertNumericQualityToString- نماد صحیح را برای کیفیت تنظیم کنید. آیکون جدید
ic_sleep_activeدر کد شروع برای شما ارائه شده است.
holder.qualityImage.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
})- در اینجا تابع به روز شده
onBindViewHolder()است که تمام داده ها را برایViewHolderتنظیم می کند:
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text= convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.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
})
}- برنامه خود را اجرا کنید صفحه نمایش شما باید مانند تصویر زیر باشد که نماد کیفیت خواب را به همراه متنی برای مدت زمان خواب و کیفیت خواب نشان می دهد.

RecyclerView شما اکنون کامل شده است! شما یاد گرفتید که چگونه یک Adapter و یک ViewHolder را پیاده سازی کنید و آنها را برای نمایش لیستی با یک Adapter RecyclerView کنار هم قرار دادید.
کد شما تاکنون روند ایجاد آداپتور و نگهدارنده نمایش را نشان می دهد. با این حال، شما می توانید این کد را بهبود بخشید. کد برای نمایش و کد برای مدیریت دارندگان view با هم مخلوط شده اند و onBindViewHolder() جزئیات مربوط به نحوه به روز رسانی ViewHolder می داند.
در یک برنامه تولیدی، ممکن است چندین نگهدارنده نمایش، آداپتورهای پیچیدهتر و چندین توسعهدهنده در حال تغییر داشته باشید. شما باید کد خود را طوری ساختار دهید که همه چیز مربوط به یک view نگهدار فقط در نگهدارنده view باشد.
مرحله 1: Refactor onBindViewHolder()
در این مرحله، کد را تغییر میدهید و تمام عملکردهای نگهدارنده view را به ViewHolder منتقل میکنید. هدف از این refactoring تغییر ظاهر برنامه برای کاربر نیست، بلکه کار کردن روی کد را برای توسعه دهندگان آسانتر و ایمنتر میکند. خوشبختانه اندروید استودیو ابزارهایی برای کمک به شما دارد.
- در
SleepNightAdapter، درonBindViewHolder()همه چیز را انتخاب کنید به جز عبارتی کهitemمتغیر را اعلام کنید. - کلیک راست کنید، سپس Refactor > Extract > Function را انتخاب کنید.
- تابع
bindرا نامگذاری کنید و پارامترهای پیشنهادی را بپذیرید. روی OK کلیک کنید.
تابعbind()در زیرonBindViewHolder()قرار داده شده است.
private fun bind(holder: ViewHolder, item: SleepNight) {
val res = holder.itemView.context.resources
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
holder.quality.text = convertNumericQualityToString(item.sleepQuality, res)
holder.qualityImage.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
})
}- مکان نما را روی کلمه
holderپارامترholderbind()قرار دهید.Alt+Enter(Option+Enterدر مک) را فشار دهید تا منوی قصد باز شود. برای تبدیل این تابع به یک تابع پسوندی که دارای امضای زیر است ، Convert parameter to گیرنده را انتخاب کنید:
private fun ViewHolder.bind(item: SleepNight) {...}- تابع
bind()را برش داده و درViewHolderقرار دهید. -
bind()را عمومی کنید. - در صورت لزوم
bind()به آداپتور وارد کنید. - از آنجایی که اکنون در
ViewHolderاست، میتوانید قسمتViewHolderرا از امضا حذف کنید. در اینجا کد نهایی تابعbind()در کلاسViewHolderآمده است.
fun bind(item: SleepNight) {
val res = itemView.context.resources
sleepLength.text = convertDurationToFormatted(
item.startTimeMilli, item.endTimeMilli, res)
quality.text = convertNumericQualityToString(
item.sleepQuality, res)
qualityImage.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
})
}مرحله 2: روی CreateViewHolder Refactor کنید
متد onCreateViewHolder() در آداپتور در حال حاضر نمای را از منبع چیدمان برای ViewHolder افزایش می دهد. با این حال، تورم ربطی به آداپتور ندارد، و همه چیز به ViewHolder مربوط می شود. تورم باید در ViewHolder اتفاق بیفتد.
- در
onCreateViewHolder()تمام کدهای موجود در بدنه تابع را انتخاب کنید. - کلیک راست کنید، سپس Refactor > Extract > Function را انتخاب کنید.
- تابع را
fromنامگذاری کنید و پارامترهای پیشنهادی را بپذیرید. روی OK کلیک کنید. - مکان نما را روی نام تابع
from.Alt+Enter(Option+Enterدر مک) را فشار دهید تا منوی قصد باز شود. - انتقال به شی همراه را انتخاب کنید. تابع
from()باید در یک شیء همراه باشد تا بتوان آن را در کلاسViewHolderفراخوانی کرد نه در یک نمونهViewHolder. - شیء
companionرا به کلاسViewHolderمنتقل کنید. - ساخت
from()public. - در
onCreateViewHolder()عبارتreturnرا تغییر دهید تا نتیجه فراخوانیfrom()را در کلاسViewHolderبرگردانید.
متدهایonCreateViewHolder()وfrom()تکمیل شده شما باید شبیه کد زیر باشد و کد شما باید بدون خطا ساخته و اجرا شود.
override fun onCreateViewHolder(parent: ViewGroup, viewType:
Int): ViewHolder {
return ViewHolder.from(parent)
}companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val view = layoutInflater
.inflate(R.layout.list_item_sleep_night, parent, false)
return ViewHolder(view)
}
}- امضای کلاس
ViewHolderرا طوری تغییر دهید که سازنده خصوصی باشد. از آنجایی کهfrom()اکنون متدی است که یک نمونهViewHolderجدید را برمی گرداند، دیگر دلیلی وجود ندارد که کسی سازندهViewHolderرا فراخوانی کند.
class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){- برنامه را اجرا کنید. برنامه شما باید مانند قبل ساخته و اجرا شود که نتیجه مطلوب پس از refactoring است.
پروژه اندروید استودیو: RecyclerViewFundamentals
- نمایش لیست یا شبکه ای از داده ها یکی از رایج ترین کارهای رابط کاربری در اندروید است.
RecyclerViewطراحی شده است تا حتی در هنگام نمایش لیست های بسیار بزرگ نیز کارآمد باشد. -
RecyclerViewفقط کارهای لازم برای پردازش یا ترسیم مواردی را انجام می دهد که در حال حاضر روی صفحه قابل مشاهده هستند. - هنگامی که یک مورد از صفحه خارج می شود، نمای آن بازیافت می شود. این بدان معناست که مورد با محتوای جدیدی پر شده است که روی صفحه نمایش می رود.
- الگوی آداپتور در مهندسی نرم افزار به یک شی کمک می کند تا با یک API دیگر کار کند.
RecyclerViewاز یک آداپتور برای تبدیل داده های برنامه به چیزی که می تواند نمایش دهد، بدون نیاز به تغییر نحوه ذخیره و پردازش داده ها توسط برنامه استفاده می کند.
برای نمایش داده های خود در RecyclerView ، به بخش های زیر نیاز دارید:
- RecyclerView
برای ایجاد یک نمونه ازRecyclerView، یک عنصر<RecyclerView>را در فایل طرح بندی تعریف کنید. - LayoutManager
یکRecyclerViewازLayoutManagerبرای سازماندهی طرح بندی آیتم ها درRecyclerViewاستفاده می کند، مانند چیدن آنها در یک شبکه یا در یک لیست خطی.
در<RecyclerView>در فایل layout، ویژگیapp:layoutManagerروی مدیر طرح (مانندLinearLayoutManagerیاGridLayoutManager) تنظیم کنید.
همچنین می توانیدLayoutManagerبرای یکRecyclerViewبه صورت برنامه نویسی تنظیم کنید. (این تکنیک در کدهای بعدی پوشش داده شده است.) - طرح بندی برای هر مورد
یک طرح بندی برای یک مورد از داده ها در یک فایل طرح بندی XML ایجاد کنید. - آداپتور
آداپتوری ایجاد کنید که داده ها و نحوه نمایش آنها را درViewHolderآماده می کند. آداپتور را باRecyclerViewمرتبط کنید.
هنگامی کهRecyclerViewاجرا می شود، از آداپتور برای نحوه نمایش داده ها بر روی صفحه استفاده می کند.
آداپتور از شما می خواهد که روش های زیر را پیاده سازی کنید:
–getItemCount()برای برگرداندن تعداد آیتم ها.
–onCreateViewHolder()برای بازگرداندنViewHolderبرای یک آیتم در لیست.
–onBindViewHolder()برای تطبیق داده ها با نماهای یک آیتم در لیست. - ViewHolder
یکViewHolderحاوی اطلاعات نمای برای نمایش یک مورد از طرح بندی مورد است. - متد
onBindViewHolder()در آداپتور داده ها را با view ها تطبیق می دهد. شما همیشه این روش را نادیده می گیرید. به طور معمول،onBindViewHolder()طرح بندی یک آیتم را افزایش می دهد و داده ها را در view ها در طرح قرار می دهد. - از آنجایی که
RecyclerViewچیزی در مورد داده ها نمی داند،Adapterباید زمانی که داده ها تغییر می کندRecyclerViewاطلاع دهد. ازnotifyDataSetChanged()برای اطلاع دادن بهAdapterمبنی بر تغییر داده ها استفاده کنید.
دوره بی ادبی:
مستندات توسعه دهنده اندروید:
این بخش، تکالیف احتمالی را برای دانشآموزانی که در این آزمایشگاه کد به عنوان بخشی از دورهای که توسط یک مربی هدایت میشود، کار میکنند، فهرست میکند. این وظیفه مربی است که موارد زیر را انجام دهد:
- در صورت نیاز تکالیف را تعیین کنید.
- نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
- تکالیف را نمره دهید.
مربیان میتوانند از این پیشنهادات به اندازهای که میخواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر میکنند مناسب است، محول کنند.
اگر به تنهایی از طریق این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.
به این سوالات پاسخ دهید
سوال 1
RecyclerView چگونه موارد را نمایش می دهد؟ همه موارد کاربردی را انتخاب کنید.
▢ موارد را در یک لیست یا یک شبکه نمایش می دهد.
▢ به صورت عمودی یا افقی پیمایش می کند.
▢ در دستگاه های بزرگتر مانند تبلت ها به صورت مورب پیمایش می کند.
▢ هنگامی که یک لیست یا یک شبکه برای مورد استفاده کافی نیست، به طرحبندیهای سفارشی اجازه میدهد.
سوال 2
مزایای استفاده از RecyclerView چیست؟ همه موارد کاربردی را انتخاب کنید.
▢ به طور موثر لیست های بزرگ را نمایش می دهد.
▢ به طور خودکار داده ها را به روز می کند.
▢ هنگامی که یک مورد به روز می شود، حذف می شود یا به لیست اضافه می شود، نیاز به رفرش را به حداقل می رساند.
▢ برای نمایش مورد بعدی که روی صفحه اسکرول میشود، از نمای خارج از صفحه استفاده مجدد میکند.
سوال 3
برخی از دلایل استفاده از آداپتورها چیست؟ همه موارد کاربردی را انتخاب کنید.
▢ تفکیک نگرانی ها تغییر و آزمایش کد را آسان تر می کند.
▢ RecyclerView نسبت به دادههایی که نمایش داده میشوند آگنوستیک است.
▢ لایههای پردازش دادهها نباید به نحوه نمایش دادهها بپردازند.
▢ برنامه سریعتر اجرا خواهد شد.
سوال 4
کدام یک از موارد زیر در مورد ViewHolder صادق است؟ همه موارد کاربردی را انتخاب کنید.
▢ طرح ViewHolder در فایل های طرح بندی XML تعریف شده است.
▢ برای هر واحد داده در مجموعه داده یک ViewHolder وجود دارد.
▢ شما می توانید بیش از یک ViewHolder در RecyclerView داشته باشید.
▢ Adapter داده ها را به ViewHolder متصل می کند.
درس بعدی را شروع کنید: