
天才第一步
先说一下整体思想: 如果我们想写一个通用的adapter,那么我们得让adapter里的方法变成大家都能用的(废话),所以onBindViewHolder我们拿到adapter之外实现,这样的话,adapter里有个性的方法就不复存在了

效果图
环境配置
- Kotlin(中文网、民间大神教程)
- DataBinding
- 缺啥就是啥吧
环境配置好了,我们先来了解一下DiffUtil的工作原理。
第一次发布文章,准备的也不是那么充分,所以....尽情的点链接吧 DiffUtil详解
正餐
- ViewHolder
inner class BaseViewHolder<out T : ViewDataBinding>(val binding: T) : RecyclerView.ViewHolder(binding.root) {
init {
binding.executePendingBindings()
}
}
因为我们使用了DataBinding,所以直接在ViewHolder里保存ViewDataBinding对象即可,省去了传统方法初始化item里所有变量的麻烦。 因为我们要写的是通用的adapter,所以泛型是必不可少的。
- onCreateViewHolder
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseViewHolder<T> {
val view = LayoutInflater.from(context).inflate(resId, null)
val binding = DataBindingUtil.bind<T>(view)
return BaseViewHolder(binding)
}
和传统写法没啥区别,不做赘述
- onBindViewHolder
override fun onBindViewHolder(holder: BaseViewHolder<T>?, position: Int) {
listener(holder!!, position)
}
这里就是一个回调(实际上用的是lambda表达式),将holder与position作为参数传递给RecyclerView所在的activity或者fragment
到目前为止,最基本的东西都写完了,看下使用方法
//设置dataSize的同时,刷新adapter
var dataSize = 0
set(value) {
field = value
notifyDataSetChanged()
}
adapter = BaseAdapter<ItemBinding, Bean>(this, R.layout.item) {
holder, position ->
holder.binding.bean = realData[position]
}
adapter.dataSize = data.size

就是这么简洁
- 融入DiffUtil
//用于刷新adapter的方法,同时返回最新的数据
fun refresh(oldData: List<K>, newData: List<K>, areContentsTheSame: (K, K) -> Boolean): List<K> {
DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldData[oldItemPosition] == newData[newItemPosition]
}
override fun getOldListSize(): Int = oldData.size
override fun getNewListSize(): Int = newData.size
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return areContentsTheSame(oldData[oldItemPosition], newData[newItemPosition])
}
}).dispatchUpdatesTo(this)
//这里需要注意一下 我们要先将DiffUtil.Result绑定到adapter之后
//再通知adapter数据长度变化
dataSize = newData.size
return newData
}
需要注意的地方是,DiffUtil.DiffResult绑定adapter之后,会自己选择调用adapter的四种刷新方法,详细的可以自己看下源码。
所以我们的dataSize在这时,改成私有变量,去掉自定义setter
var dataSize = 0
然后这里实现的areContentsTheSame和onBindViewHolder差不多,都是属于比较有个性的方法(传进来的数据类不一样,比较的属性不一样),所以我们也用同样的方法, 拿出去实现。
到这里差不多该结束了,使用:
adapter = BaseAdapter<ItemBinding, Bean>(this, R.layout.item) {
holder, position ->
holder.binding.bean = realData[position]
}
realData = adapter.refresh(oldList, newList) { (name, age), (name1, age1) ->
if (name != name1) false else age == age1
}

End
- 附件
直接贴上来两个类的代码吧 MainActivity.kt
class MainActivity: AppCompatActivity() {
private var flag = true
private var adapter: BaseAdapter<ItemBinding, Bean> by Delegates.notNull()
private var realData: List<Bean> by Delegates.notNull()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityPracticeBinding = DataBindingUtil.setContentView(this, R.layout.activity_practice)
val list: ArrayList<Bean> = ArrayList()
val data: ArrayList<Bean> = ArrayList()
//Ops, these are fake data
val bean = Bean("name", "age")
val bean2 = bean.copy(name = "2")
val bean3 = bean.copy(name = "3")
val bean4 = bean.copy(name = "4")
val bean5 = bean.copy(name = "5")
with(list) {
add(bean);add(bean2);add(bean3);add(bean4);add(bean5);add(bean3)
}
with(data) {
add(bean);add(bean2);add(bean3);add(bean5);add(bean3);add(bean2);add(bean3);add(bean)
}
adapter = BaseAdapter<ItemBinding, Bean>(this, R.layout.item) {
holder, position ->
holder.binding.bean = realData[position]
}
refresh(newList = list)
with(binding) {
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
refresh.setOnClickListener {
refresh(if (flag) list else data, if (!flag) list else data)
flag = !flag
}
}
}
private fun refresh(oldList: ArrayList<Bean> = ArrayList(), newList: ArrayList<Bean>) {
realData = adapter.refresh(oldList, newList) { (name, age), (name1, age1) ->
if (name != name1) false else age == age1
}
}
}
BaseAdapter.kt
class BaseAdapter<in T : ViewDataBinding, K>(
val context: Context,
val resId: Int,
val listener: (BaseAdapter<T, K>.BaseViewHolder<T>, Int) -> Unit) : RecyclerView.Adapter<BaseAdapter<T, K>.BaseViewHolder<T>>() {
var dataSize = 0
override fun onBindViewHolder(holder: BaseViewHolder<T>?, position: Int) {
listener(holder!!, position)
}
override fun getItemCount(): Int = dataSize
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): BaseViewHolder<T> {
val view = LayoutInflater.from(context).inflate(resId, null)
val binding = DataBindingUtil.bind<T>(view)
return BaseViewHolder(binding)
}
inner class BaseViewHolder<out T : ViewDataBinding>(val binding: T) : RecyclerView.ViewHolder(binding.root) {
init {
binding.executePendingBindings()
}
}
//用于刷新adapter的方法,同时返回最新的数据
fun refresh(oldData: List<K>, newData: List<K>, areContentsTheSame: (K, K) -> Boolean): List<K> {
DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldData[oldItemPosition] == newData[newItemPosition]
}
override fun getOldListSize(): Int = oldData.size
override fun getNewListSize(): Int = newData.size
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return areContentsTheSame(oldData[oldItemPosition], newData[newItemPosition])
}
}).dispatchUpdatesTo(this)
//这里需要注意一下 我们要先将DiffUtil.Result绑定到adapter之后
//再通知adapter数据长度变化
dataSize = newData.size
return newData
}
}