توی برنامه نویسی قطعا به لیست ها نیاز داریم و تقریبا همه ی ما لیست هامون ادامه دار هستن، توی برنامه نویسی اندروید مدت های زیادیه که بجای ListView از RecyclerView استفاده میشه که در کنارش یه دونه RecyclerView.Adapter هم هست و وظیفه ی چیدمان آیتم ها و مدیریتشون روی توی RecyclerView بر عهده داره.
تا اینجای کار همه چی خوبه ولی مشکل از اونجایی شروع میشه که لیست هامون صفحه بندی پیدا میکنن، البته شاید نشه اسمش رو مشکل گذاشت؛ چالش کلمه ی بهتریه!
توی RecyclerView یک Listener ست میشه کرد که با اون متوجه میشیم که آیا به انتهای لیست رسیدیم یا نه، هرچند از طریق آداپتر هم میشه این رو فهمید و شاید راه های دیگه ...
گوگل برای حل این چالش یک کامپوننت به Android JetPack اضافه کرد به اسم Paging اما خب یا هنوز خیلی جا نیوفتاده یا اینکه استقبال ازش نشده، من که خودم تست کردم ترغیب به استفاده مجدد نشدم که البته هیچ قطعیتی وجود نداره برای استفاده نکردن ازش :)
اینها مقدمه بود برای معرفی یک راهکار متفاوت تر!
زمانی که از سرور دیتا ها دریافت میشه معمولا صفحه های داده ای که میاد تعداد ثابتی دارند و تنها ممکنه صفحه آخری که دریافت میشه تعداد مقادیرش کمتر باشه
https://yourdomain/api/user/?page=1
یعنی اگر 32 (از 0 تا 31) کاربر داشته باشیم و از سرور بخواهیم لیست کاربر ها رو بهمون بده و سرور هم بر اساس اعلام خودمون یا تصمیم خودش مثلا هر سری 10 کاربر به ما برگردونه پس به این صورت خواهد شد:
صفحه ی اول 10 کاربر - 0 تا 9
صفحه دوم 10 کاربر - 10 تا 19
صفحه ی سوم 10 کاربر - 20 تا 29
صفحه چهارم 2 کاربر - 30 تا 31
یکسری از api ها زمانی که داده رو برمیگردونن یک پارامتر مثلا با عنوان TotalPage برمیگردونن توی هر Api که تعداد کل صفحات رو اعلان میکنه و برای مثال ما 4 رو برمیگردونه که ما الکی برای صفحات 5 و بالاتر درخواست ارسال نکنیم چون داده ای وجود نداره
اگر دقت کرده باشین بدون استفاده از پارامتر TotalPage توی جواب api میتونیم بفهمیم که دیگه آیا درخواستی رو به سرور برای گرفتن صفحه 5 ارسال کنیم یا نه.
وقتی ما میدونیم هر صفحه 10 تا کاربر رو برمیگردونه پس به احتمال خیلی زیاد صفحه ای که تعداد کمتری رو برگردونه اون صفحه ی آخره، یعنی وقتی صفحه 4 رو درخواست میکنیم و تنها 2 تا کاربر رو نشون میده پس اون صفحه صفحه ی آخر از لیست کاربرامون هست.
جالب بود نه؟
این حالت 2 نکته داره یکی مفید و یکی ممکنه مضر باشه
نکته ی مفیدش اینکه یک کوئری رو از سرور کم میکنه اونم اینکه نیاز نیست هر بار که api صدا زده شد بره و تعداد همه ی کاربرا رو برگردونه، البته ممکنه خیلی به چشم نیاد توی سرور های درست طراحی شده و البته بزرگ!
نکته ی منفی اینه که اگر تعداد کاربرا بخش پذیر بر تعداد کاربرای هر صفحه باشه اونوقت یک رکوئست اضافه به سرور فرستاده می شه، به چه صورت؟
فرض کنید لیست کاربرا 40 عدد باشه و وقتی صفحه 4 رو صدا بزنیم 10 عدد کاربر رو برمیگردونه و چون تعداد کاربرا 10 تاست این صفحه رو صفحه ی آخر نمیدونیم و یک رکوئست به سرور می فرستیم که صفحه ی 5 رو بده!
این مورد رو هم شاید بشه در نظر نگرفت چون احتمالش خیلی نیست!
خب اینم از مقدمه ی دوم!
حالا بر اساس مفاهیم بالا به تازگی یک لایبرری ساده ساختم که چالش صفحه بندی رو توی 3 مرحله حل میکنه بر اساس یک Listener میتونید صدا زدن Api رو مدیریت کنید.
ابتدا کتابخونه رو به برنامه اضافه کنید:
1- به Build.Gradle خط زیر رو اضافه کنید
allprojects { repositories { ... maven {url 'https://jitpack.io' } } }
2- کتابخانه رو به Gradle برانامه اضافه کنید
dependencies { implementation 'com.github.softrunapp:Paginated-RecyclerView:1.0.0' }
خب بعد از اضافه کردن کتابخانه میریم سراغ مراحل کار با کتابخانه.
1- ابتدا کلاس Adapter رو میسازیم و به جای اینکه Extends بشه از RecyclerView.Adapter اینبار از PaginatedAdapter مشتق گرفته شده و توی پارامتر ها علاوه بر ViewHolder کلاس مدل رو هم قرار میدیم:
public class MyAdapter extends PaginatedAdapter<User, MyAdapter.ViewHolder> { ... @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { User user = getItem(position); //get item with position } ...
همونطور که توی کد بالا میبینید کلاس User رو هم به عنوان پارامتر به کلاس PaginatedAdapter پاس داده ایم. همینطور توی متد onBindViewHolder با استفاده از متد getItem میتونیم به مدل کنونی دسترسی داشته باشیم.
2- حالا آداپترمون رو یک نمونه ازش میسازیم و بهش RecyclerView رو پاس میدیم و همینطور یک لیستنر هم ست میکنیم:
MyAdapter adapter = new MyAdapter(); adapter.setDefaultRecyclerView(this, R.id.recyclerView); // آی دی لیست و اکتیویتی رو ست میکنیم برای این متد adapter.setOnPaginationListener(new PaginatedAdapter.OnPaginationListener() { @Override public void onCurrentPage(int page) { //صفحه ی کنونی که بارگذاری شده } @Override public void onNextPage(int page) { // اینجا باید صفحه جدید رو از سرور درخواست کنیم } @Override public void () { // همه ی صفحات بارگذاری شده اند } });
توی کد بالا توضیح داده شده هر متد چه زمانی فراخوانی میشه
به صورت پیشفرض صفحه اول رو 1 در نظر میگیره و اندازه هر صفحه رو 10 قرار میده که این مقادیر قابل ویرایش هستند که در ادامه قرارشون میدم
3. ست کردن لیست دیتاهای دریافتی از سرور
adapter.submitItems(yourListData);
پس از دریافت داده ها مثلا User ها لیست User ها رو با متد بالا به آداپتر می فرستیم
تمام شد!
سفارشی سازی:
MyAdapter adapter = new MyAdapter(); //setters adapter.setStartPage(1); //set first page of data. default value is 1. adapter.setPageSize(10); //set page data size. default value is 10. adapter.setRecyclerView(recyclerView); // set your RecyclerView with options //getters adapter.getStartPage(); // return start page adapter.getCurrentPage(); // return last page which loaded adapter.getRecyclerView(); // return recycler view
همینطور برای دیدن مثال کامل از گیت هاب روی لینک زیر کلیک کنید:
https://github.com/softrunapp/Paginated-RecyclerView
اگر سوال یا انتقاد یا پیشنهادی دارین خوشحال میشم باهام در میون بزارید
همینطور میتونید پروژه رو توی گیت هاب فورک کنید و توی بهبود کتابخونه کمک کنید
امیدوارم مفید بوده باشه :)