آشنایی با RecyclerView در اندروید

با سلام و درود، خب میخوام در مورد RecyclerView در اندروید صحبت کنم که فکر کنم همه ی اندروید کارها حداقل اسمش رو دیدن و ازش استفاده نیز کردن، منتهی خیلیا آشنایی کامل از قابلیت ها و ویژگی های RecyclerView ندارند، امیدوارم این مطلب به فهم بیشتر مفاهیم , آشنایی با RecyclerView کمک بکنه.

لیست ها اجتناب ناپذیر هستند.

اپلیکیشن تلگرام رو که باز کنید یک لیست از چت هاتون باز میشه. یا اپلیکیشت پخش موسیقیتان که لیستی از موسیقی هایتان را در بر دارد. حتی صفحه خانه (Home Screen) از گوشی هوشمندتون نیز یه لیست محسوب می شود. لیست ها راحت ترین راه نمایش داده روی ابزار ها با صفحه نمایش کوچک (همانند گوشی هوشمند) محسوب می شوند. پس داده های بسیار زیاد را می توانند روی یک صفحه کوچک به نمایش بگذارند.

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

گوگل

گوگل RecyclerView را در کنار CardView  و  Design Support Library در سال 2014 با عرضه اندروید Lollipop منتشر کرد. ایده پشت RecyclerView ساده است، به جای ساخت و ایجاد View برای هر دفعه که کاربر اسکرول می کند، view یک مرتبه ایجاد می شود و هر بار که نیاز بود recycle و استفاده مجدد می شوند.  RecyclerView  به غیر از الگوی استفاده مجدد و یا الگوی ViewHolder ، برخی بهبودهای دیگر نسبت به لیست های سابق را ارائه می دهد، مانند Layout Manager، Item Decoration، Item Animator و غیره. خب دهید وارد موضوع اصلی بشیم و ببینیم که RecyclerView در مقابل لیست های سابق چه چیزی را ارائه میدهد

1- RecyclerView چیست؟

RecyclerView کتابخانه ای برای طراحی لیست می باشد که اساسا پنجره ای با سایز ثابت را برای بارگیری مجموعه داده ی بزرگی در آن فراهم می کند. این view ای که در ابتدا ایجاد شده است را هر زمان که view مورد نظر خارج از محدوده یا پنجره شد recycle می کند و سپس اگر نیاز باشد، آنها رابرای آنکه به نظر نرسد که View پایان یافته و ادامه دارد مجدد reuse می کند و به صورت عمودی محتویات درون پنرج به صورت ادامه دار نمایش داده می شوند.

سه پنجره را در زیر می بینید. هر سه دارای recyclerView هستند. اولی از StaggeredLayoutManager استفاده کرده است در حالی که دومی و سومی از LinearLayoutManger استفاده کرده اند. در ادامه به این می پردازیم که LayoutManger چی هست.

بهمچنین ممکن است به این توجه کرده باشید که در پنجره اولی view ها مشابه نیستند؛ آنها آیتم های داده متفاوتی را درون خودشان دارند. بله، RecyclerView این قابلیت را برای شما ممکن می سازد، به طور خاص، الگوی ViewHolder از RecyclerView. برای هر view یک ViewHolder جداگانه تعریف کنید و مدیریتش را به RecyclerAdapter بسپارید. به طریقه این کار در ادامه خواهیم پرداخت.

 
همانطور که در ابتدا گفته شد، RecyclerView یک کتابخانه پشتیبانی مستقل از تیم گوگل می باشد که درسال 2014 منتشر شد. پس ما نیاز داریم تا آن را به صورت جداگانه در اپلیکیشن خودمان اضافه کنیم، این کتابخانه با هیچ ورژنی از SDK همراه نبوده و باید به صورت دستی به اپ اضافه بشه. افزودن خط زیر به بلاک dependencies  فایل build.graddle از اپلیکیشن و انجام دادن sync گردل، gradle به ما اجازه استفاده از این کتابخانه را می دهد.
implementation 'com.android.support:recyclerview-v7:26.1.0'

For Android Studio below version 3.0, this needs to be added instead, برای اندروید استادیو ورژن های کمتر از 3 نیاز داریم تا خط زیر را به جای خط فوق اضافه کنیم.

compile 'com.android.support:recyclerview-v7:26.1.0'

 

2- چقدر متفاوت از ListView می باشد؟

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

مطمئنا، ListView نیز می تواند دارای ViewHolder باشد، اما این به صورت پیش فرض امکان پذیر نمی باشد و ما را مجبور می کند که کد بیشتری را برای بهبود کارایی لیست استفاده کنیم. از سوی دیگر، ViewHolder ها قسمت مهمی از کل معماری RecyclerView می باشد، به این معنی که سیستم شما را مجبور می کند برای هر layout  ای که ممکن است به کار بگیرید، یک ViewHolder داشته باشید. معماری سپس از این ViewHolder ها جهت ساخت کپی View از layout ای که میخواهد استفاده شود، استفاده می کند و به جای ایجاد مجدد آنها، از view های کش (cached) شده استفاده می کند و آنها را با داده های جدید bind می کند.

