سپهر تابعیان
سپهر تابعیان
خواندن ۹ دقیقه·۴ سال پیش

واقعا داخل RecyclerView چه اتفاقاتی می افتد؟


Image reference: Microsoft docs
Image reference: Microsoft docs


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

واقعا تا حالا کنجکاو نشدید که RecyclerView چطوری کار می کند؟
چطوری جریانی از دیتا رو به کاربر نمایش می دهد؟

چرا ما باید از تمام این عوامل اطلاع داشته باشیم؟

فقط به این فکر کنید که تمام اپلیکیشن های اندرویدی مدرن تقریبا از RecyclerView

استفاده می کنند،بنابراین نحوه کار با آن بر تجربه میلیون ها کاربر اندرویدی تاثیر می گذارد.

ما حتما درباره اینکه " دیتا چطوری توسط RecyclerView نمایش داده می شود " آموزش های خوب زیادی پیدا می کنیم. اما آیا “black box” برای حل سناریو های پیچیده آن هم در حالی که ما میلیون ها دستگاه اندرویدی با rendering و performance متفاوتی داریم رویکرد مناسبی است؟!

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

یک نقل قول معروف از بنجامین فرانکلین درباره یادگیری هست :

” Tell me and I forget, teach me and I may remember, involve me and I learn”

اما قبل اینکه به درون RecyclerView ورود کنیم ما باید بفهمیم که چرا با وجود ListView ما سراغ RecyclerView می رویم.

با ListView ما یک سری نکات منفی داشتیم:

  • تاخیر در پیمایش(Lag in Scrolling): به تعداد آیتم های دیتا که درون data-set ما است ListView میاد و view ( ردیف ) می سازد.ساخت view ها و استفاده از متد findViewById پرهزینه است.البته با نوشتن منطق می توانیم عملکرد(performance ) در ListView را بهبود ببخشیم.
  • پشتیبانی از انیمیشن های پیشفرض وجود نداشت : ListView با عدم پشتیبانی از انیمیشن ساخته شد و ما می دانیم برای اینکه خودمان یک انیمیشن خوب ست کنیم چقدر زمان باید صرف کنیم.
  • پیمایش فقط در یک جهت( عمودی ) : ListView فقط از پیمایش عمودی پشتیبانی می کند و پیمایش افقی مجاز نیست.

به غیر از موارد ذکر شده بالا موارد دیگه هم هست که توسعه دهندگان اندروید را این نتیجه رساند که بهینه سازی و کارایی های بیشتری نیاز است.


از RecyclerView چه می دانیم؟

طبق داکیومنت های اندروید : RecyclerView یک جزئی از UI است که به ما اجازه پیمایش می دهد.

اساسا یک ViewGroup جدید است و برای ارائه هر view که بر پایه adapter است از آن در حالت های horizontal/vertical /grid /staggered grid با استفاده از الگوی Viewholder استفاده می شود.

اصطلاحات جدید زیادی وجود داره؟ لطفا گیج نشید و به من چند لحظه اجازه دهید تا توضیح بدم :

Image reference: Google
Image reference: Google

ما چهار تا جز اصلی در RecyclerView داریم :

  • جز اول RecyclerView.Adapter: وظیفه binding از دیتاسورس به آیتم ها ( که درونRecyclerView نمایش می دهد ) را به عهده دارد.آداپتر می داند که position آیتم های view درون RecyclerView را چطور به یک مکان خاص درون دیتاسورس مرتبط کند .
  • جز دوم RecyclerView.ViewHolder: به ما در ترسیم UI کمک می کند تا بتوانیم آیتم های اختصاصی خودمان را روی صفحه ترسیم کنیم و استفاده از آن اجباری است.
  • جز سوم RecyclerView.LayoutManager: نحوه چیدمان آیتم ها درون RecyclerView را مشخص می کند.شما می توانید از layout manager های از پیش تعریف شده یا از layout manager شخصی سازی شده استفاده کنید که میشه به صورت linear یا grid باشه.
  • جز چهارمRecyclerView.ItemAnimator: یک سری انیمیشن پیش فرض درون RecyclerView وجود دارد که ما می توانیم آن ها را override کنیم و طبق نیاز آنها را تغییر دهیم که به صورت پیش فرض از DefaultItemAnimator استفاده می شود.

