Potato
Potato copied to clipboard
MultiType 的扩展使用(ViewBinding, Diff.Callback)
MultiType 的扩展使用(ViewBinding, Diff.Callback)
该库是我目前使用过最好用的 adapter 库,严格的遵循数据驱动,针对使用方便性做了一些扩展。
Delegate 的封装
这一步封装,结合 viewbinding 省去 viewholder 的创建过程。
使用反射
abstract class ViewBindingDelegate<T, VB : ViewBinding> :
ItemViewDelegate<T, ViewBindingViewHolder<VB>>() {
override fun onCreateViewHolder(
context: Context,
parent: ViewGroup
): ViewBindingViewHolder<VB> {
return ViewBindingViewHolder(inflateBindingWithGeneric(parent))
}
}
ViewHolder 的创建需要传入 layout 对应的binding,这里使用反射生成。
相关反射代码如下:
@JvmName("inflateWithGeneric")
fun <VB : ViewBinding> Any.inflateBindingWithGeneric(layoutInflater: LayoutInflater): VB =
withGenericBindingClass(this) { clazz ->
clazz.getMethod("inflate", LayoutInflater::class.java).invoke(null, layoutInflater) as VB
}
@JvmName("inflateWithGeneric")
fun <VB : ViewBinding> Any.inflateBindingWithGeneric(layoutInflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean): VB =
withGenericBindingClass(this) { clazz ->
clazz.getMethod("inflate", LayoutInflater::class.java, ViewGroup::class.java, Boolean::class.java)
.invoke(null, layoutInflater, parent, attachToParent) as VB
}
@JvmName("inflateWithGeneric")
fun <VB : ViewBinding> Any.inflateBindingWithGeneric(parent: ViewGroup): VB =
inflateBindingWithGeneric(LayoutInflater.from(parent.context), parent, false)
fun <VB : ViewBinding> Any.bindViewWithGeneric(view: View): VB =
withGenericBindingClass(this) { clazz ->
clazz.getMethod("bind", LayoutInflater::class.java).invoke(null, view) as VB
}
private fun <VB : ViewBinding> withGenericBindingClass(any: Any, block: (Class<VB>) -> VB): VB {
any.allParameterizedType.forEach { parameterizedType ->
parameterizedType.actualTypeArguments.forEach {
try {
return block.invoke(it as Class<VB>)
} catch (e: Exception) {
}
}
}
throw IllegalArgumentException("There is no generic of ViewBinding.")
}
private val Any.allParameterizedType: List<ParameterizedType>
get() {
val genericParameterizedType = mutableListOf<ParameterizedType>()
var genericSuperclass = javaClass.genericSuperclass
var superclass = javaClass.superclass
while (superclass != null) {
if (genericSuperclass is ParameterizedType) {
genericParameterizedType.add(genericSuperclass)
}
genericSuperclass = superclass.genericSuperclass
superclass = superclass.superclass
}
return genericParameterizedType
}
不使用反射
不使用发射,需要抽象一个方法,将 binding 的实例返回。
abstract class ViewBindingDelegate2<T, VB : ViewBinding> :
ItemViewDelegate<T, ViewBindingViewHolder<VB>>() {
override fun onCreateViewHolder(
context: Context,
parent: ViewGroup
): ViewBindingViewHolder<VB> {
return ViewBindingViewHolder(binding(parent))
}
abstract fun binding(parent: ViewGroup): VB
}
class ViewBindingViewHolder<VB : ViewBinding>(val binding: VB) :
RecyclerView.ViewHolder(binding.root)
结合 DiffUtil.Callback 做局部更新
这里考虑使用扩展函数,封装进行使用。
abstract class AnyCallback(val oldItems: List<Any>, val newItems: List<Any>) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return areItemsTheSame(oldItem, newItem)
}
abstract fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return areContentsTheSame(oldItem, newItem)
}
abstract fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean
}
// 带动画的差分更新
fun MultiTypeAdapter.submitList(callback: AnyCallback) {
val result = DiffUtil.calculateDiff(callback)
items = callback.newItems
result.dispatchUpdatesTo(this)
}
// 旧方法更新
fun MultiTypeAdapter.updateItems(items: List<Any>) {
this.items = items
notifyDataSetChanged()
}
参考资料
-
MultiType: https://github.com/drakeet/MultiType
-
DylanCaiCoding:ViewBindingKTX: https://github.com/DylanCaiCoding/ViewBindingKTX