دیگر سودی که RecyclerView نسبت به ListView دارد، LayoutManager ، Item Decoration و Item Animator می باشد. فقط به ساخت یک Grid list با سه ستون فکر کنید که زمانی که آیتم جدیدی insert می شود یا دارای فاصله مناسب بین طرفین child view با مرزها است، متحرک می شود.مشکل به نظر میاد، درسته!؟ خب، RecyclerView انجام این کار را با استفاده از این سه ویژگی اصلی اش که ازشون نام بردیم، بسیار ساده میکند. این کار را انجام دهید:

GridLayoutManager glm = new GridLayoutManager(context, 3);
recyclerView.setLayoutManager(glm);

و ما رفتار RecyclerView را با این کار شبیه به یک grid می کنید، یا این کد زیر:

RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator(); 
recyclerView.setItemAnimator(itemAnimator);

جهت افزودن انیمه-animate (متحرک شدن) برای ورود یا خروج Child View ها.

 

3- تعریف و ویژگی ها.

یه RecyclerView چیزی نیست جز یک ViewGroup ساده که ScrolView را پیاده سازی می کند. چیزی که RecyclerView را جادویی می کند، توانایی هایش در مدیریت View های داخل آن است.

RecyclerView

RecyclerView یک View را با recycle یا scrape کردن آن مدیریت می کند. یک View که recycle شده است، view ای می باشد که قبلا inflate شده است و می توان آن را کش (cache) کرد تا با یا بدون bind کردن داده های دیگر مجدد استفاده شود.

Scrap view

 Scrap view از سوی دیگر یک view ای می باشد که از layout در زمان ایجاد detach شده است. در موردش به عنوان یه View استاتیک فکر کنید. حال اگر نیازی بهش باشد، RecyclerView از آن با یا بدون data binding استفاده خواهد کرد. سودی که Scrap view ارائه می دهد، این است که ما می توانیم در سیستم یک view داشته باشیم اما هیچ سرباری روی کارایی سیستم د رزمانی که از آن view استفاده نمی شود، ندارد.

مبنای معماری RecyclerView مشابه دیگر VirewGroup ها می باشد (یادتان باشه که یه ViewGroup هست.) ویژگی های خاصی که همارهش هست عبارتند از reverseLayout، fastScrollingEnabled، layoutManager و stackFromEnd.

 

4- LayoutManager چیست؟

LayoutManager قلب و روح یک recyClerView به حساب می آید. این مسئولیت را دارد تا به recyclerView بگوید که چه زمانی باید یک Child view را recycle کند (هر وقت از محدوده صفحه خارج شد). بدون این، recyclerView نمی تواند تشخیص دهد که چه نوع layout هایی قرار است که برای قرار گرفتن روی صفحه نمایش پشتیبانی شوند، یک linear layout باشه یا یک grid با spans یا اینکه این grid layout یک stagged layout هست که برایش نگه داشتن view ها در یک ردیف با ارتفاع یکسان قابل قبول نیست.

ایده پایه پشت LayoutManager این بوده که قابلیت تغییر Layout از مجموعه داده مشابه به صورت داینامیک با تنها یک اپلیکیشن setter را ممکن سازد.

3 تا layoutManager برای recyclerView در دسترس است:

  • LinearLayoutManager: برای مدیریت LinearLayout نسبتا ساده (عمودی یا افقی)
  • GridLayoutManager: برای ایجاد یا مدیریت grid Layout که دارای spans است (ستون ها)
  • StaggeredLayoutManager: برای مدیریت staggered Layout شبیه اسکرین شات بالایی که گذاشته شده است.(اولین پنجره)

در مورد LayoutManager بعدا در بلاگ بیشتر صحبت خواهم کرد

5- Recycler Adapter 

مهمترین قسمت از کل معماری RecyclerView همین Adapter هست. یک RecyclerView Adapter شبیه باقی adapter ها مسئولیت bind کردن مجموعه داده به View هایی که در پنجره باید نمایش داده بشوند را دارد. یک adapte عمومی، اساسا فقط یک iterator هست که محدودیت هایش را زا getCount() که معمولا وابسته به سایز مجموعه داده  است می گیرد و view های جدید را می سازد و داده ها را از مجموعه داده های مختلف به آنها bind می کند. ListView یا ViewPager از همین نوع adapter استفاده می کنند. از سوی دیگر RecyclerView از سیستم پیچیده تری برای bind کردن داده و مدیریت view ها استفاده می کند.