حالا بیایید و همه این ها را با یک مثال خوشمزه درک کنیم

فرض کنید با هم در مقابل یک کیوسک فلافل ایستادیم و یک صف عظیم وجود دارد و همزمان فقط 4 نفر می توانند وارد شوند و سفارش دهند.آنجا شخصی وجود دارد که برای هر 4 نفری که وارد می شوند بشقاب تهیه می کند.راه دیگش اینه که اون شخص بشقاب های اضافی را دستش نگه دارد و پیشاپیش به افراد در صف و دو نفری که فلافل را تمام کردند و ممکن است دوباره سفارش دهند بشقاب دهد.آن هایی هم که فلافلشان را خوردند بشقاب ها را تحویل فروشنده می دهند تا آن را تمیز کند و به دو نفر جدید بدهد.

mage Reference: Google
mage Reference: Google


خب در اینجا فروشنده RecyclerView است، بشقاب ها view ها هستند، فلافل ها همان data هستند چهار نفری که همزمان وارد می شوند (visible) ، Viewport در RecyclerView است و ایده استفاده کمتر از بشقاب ها همان ViewHolder است. شخصی آنها را تمیز می کند و هروقت که نیاز شد دوباره از آنها
استفاده می کند.این مزیت اصلی استفاده از ViewHolder است و دیگه CPU روی ساختن view ها تمرکز ندارد و اینطوری task ها کاهش پیدا می کند.



امیدوارم با اصطلاحات آشنا شده باشید.حالا اجازه دهید بریم سراغ جزئیات.

چطوری RecyclerView کار می کند؟

بیایید روند کار رو درک کنیم:

Image reference: Microsoft
Image reference: Microsoft


خب،data (آیتم) درون data-set (لیست آیتم ها) نگهداری می شود.آداپتر هم data را به view ها متصل می کند و سپس آنها را به layout manager می دهد تا view ها کنترل شوند.

جالبه بدانید که RecyclerView به هر آیتمی که در دیتاسورس است ، یک item view اختصاص نمی دهد.

در عوض فقط مختص به تعدادitem view های صفحه نمایش (Viewport) است .وقتی view برای اولین بار از دید خارج می شود همانطور که در دیاگرام بالا نشان داده شده است،فرآیند بازیافت طی می شود:

  • وقتی view پیمایش می شود و از دید خارج می شود به آن scrap view میگیم.

بیایید Scrap view را بیشتر درک کنیم.Recycler برای ذخیره سازی این نوع view ها Scrap Heap دارد:

خب حالا این چیه؟

این یک کالکشن سبک است، از آنجا که دیتا هنوز به ViewHolder متصل است view ها می توانند بدون برگشت به آداپتر، مستقیما به Layout Manager بروند .بنابراین برای binding نیازی به انتقال مجدد دیتا به آداپتر نیست.View هایی که اینجا قرار داده شدند موقتا جدا می شودند اما درونlayout یکسان دوباره استفاده می- شوند.

ویو در بالا و پایین Viewport از نوع view های جدا شده هستند.
انتظار می رود که این ویو های جدا شده قبل از بازگشت کد مجددا متصل شود.
  • یک آیتم جدید وقتی که می خواهد نمایش داده شود یک ویو را از درون recycle pool برمی دارد .

از آنجا که این ویو قبل از اینکه نمایش داده شود دوباره باید توسط آداپتر به سر جای اول خود برگردد، (re-bound) در اصطلاح به این ویو dirty view می گویند.

مشابه Scrap heap که در بالا اشاره شد،برای این نوع ویو ها یک سیستم ذخیره سازی به اسم Recycle Pool داریم.خب حالا باز این چیه؟!

یک کالکشن متشکل از ویوهایی است که فرض می شود دیتا نادرستی (دیتا با position یا index متفاوت) دارند که بهش میگیم dirty view.این ویو ها همیشه به آداپتر پاس داده می شود که دیتا بتواند به ViewHolder متصل شود و درنهایت به Layout Manager برگردد.

در برخی از موارد یک نمونه از Recycler به Layout Manager داده می شود که بتواند ویو های قدیمی یا ویوهای جدید را بازیابی کند.

