Skip to content

Instantly share code, notes, and snippets.

@khaledahmedelsayed
Last active March 3, 2024 17:55
Show Gist options
  • Select an option

  • Save khaledahmedelsayed/31bcc56a7e6da404658c8badd025e58e to your computer and use it in GitHub Desktop.

Select an option

Save khaledahmedelsayed/31bcc56a7e6da404658c8badd025e58e to your computer and use it in GitHub Desktop.
Load More (Pagination) ListAdapter and RecyclerView
/**
* 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