این کد لبه بخشی از دوره آموزشی 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 در اندروید استودیو باز کنید. - در قسمت 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
یکid
sleep_list
.
android:id="@+id/sleep_list"
-
RecyclerView
را قرار دهید تا قسمت باقی مانده از صفحه در داخلConstraintLayout
را اشغال کند. برای انجام این کار، بالایRecyclerView
را به دکمه Start ، پایین را به دکمه Clear و هر طرف را به والد محدود کنید. با استفاده از کد زیر، عرض و ارتفاع طرحبندی را در Layout Editor یا XML روی ۰ 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"
- یک layout manager به
RecyclerView
XML اضافه کنید. هر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
، یک متغیرlistOf
SleepNight
برای نگهداری داده ها ایجاد کنید.
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()
،text
textView
را روی عدد با کیفیت خواب تنظیم کنید. این کد فقط لیستی از اعداد را نمایش می دهد، اما این مثال ساده به شما امکان می دهد ببینید که چگونه آداپتور داده ها را به نگهدارنده 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 برای view و گروه viewparent
برای view عبور کنید. آرگومان سوم، بولی،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
چیزی در مورد داده ها نمی داند. فقط در مورد نگهدارنده های دید که آداپتور به آن می دهد می داند.
برای اینکه به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 دارنده تنظیم کرده باشند، تنظیم یا بازنشانی کند.
به عنوان مثال، میتوانید رنگ متن را در نگهدارندههای نمایشی که دارای رتبهبندی کیفیت کمتر یا مساوی 1 هستند و نشاندهنده خواب ضعیف هستند، روی قرمز تنظیم کنید.
- در کلاس
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
به جایViewHolder
یکTextItemViewHolder
باشد. - در داخل
onBindViewHolder()
تمام کدها را به جز تعریفitem
حذف کنید. - یک
val
res
تعریف کنید که دارای ارجاع بهresources
برای این view است.
val res = holder.itemView.context.resources
- متن نمای متن
sleepLength
را روی مدت زمان تنظیم کنید. کد زیر را کپی کنید، که تابع قالب بندی ارائه شده با کد شروع را فراخوانی می کند.
holder.sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
- این یک خطا می دهد، زیرا
convertDurationToFormatted()
باید تعریف شود.Util.kt
را باز کنید و کد و واردات مربوط به آن را از نظر خارج کنید. ( کد > نظر با نظرات خط را انتخاب کنید.) - به
onBindViewHolder()
، از convertNumericQualityToStringconvertNumericQualityToString()
برای تنظیم کیفیت استفاده کنید.
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 باشد.
مرحله 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
پارامترholder
bind()
قرار دهید.Alt+Enter
(Option+Enter
در مک) را فشار دهید تا منوی قصد باز شود. برای تبدیل این تابع به یک تابع پسوندی که دارای امضای زیر است، Convert parametr 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
متصل می کند.
درس بعدی را شروع کنید: