Last active
September 12, 2023 11:53
-
-
Save yogacp/765136a364de1bf5e3e735bf8a69f930 to your computer and use it in GitHub Desktop.
Recyclerview that use Abstract Adapter and viewbinding
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
/** | |
* Create the Abstract Adapter and the ViewHolder that we need to hold the item view | |
*/ | |
abstract class AbstractAdapter<T : ViewBinding, ITEM> constructor( | |
protected var itemList: List<ITEM>, | |
private val bindingClass: (LayoutInflater, ViewGroup, Boolean) -> T | |
) : RecyclerView.Adapter<AbstractAdapter.Holder>() { | |
var binding: T? = null | |
init { | |
update(itemList) | |
notifyDataSetChanged() | |
} | |
override fun getItemCount(): Int = itemList.size | |
override fun getItemViewType(position: Int): Int = position | |
@Suppress("UNCHECKED_CAST") | |
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { | |
//inflate the bindingClass using ViewGroup binding delegation | |
val viewBinding = parent.viewBinding(bindingClass) | |
this.binding = viewBinding | |
val viewHolder = Holder(viewBinding.root) | |
val itemView = viewHolder.itemView | |
itemView.tag = viewHolder | |
itemView.setOnClickListener { | |
val adapterPosition = viewHolder.adapterPosition | |
if (adapterPosition != RecyclerView.NO_POSITION) { | |
onItemClick(itemView, adapterPosition) | |
} | |
} | |
return viewHolder | |
} | |
override fun onBindViewHolder(holder: Holder, position: Int) { | |
val item = itemList[position] | |
holder.itemView.bind(item) | |
} | |
override fun onViewRecycled(holder: Holder) { | |
super.onViewRecycled(holder) | |
onViewRecycled(holder.itemView) | |
} | |
private fun updateAdapterWithDiffResult(result: DiffUtil.DiffResult) { | |
result.dispatchUpdatesTo(this) | |
} | |
private fun calculateDiff(newItems: List<ITEM>): DiffUtil.DiffResult { | |
return DiffUtil.calculateDiff(DiffUtilCallback(itemList, newItems)) | |
} | |
private fun update(items: List<ITEM>) { | |
updateAdapterWithDiffResult(calculateDiff(items)) | |
} | |
private fun add(item: ITEM) { | |
itemList.toMutableList().add(item) | |
notifyItemInserted(itemList.size) | |
} | |
private fun remove(position: Int) { | |
itemList.toMutableList().removeAt(position) | |
notifyItemRemoved(position) | |
} | |
protected open fun View.bind(item: ITEM) {} | |
protected open fun onViewRecycled(itemView: View) {} | |
protected open fun onItemClick(itemView: View, position: Int) {} | |
class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) | |
} |
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
/** | |
* Create a DiffUtil callback | |
*/ | |
internal class DiffUtilCallback<ITEM>( | |
private val oldItems: List<ITEM>, | |
private val newItems: List<ITEM> | |
): DiffUtil.Callback() { | |
override fun getOldListSize(): Int = oldItems.size | |
override fun getNewListSize(): Int = newItems.size | |
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { | |
return oldItems[oldItemPosition] == newItems[newItemPosition] | |
} | |
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { | |
return oldItems[oldItemPosition] == newItems[newItemPosition] | |
} | |
} |
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
/** | |
* Create a General Adapter to handle the itemView setup and click | |
*/ | |
class GeneralAdapter<T : ViewBinding, ITEM>( | |
items: List<ITEM>, | |
bindingClass: (LayoutInflater, ViewGroup, Boolean) -> T, | |
private val bindHolder: View.(T?, ITEM) -> Unit | |
) : AbstractAdapter<T, ITEM>(items, bindingClass) { | |
private var itemClick: View.(ITEM) -> Unit = {} | |
var viewBinding: T? = null | |
constructor( | |
items: List<ITEM>, | |
bindingClass: (LayoutInflater, ViewGroup, Boolean) -> T, | |
bindHolder: View.(T?, ITEM) -> Unit, | |
itemViewClick: View.(ITEM) -> Unit = {} | |
) : this(items, bindingClass, bindHolder) { | |
this.itemClick = itemViewClick | |
} | |
override fun onBindViewHolder(holder: Holder, position: Int) { | |
super.onBindViewHolder(holder, position) | |
if (position == holder.adapterPosition) { | |
this.viewBinding = binding | |
holder.itemView.bindHolder(binding, itemList[position]) | |
} | |
} | |
override fun onItemClick(itemView: View, position: Int) { | |
itemView.itemClick(itemList[position]) | |
} | |
} |
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
/** | |
* And here how we use it, Just call the recyclerview binding and call .setup() and include | |
*/ | |
binding.recyclerview.setup( | |
listData, | |
ItemSampleBinding::inflate, | |
{ binding, data -> | |
binding?.tvTitle?.text = data.name | |
binding?.btnSetup?.setOnClickListener { | |
showAlert(data.description) | |
} | |
} | |
) |
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
/** | |
* Create the ViewGroup binding delegation | |
*/ | |
inline fun <T : ViewBinding> ViewGroup.viewBinding(binding: (LayoutInflater, ViewGroup, Boolean) -> T): T { | |
return binding(LayoutInflater.from(context), this, false) | |
} | |
/** | |
* Create an extension function to RecyclerView | |
* And create function setup() that implement the GeneralAdapter and its viewBinding. | |
*/ | |
fun <T : ViewBinding, ITEM> RecyclerView.setup( | |
items: List<ITEM>, | |
bindingClass: (LayoutInflater, ViewGroup, Boolean) -> T, | |
bindHolder: View.(T?, ITEM) -> Unit, | |
itemClick: View.(ITEM) -> Unit = {}, | |
manager: RecyclerView.LayoutManager = LinearLayoutManager(this.context) | |
): GeneralAdapter<T, ITEM> { | |
val generalAdapter by lazy { | |
GeneralAdapter(items, bindingClass, | |
{ binding: T?, item: ITEM -> | |
bindHolder(binding, item) | |
}, { | |
itemClick(it) | |
} | |
) | |
} | |
layoutManager = manager | |
adapter = generalAdapter | |
(adapter as GeneralAdapter<*, *>).notifyDataSetChanged() | |
return generalAdapter | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment