Skip to content

Instantly share code, notes, and snippets.

@NickHolcombe
Last active October 18, 2023 00:16
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NickHolcombe/0e1c27892d6b73271f10e12268f40187 to your computer and use it in GitHub Desktop.
Save NickHolcombe/0e1c27892d6b73271f10e12268f40187 to your computer and use it in GitHub Desktop.
Example of a RecyclerView Adapter based on ListAdapter with a header
package example
import android.support.v7.recyclerview.extensions.AsyncDifferConfig
import android.support.v7.recyclerview.extensions.AsyncListDiffer
import android.support.v7.util.DiffUtil
import android.support.v7.util.ListUpdateCallback
import android.support.v7.widget.RecyclerView
/**
* {@link RecyclerView.Adapter RecyclerView.Adapter} base class based on
* {@link ListAdapter} for presenting List data with header rows in a
* {@link RecyclerView}, including computing diffs between Lists on a background thread.
*
* @param <T> Type of the Lists this Adapter will receive.
* @param <VH> A class that extends ViewHolder that will be used by the adapter.
* @param diffCallback DiffUtil.ItemCallback<T> class used by DiffUtils for comparing <T> items
* @param headerOffset Int number of header rows before list
*/
abstract class ListAdapterWithHeader<T, VH : RecyclerView.ViewHolder>(
private val diffCallback: DiffUtil.ItemCallback<T>,
private val headerOffset: Int = 1
) : RecyclerView.Adapter<VH>() {
private val mHelper by lazy {
AsyncListDiffer<T>(
OffsetListUpdateCallback(this, headerOffset),
AsyncDifferConfig.Builder<T>(diffCallback).build()
)
}
/**
* Submits a new list to be diffed, and displayed.
*
* If a list is already being displayed, a diff will be computed on a background thread, which
* will dispatch Adapter.notifyItem events on the main thread.
*
* @param list The new list to be displayed.
*/
fun submitList(list: List<T>?) {
mHelper.submitList(list)
}
/**
* Accounts for header offset
*/
fun getItem(position: Int): T {
return mHelper.currentList[position - headerOffset]
}
/**
* Returns the total number of items in the data set held by the adapter (includes header offset)
*
* @return The total number of items in this adapter (plus the header offset)
*/
override fun getItemCount(): Int {
return mHelper.currentList.size + headerOffset
}
/**
* ListUpdateCallback that dispatches update events to the given adapter with a position offset to
* allow for the number of header items.
*
* @see DiffUtil.DiffResult#dispatchUpdatesTo(RecyclerView.Adapter)
*/
private class OffsetListUpdateCallback(
private val adapter: RecyclerView.Adapter<*>,
private val offset: Int
) : ListUpdateCallback {
fun offsetPosition(originalPosition: Int): Int {
return originalPosition + offset
}
override fun onInserted(position: Int, count: Int) {
adapter.notifyItemRangeInserted(offsetPosition(position), count)
}
override fun onRemoved(position: Int, count: Int) {
adapter.notifyItemRangeRemoved(offsetPosition(position), count)
}
override fun onMoved(fromPosition: Int, toPosition: Int) {
adapter.notifyItemMoved(offsetPosition(fromPosition), offsetPosition(toPosition))
}
override fun onChanged(position: Int, count: Int, payload: Any?) {
adapter.notifyItemRangeChanged(offsetPosition(position), count, payload)
}
}
}
@aubryll
Copy link

aubryll commented Nov 24, 2019

Hi NickHolcombe, I converted your adapter to Java and put it https://gist.github.com/aubryll/f70b81bfa5ff86cdb4efd86148d60163 all thanks to you, I was almost loosing my mind looking for a solution to add a header to my pagedlistadapter.

@Amjad50
Copy link

Amjad50 commented Jan 31, 2020

Thank you

@inderdhir
Copy link

Thanks for this!

@Dikyashi
Copy link

Great

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment