Last active
March 3, 2024 17:55
-
-
Save khaledahmedelsayed/31bcc56a7e6da404658c8badd025e58e to your computer and use it in GitHub Desktop.
Load More (Pagination) ListAdapter and RecyclerView
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Use these classes to implement pagination without paging libs | |
| */ | |
| import android.annotation.SuppressLint | |
| import android.view.LayoutInflater | |
| import android.view.View | |
| import android.view.ViewGroup | |
| import androidx.recyclerview.widget.DiffUtil | |
| import androidx.recyclerview.widget.ListAdapter | |
| import androidx.recyclerview.widget.RecyclerView | |
| import androidx.viewbinding.ViewBinding | |
| import android.content.Context | |
| import android.util.AttributeSet | |
| class LoadMoreRecyclerView @JvmOverloads constructor( | |
| context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 | |
| ) : RecyclerView(context, attrs, defStyleAttr) { | |
| fun setupLoadMoreWithViewModel(vm: LoadMoreVM<*>) { | |
| setupLoadMore( | |
| predicate = vm.canLoadMore(), | |
| onLoadMore = { vm.loadMore() }) | |
| } | |
| } | |
| abstract class LoadMoreListAdapter<T, VB : ViewBinding>(diffCallBack: DiffUtil.ItemCallback<T>) : | |
| ListAdapter<T, RecyclerView.ViewHolder>(diffCallBack) { | |
| override fun getItemViewType(position: Int): Int { | |
| return when (getItem(position)) { | |
| null -> ITEM_VIEW_TYPE_LOADING | |
| else -> ITEM_VIEW_TYPE | |
| } | |
| } | |
| inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) | |
| inner class LoadingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) | |
| abstract fun getItemView(inflater: LayoutInflater, parent: ViewGroup) : View | |
| abstract fun getLoaderView(inflater: LayoutInflater, parent: ViewGroup) : View | |
| abstract fun getViewBinding(itemView: View): VB | |
| abstract fun bindItemView(binding: VB, item: T, holder: RecyclerView.ViewHolder) | |
| override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { | |
| val inflater = LayoutInflater.from(parent.context) | |
| return when (viewType) { | |
| ITEM_VIEW_TYPE -> ItemViewHolder(getItemView(inflater, parent)) | |
| else -> LoadingViewHolder(getLoaderView(inflater, parent)) | |
| } | |
| } | |
| override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { | |
| if (holder.itemViewType == ITEM_VIEW_TYPE) | |
| bindItemView(getViewBinding(holder.itemView), getItem(position), holder) | |
| } | |
| @SuppressLint("NotifyDataSetChanged") | |
| fun addChangedList(notificationList: List<T>?) { | |
| removeLoadingView() | |
| submitList(notificationList) | |
| notifyDataSetChanged() | |
| } | |
| fun clearOldListAndAddNew(list: List<T>) { | |
| removeLoadingView() | |
| submitList(null) | |
| submitList(list) | |
| } | |
| private fun removeLoadingView() { | |
| if (currentList.isNotEmpty() && getItem(currentList.size - 1) == null) { | |
| val listWithOutLoading = ArrayList(currentList).apply { removeAt(currentList.size - 1) } | |
| submitList(listWithOutLoading) | |
| } | |
| } | |
| private fun addLoadingView() { | |
| if ((currentList.size - 1) != -1 && getItem(currentList.size - 1) != null) { | |
| submitList(currentList + null) | |
| notifyItemInserted(currentList.size) | |
| } | |
| } | |
| companion object { | |
| private const val ITEM_VIEW_TYPE = 0 | |
| private const val ITEM_VIEW_TYPE_LOADING = 1 | |
| fun LoadMoreRecyclerView.setupLoadMore(predicate : Boolean, onLoadMore : () -> Unit) { | |
| addOnScrollListener(object : RecyclerView.OnScrollListener() { | |
| override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { | |
| super.onScrolled(recyclerView, dx, dy) | |
| if (!recyclerView.canScrollVertically(1) && predicate) { | |
| onLoadMore() | |
| if (adapter is LoadMoreListAdapter<*, *>) | |
| (adapter as LoadMoreListAdapter<*, *>).addLoadingView() | |
| } | |
| } | |
| }) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment