Skip to content

Instantly share code, notes, and snippets.

@AliAzaz
Last active July 25, 2023 06:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AliAzaz/fea768c495c33f049ebec86365c2a37b to your computer and use it in GitHub Desktop.
Save AliAzaz/fea768c495c33f049ebec86365c2a37b to your computer and use it in GitHub Desktop.
Generic RecyclerAdapter implementtion using DiffUtils
private lateinit var adapter: GenericListAdapter<ImagesInfo>
adapter = GenericListAdapter(R.layout.product_view) { item, position ->
viewModel.setSelectedProduct(item)
findNavController().navigate(ImageListFragmentDirections.actionImageListFragmentToImageDetailFragment())
}
// Adapter
class GenericListAdapter<T> internal constructor(
@IdRes private val layout: Int,
private val clickListener: (item: T, position: Int) -> Unit
) : RecyclerView.Adapter<GenericViewHolder<T>>() {
var productItems: ArrayList<T> = ArrayList()
set(value) {
field = value
val diffCallback =
GenericViewHolder.ChildViewDiffUtils(filteredProductItems, productItems)
val diffResult = DiffUtil.calculateDiff(diffCallback)
if (filteredProductItems.size > 0)
filteredProductItems.clear()
filteredProductItems.addAll(value)
diffResult.dispatchUpdatesTo(this)
}
private var filteredProductItems: ArrayList<T> = ArrayList()
set(value) {
field = value
val diffCallback =
GenericViewHolder.ChildViewDiffUtils(filteredProductItems, productItems)
val diffResult = DiffUtil.calculateDiff(diffCallback)
diffResult.dispatchUpdatesTo(this)
}
fun clearProductItem() {
filteredProductItems.clear()
}
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): GenericViewHolder<T> {
val layoutInflater = LayoutInflater.from(viewGroup.context)
val binding = DataBindingUtil.inflate<ViewDataBinding>(layoutInflater, i, viewGroup, false)
return GenericViewHolder(binding)
}
override fun onBindViewHolder(holder: GenericViewHolder<T>, i: Int) {
val item = filteredProductItems[i]
holder.bind(item)
holder.itemView.parentLayout.setOnClickListener {
clickListener(item, i)
}
}
override fun getItemCount(): Int = filteredProductItems.size
override fun getItemViewType(position: Int) = layout
}
// ViewHolder
class GenericViewHolder<T>(private val bi: ViewDataBinding) :
RecyclerView.ViewHolder(bi.root) {
fun bind(item: T) {
bi.apply {
bi.setVariable(BR.item, item)
bi.executePendingBindings()
}
}
class ChildViewDiffUtils<T>(
private val oldList: ArrayList<T>,
private val newList: ArrayList<T>
) :
DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].toString() == newList[newItemPosition].toString()
}
override fun getOldListSize(): Int {
return oldList.size
}
override fun getNewListSize(): Int {
return newList.size
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}
}
@atulgpt
Copy link

atulgpt commented Aug 19, 2022

Hi @AliAzaz is the code right? In the overridden method areItemsTheSame you are using .toString which will fail for example for a

data class Car(val registrationId: Long, val currentColor: String)

for the same registered car Car(registrationId = "abc", currentColor = "red") and Car(registrationId = "abc", currentColor = "yellow") your code will treat these as different item while only areContentsTheSame should return false as these two elements have different data. But you code will WRONGLY return the areItemsTheSame as false as well.

Or is this code specific to your use case only? In that case it should not be present inside GenericViewHolder.kt

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