آشنایی با RecyclerView در اندروید
در این مقاله قصد داریم تا پیاده سازی لیست ها را در اندروید مورد بررسی قرار دهیم. دو کلاس در اندروید وجود دارد که میتوانیم از آنها برای نمایش لیستی از آیتم ها استفاده کنیم. این دو کلاس ListView و RecyclerView هستند. در این مقاله مزیت استفاده RecyclerView نسبت به ListView را مورد بررسی قرار خواهیم داد.
کلاس RecyclerView را میتوان یک نسخه پیشرفته از ListView و GridView دانست. از این کلاس برای نمایش داده ها با ظاهر پیچیده و در قالب یک لیست استفاده میشود. در حقیقت طبق تعریفی که برای RecyclerView آمده است٬ این کلاس حجم زیادی از داده ها را به یک صفحه محدود میکند.
این کلاس همراه با Material Design معرفی شده است.
برای استفاده از این کلاس در پروژه ابتدا باید dependency آن را به build.gradle اضافه کنیم:
implementation 'com.android.support:recyclerview-v7:LATEST_VERSION'
که LATEST_VERSION برابر آخرین نسخه منتشر شده آن است. آخرین نسخه را میتوانید از اینجا دریافت کنید.
مزیت RecyclerView نسبت به ListView
Orientation
یکی از ویژگی هایی که RecyclerView را کاملا از ListView متمایز میکند٬ نحوه قرار گرفتن داده های آن به صورت عمودی یا افقی است. همانطور که میدانید در ListView ها تنها این امکان وجود دارد تا لیست به صورت عمودی باشد. به این معنا که داده اول در بالای لیست و داده دوم در پایین آن قرار میگیرد. اما در RecyclerView میتوانیم چینش داده ها را به صورت افقی تنظیم کنیم. به این معنا که داده اول در سمت راست لیست و داده دوم در سمت چپ داده اول قرار گیرد. این عمل با تنظیم Layout Manager در RecyclerView قابل انجام است:
recyclerView.setLayoutManager(new LinearLayoutManager(Context, LinearLayoutManager.HORIZONTAL, reverseLayout)); recyclerView.setLayoutManager(new LinearLayoutManager(Context, LinearLayoutManager.VERTICAL, reverseLayout));
در کد فوق٬ در خط اول مشخص کردهایم که چینش به صورت افقی باشد و در خط دوم چینش را به صورت عمودی تعیین نمودهایم. متغیر سوم که از نوع boolean است مشخص میکند لیست از آخر به اول ایجاد شود. در حالت عادی این متغیر را برابر false قرار میدهیم. برای مثال اگر بخواهیم لیست به صورت RTL باشد باید ترتیب چینش آن را به صورت Horizontal و reverse layout را برابر با true قرار دهیم.
ViewHolder
مزیت دیگر RecyclerView الزام استفاده از ViewHolder است. ViewHolder ها وظیفه نمایش داده ها در ظاهر تعیین شده برای لیست را برعهده دارند. در واقع میتوان اینگونه گفت که ظاهر لیست توسط ViewHolder ها مشخص میشود. هر ViewHolder وظیفه نمایش یک سطر در لیست را برعهده دارد. این کلاس که از کلاس RecyclerView.ViewHolder ارث بری میکند. در بعضی از کد ها ممکن است کلاس ViewHolder را به صورت static تعریف کنند. چند علت برای این روش ذکر شده است. علت اول این است که ممکن است بخواهیم از این کلاس در لیست های دیگر نیز اضافه کنیم. اما این روش توصیه نمیشود. زیرا بهتر است برای هر لیست٬ ViewHolder منحصر به فرد آن تعریف شود. این روش ممکن است تا حدودی SRP را نقض کند. به این صورت که ممکن است لیست دومی که از این ViewHolder استفاده میکند٬ نیاز به تغییراتی داشته باشد و همین امر باعث شود ساختار کد ViewHolder دچار پیچیدگی اضافه و یا بهم ریختگی شود.
در بعضی موارد نیز ممکن است برنامه نویس بخواهد از بیرون کلاس adapter تغییراتی را مستقیما روی ViewHolder اعمال کند که چنین تغییرات مستقیمی میتواند نقض کننده Encapsulation باشد. لذا با مشخص شدن مشکل این روش٬ تعریف static و یا غیر static آن تفاوتی ایجاد نمیکند.
علت سوم که ممکن است ViewHolder به صورت static تعریف شود٬ بحث حافظه است. کلاس اگر به صورت static تعریف شود٬ مقدار حافظه کمتری را اشغال میکند. اما یک ویژگی بسیار مفید در بحث مدیریت حافظه در RecyclerView٬ عمل recycle آن است. بنابراین بحث اشغال حافظه توسط آیتم های لیست جزو دغدغه های ما نیست.
public class CustomViewHolder extends RecyclerView.ViewHolder { private final ImageView avatar; private final TextView title; private final TextView description; public CustomViewHolder(@NonNull View itemView) { super(itemView); avatar = itemView.findViewById(R.id.avatar_img); title = itemView.findViewById(R.id.title_txt); description = itemView.findViewById(R.id.description_txt); } public void bindData(Movie movie) { avatar.setImageDrawable(movie.getAvatar()); title.setText(movie.getTitle()); description.setText(movie.getDescription); } }
اجزا ظاهری در یک فایل xml چینش میشوند که این فایل ظاهری در adapter مشخص میشوند. یک instance از نوع کلاس view از روی آن xml ایجاد میشود و instance ایجاد شده به کلاس ViewHolder ارجاع داده میشود. به این ترتیب در کلاس ViewHolder میتوانیم اجزار با با استفاده از findViewById برای کلاس ViewHolder تعریف کنیم.
Recycle
یکی از ویژگی های بسیار مهم و مفید RecyclerView عمل recycle آن است. به این صورت که وقتی یک آیتم از صفحه خارج میشود (دیگر قابل مشاهده نیست) از حافظه حذف می شود. به این ترتیب نگرانی ما بابت اشغال حافظه توسط آیتم های ایجاد شده برطرف میگردد. برای مثال فرض کنید در یک لیست ۱۰ آیتم وجود دارد. وقتی لیست ایجاد میشود٬ متناسب با ابعاد صفحه٬ ۳ آیتم در صفحه نمایش داده میشود. وقتی لیست به بالا scroll میکند و آیتم چهارم و پنجم نمایش داده میشوند٬ آیتم اول که دیگر در صفحه قابل مشاهده نیست و به بالا رفته است٬ از حافظه پاک میشود. اگر مجدد صفحه به پایین scroll کند٬ آیتم هایی که از دید خارج شدهاند از حافظه پاک میشوند و آیتم اول دوباره ایجاد میشود. در اینجا پاک شده از حافظه٬ به معنای حذف داده های آنها از لیست داده ها نیست.
Adapter
هر یک از ViewHolder ها توسط Adapter ایجاد و مدیریت میشوند. adapter داده های هر مربوط به هر سطر را به ViewHolder مربوط به آن متصل میکند. adapter این کار را با تعیین جایگاه برای ViewHolder انجام میدهد. روش انجام آن به این صورت است که ما یک لیست از داده ها را که به صورت مدل دادهای است را به adapter ارجاع میدهیم. همچنین ظاهر ViewHolder را نیز مشخص کردهایم و طبق تعریف ViewHolder شیوه نمایش داده توسط ظاهر طراحی شده برای سطر ها را نیز تعریف کردهایم. ظاهر ViewHolder در متد onCreateViewHolder برای adapter مشخص میشود و یک instance از روی آن ایجاد میشود که از نوع کلاس View است. سپس با استفاده از این کلاس ViewHolder را ایجاد میکنیم. این متد ViewHolder ایجاد شده را برمیگرداند:
public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View view = LayoutInflater.from(viewGroup.getContext()).inflate( R.layout.item_movie, viewGroup, false); return new CustomViewHolder(view); } }
همانطور که در تعریف کلاس adapter مشاهده میکنید٬ این کلاس یک کلاس generic است که نوعی از RecyclerView.ViewHolder را دریافت میکند. برای مثال کد فوق را میتوانیم به این صورت نیز بنویسیم:
public class CustomAdapter extends RecyclerView.Adapter<CustomViewHolder> { @NonNull @Override public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { } }
سپس باید مشخص کنیم که چه تعدادی از این ViewHolder ها برای لیست نیاز است. همانطور که گفته شد adapter هر یک از داده های موجود در لیست دادهای را به یک ViewHolder اختصاص میدهد. بنابراین باید برای آن تعریف کنیم که چه تعداد ViewHolder مورد نیاز ما است. برای مشخص کردن تعداد ViewHolder ها از متد getItemCount استفاده میشود:
private List<Movie> movies; @Override public int getItemCount() { return movies.size(); }
در کد فوق مشخص کردهایم که تعداد ViewHolder ها برابر با تعداد آیتم های موجود در لیست دادهای باشد.
در نهایت adapter به هریک از ViewHolder ها یک شماره خانه اختصاص میدهد که متناسب با آن شماره٬ بتواند داده هر سطر را به ViewHolder آن اختصاص دهد. این عمل با استفاده از متد onBindViewHolder انجام میشود:
@Override public void CustomViewHolder(@NonNull CustomViewHolder viewHolder, int position) { viewHolder.bindData(movies.get(position)); }
در کد فوق٬ postion بر اساس تعدادی که برای viewHolder ها تعریف کردهایم٬ مقدار دهی میشود. در اینجا باید توجه کنیم که اگر تعداد تعریف شده کمتر از تعداد داده های موجود در لیست دادهای باشد٬ مشکلی به وجود نمیآید و فقط تعداد داده ها به اندازه تعداد تعریف شده برای adapter نمایش داده میشود. اما اگر تعداد مشخص شده بیشتر از تعداد داده ها باشد و دستورات شرطی برای کنترل آن قرار ندهیم٬ برنامه با خطا مواجه میشود. برای مثال فرض کنید تعداد داده های ما ۵ مورد است و ما برای adapter تعداد ۶ ViewHolder را تعریف کردهایم. تا آیتم پنجم مشکلی برای ایجاد ViewHolder ها بوجود نمیآید. پس از آیتم پنجم٬ adapter میخواهد آیتم ششم را ایجاد کند. اما چون این مقدار در لیست دادهای وجود ندارد٬ برنامه با خطا مواجه میشود.
در این مقاله به استفاده ابتدایی از recyclerView پرداختیم. در مقاله های بعد مباحث پیشرفته تر recyclerView را مورد برررسی قرار خواهیم داد.
با ما همراه باشید.
دیدگاهتان را بنویسید
برای نوشتن دیدگاه باید وارد بشوید.