حالا برگردیم به ادامه dirty view . لطفا ادامه دهید تا با هم به درک بیشتری برسیم :

  • ویو کثیف(dirty view) بازیابی شده :آداپتر برای آیتم بعدی که قرار است نمایش داده شود ، دیتا را در موقعیت قرار می دهد و این دیتا برای ویوهای این آیتم کپی می کند. در ضمن منابع مربوط به این ویو ها از view holder می آید.
  • لیست آیتم های در شرف نمایش RecyclerView از ویوهای بازیابی شده اضافه می شود.
  • وقتی که کاربر برای رفتن به آیتم بعدی لیست ،عمل پیمایش را انجام دهد ویو های بازیابی شده روی صفحه نمایش ظاهر می شوند.در همین حال ویو دیگری که دور از چشم پیمایش می شود مطابق مراحل بالا بازیابی می شود.
هر دفعه که آداپتر یک item-layout را inflate می کندViewHolder مربوط به آن را هم می سازد.
ViewHolder از FindViewById برای ارجاع دادن ویو های درون Item-layout های inflate شده استفاده می کند.هر دفعه که layout ما برای نمایش دیتای جدید بازیابی شود از این ارجاعات برای دادن دیتای جدید به ویو استفاده می شود



متدهای RecyclerView :

هنگامی که شما یک RecyclerView.Adapter را پیاده سازی می کنید باید متدهای زیر را override کنید :

زمانی که موقعیت آیتم ها درون RecyclerView تعیین شد حالا layout manager این متد ها را صدا می زند.

اگر layout manager نتواند یک ویو پیدا کند، با صدا زدن متد onCreateViewHolder از آداپتر یک ویو می سازد .اگر لازم بود یک ویو را از طریق متد onBindViewHolder بایند می کند و در آخر یک ویو بر می گرداند.



اطلاع رسانی تغییرات در RecyclerView :

  • متد NotifyDataSetChanged :یک سیگنال برای زمانی که دیتا ست شما تغییر کرده است(اجبار در بروز رسانی کامل )
  • متد NotifyItemChanged : یک سیگنال برای زمانی که یک آیتم با موقعیت خاص تغییر کرده است.
  • متد NotifyItemRemoved : یک سیگنال برای زمانی که یک آیتم با موقعیت خاص حذف شده است.

مانند این داریم: notifyItemRangeInserted ،notifyItemRangeRemoved وnotifyItemRangeChanged

درباره این موارد بالا خودتون مطالعه کنید .

اگر دقیقا بدانید که دیتای شما چگونه تغییر کرده است می توانید از موارد بالا یک متد مناسب برای رفرش کردنRecyclerView صدا بزنید.



برخی نکات درباره کارایی بیشتر RecyclerView :

  • متدrecyclerView.setHasFixedSize(true): اگر احتمال می دهید که طول و عرض آیتم در XML تغییر نمی کند و اندازه محتویات RecycerView ثابت است می توانید این خط را به متدهای مقدار دهی اولیه شده RecyclerView اضافه کنید.این متد به RecyclerView میگه که سایز آیتم های من ثابت است و دیگر نیاز نیست که هردفعه که آیتم ها از RecyclerView حذف یا اضافه می شوند دوباره محاسبات اندازه XML انجام .شود
  • متد recyclerView.setItemViewCacheSize(size): قبل از اینکه ویو های خارج از صفحه به استخر بازیابی ویو ها بروند شما تعداد ویو های خارج از صفحه را مشخص کنید. وقتی که RecyclerView را پیمایش می کنید ویویی وجود دارد که از صفحه خارج شده است،RecyclerView آن را نگه خواهد داشت .اگر شما به عقب پیمایش کنید می توانید بدون اجرای مجدد onBindViewHolder به ویو برسید.
  • چک کنید که چطور و چه زمانی می توانیم از dapter.setHasStableIds(true) استفاده کنیم: از این متد می توان به انیمیشن و ماندگاری شی کمک کرد..

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

اگر این مطلب را دوست داشتید لطفا نظر دهید و با ❤️ کردن من رو خوشحال کنید.

مراجع:

ریسایکلرویو ،ListView و آناتومی ریسایکرویو

viewholderrecyclerviewlayoutmanagerریسایکلرویوandroid
توسعه دهنده اندروید و فلاتر هستم و هر چیزی که داخلش خلاقیت وجود داشته باشه برای من جذابه
شاید از این پست‌ها خوشتان بیاید