This is a RecyclerView.Adapter
extension which provides the functionality:
- Item sorting; e.g. alphabetically.
- Item filtering; filtering the list based on a search query.
- Item selection; facilitates single & multi-selection of items.
- Uses
DiffUtil
behind the scenes. This means:- Changes in the list are animated (e.g. items slide up when one is removed).
- Changes are efficient; only changed items are rebound (i.e. no
notifyDataSetChanged()
). - No UI freezes; list change calculations are done in a background thread.
The library is published to Maven Central, so ensure you have the following in your top-level build.gradle
:
allprojects {
repositories {
mavenCentral()
}
}
Add the dependency to your app-level build.gradle
:
dependencies {
implementation 'com.github.mrbean355:enhanced-adapter:1.1.0'
}
Finally, you can create an adapter which extends com.github.mrbean355.android.EnhancedAdapter
instead of the usual RecyclerView.Adapter
.
/** Maximum number of multi-selections allowed. */
private const val MAX_SELECTIONS = 3
/** Callback for calculating the diff between two items in a list. */
private val DIFF_CALLBACKS = MyDiffCallbacks()
class MyAdapter : EnhancedAdapter<MyItem, MyAdapter.MyViewHolder>(DIFF_CALLBACKS, MAX_SELECTIONS) {
/** Normal onCreateViewHolder() stuff */
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(inflater.inflate(android.R.layout.simple_list_item_1, parent, false))
}
/** Normal onBindViewHolder() stuff */
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItemAt(position)
holder.textView.text = item.name
}
/** Normal view holder stuff */
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView as TextView
}
/**
* Callback for calculating the diff between two items in a list.
*
* See: https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil.ItemCallback
*/
class MyDiffCallbacks : DiffUtil.ItemCallback<MyItem>() {
override fun areItemsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
return true
}
}
}
class MyAdapter : EnhancedAdapter<MyItem, MyAdapter.MyViewHolder>(MyDiffCallbacks(), 0) {
// Minimal stuff omitted...
/** Customise how items are compared. */
override fun compareItems(lhs: MyItem, rhs: MyItem): Int {
return lhs.name.compareTo(rhs.name)
}
}
class MyAdapter : EnhancedAdapter<MyItem, MyAdapter.MyViewHolder>(MyDiffCallbacks(), 0) {
// Minimal stuff omitted...
/** Customise how items are tested. */
override fun testItem(item: MyItem, query: String): Boolean {
return item.name.contains(query, ignoreCase = true)
}
}
class MyAdapter : EnhancedAdapter<MyItem, MyAdapter.MyViewHolder>(MyDiffCallbacks(), 0) {
// Minimal stuff omitted...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnClickListener {
// Your action here.
}
}
}
class MyAdapter : EnhancedAdapter<MyItem, MyAdapter.MyViewHolder>(MyDiffCallbacks(), 5) {
// Pass the max number of selection in the constructor (above).
// Minimal stuff omitted...
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val isSelected = isItemSelected(position)
val context = holder.itemView.context
// Do something to the item to indicate its selection status:
if (isSelected) {
holder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.colorAccent))
} else {
holder.itemView.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent))
}
holder.itemView.setOnClickListener {
// Notify that an item has been selected/unselected:
onItemClicked(holder.adapterPosition)
}
}
}