همانطور که در بالا گفته شد، RecyclerView  به صورت پیشفرض از ViewHolders استفاده می کند. بنابراین Recycler adapter به جای ایجاد view ها ، viewHolder ها را می سازد که view های inflate شده را نگه می دارد. پس از ایجاد،  ViewHolder در cache (حافظه پنهان) قرار داده شده و اگر نیاز باشد میتوان ازش استفاده مجدد کرد

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 // return new ViewHolder reference here based on the viewType
}

چیزی که نیازهست در ذهنمان نگه داریم، این هست که viewHolder بر مبنای نوع itemview ایجاد شده یا مرجع داده می شود، نه بر حسب موقعیت (position)، بنابراین با یان کار مزایایی اضافه می شود مانند  برای adpater مشاهده view ها جهت استفاده مجدد آسانتر می شود و میتوان view های ناهمگونی (heterogeneous)را با فراهم کردن view type های جدید به RecyclerView اضافه کرد.

زمانی که با RecyclerView Adapter کار می کنیم نیاز هست که این 4 اصطلاح زیر را بلد باشیم:
  • ItemViewType : یک viewType مبتنی بر position برای Reuse یا recycle کردن view ها (مجبور لغات تخصصی را با لاتین به کار ببرم که برای برنامه نیوس های فهم و درکشون راحت تر باشه)
  • ViewHolder: کلاس والد که  layout inflation و استفاده از vhild view را مدیریت می کند.onCreateViewHolder (کد بالایی) ایجاد یا recycle کردن ViewHolder را مدیریت می کند و view inflation را بر مبنای itemViewType بازگشت داده شده(returned) توسط getItemViewType(…) نیز مدیریت می کند.
  • Bind ViewHolder:  داده را به Child view از ViewHolder مورد نظر bind  می کند. متور عمومی (public method) با نام onBindViewHolder(…) مسئولیت bind کردن viewholder ها را دارد.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int pos) {
   if (holder instanceof MyViewHolder1) {
     // bind data to the views of MyViewHolder1
   }
}

این پر هزینه ترین قسمت از بلاک کد مربوط به ل Adapter می باشد، چون که این هر زمان یک view را  جه نمایش روی صفحه نمایش فراخوانی خواهد کرد و د رنتیجه باید مراقبت های ویژه ای برا اطمینان از عملکر مناسب و کارایی سیستم نیز انجام دهد. یکی از آن معبارهای ویژه این است که نگذارد کدهای slow درونش اضافه شود، مانند interfaceها (برای مثال رویداد onClick).

  • Count: متودی که چندرین بار فراخوانی می شود تا سایز محدودیت لیست را بدست بیاورد. این متود به طور کلی سایز مجموعه داده مورد استفاده  برای  adapter را بر می گرداند(return).
@Override
public int getItemCount() {
 if (dataset == null)
 return 0;
 return dataset.size();
}

6- تغییر داده ها به صورت داینامیک (اطلاع رسانی)

این در واقع هیچ ویژگی مستقلی از RecyclerView نمی باشد، بلکه به وسیله RecyclerView انجام می شود. 

زمانی که در جستجوی داده بیشتر یا آپدیت یا حذف داده های در حال حاضر هست. ما نیاز داریم تا به adapter اطلاع بدیم که داده هایمان تغییر کرده یا بروز شده اند. چندین روش برای RecyclerView وجود دارد. Adapter برای  اطلاع رسانی تغییرات همانند notifyDataSetChanged() یا notifyItemInserted(…) یا notifyItemRangeChanged(…). در مودرشون در آینده ان شاالله صحبت خواهد شد.

7-به کجا رسیدیم؟

من فقط در مورد RecyclerView یه سری اطلاعات رو داخل این مطلب در اختیارتون گذاشتم و اینکه چطور بهتر از ListView میشه به راحتی ازش استفاده کرد.

یه نکته اصلی این هست که با افزایش ویژگی ها، پیچیدگی نیز افزایش پیدا می کند، که این برای RecyclerView نیز صادق هست. ما ویژگی ها خوبی رو به دست میاریم که کارایی را بهبود می بخشه. اما در کنارش به مقدار کد بیشتری نیز نیاز داریم که شامل کدهای منطقی سیستمی نیز می شود. برای مثال اضافه کردن Header به یک ListView چقدرساده است. ولی در کنارش همین کار برای RecyclerView کلی کار نیاز داره.

نکته آخر

نیز این هست که درسته RecyclerView خیلی خوب و کاربردیه، اما برای هر موردی (use case) نباید از آن استفاده کرد. وقتی که مجموعه داده هایمان کوچک است و زیاد تغییر نمی کنند بهتر است از ListView استفاده کنیم.  چون هم راحت تر است و هم منابع سیستمی را کمتر اشغال می کند. RecyclerView بهتره برای لیست ها با داده های بسیار زیاد که دارای View type های متفاوتی است و کاربر نیاز به اسکرول کردن زیاد دارد استفاده شود.

 

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

 منابع: مرجع-1 مرجع-2

دیگر مطالب مرتبط با اندریود رو اینجا می تونید پیدا کنید.