一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
本篇文章主要是分析
BaseRecyclerViewAdapterHelper
内部是如何实现RecycleView
的适配器的以及做了哪些优化操作。源码分析是基于BaseRecyclerViewAdapterHelper
以下版本:implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.0'
其中
BaseRecyclerViewAdapterHelper
简称为BRVAH
RecycleView
子View
点击事件设置
先说结论,RecycleView子View点击事件是在onCreateViewHolder
中进行设置的,简单看下源码:
#BaseQuickAdapter
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
...
bindViewClickListener(viewHolder, viewType)
...
}
onCreateViewHolder
调用了bindViewClickListener
,并在该方法中设置了子View的点击事件和长点击事件。
为什么不再onBindViewHolder
中设置而是要在onCreateViewHolder
中设置点击事件呢?
因为在RecycleView列表显示的过程中,由于其回收复用机制,同一个ViewHolder可能会触发多次
onBindViewHolder
的执行(主要是用来重新绑定数据的),而点击事件的设置本身只用设置一次就行,所以就放在了onCreateViewHolder
中设置。
ViewHolder
中ItemView
的子控件View获取
比如我们要设置一个子控件TextView
的显示文本:
核心逻辑就在fun <T : View> getViewOrNull
这个方法中:
open fun <T : View> getViewOrNull(@IdRes viewId: Int): T? {
val view = views.get(viewId)
if (view == null) {
itemView.findViewById<T>(viewId)?.let {
views.put(viewId, it)
return it
}
}
return view as? T
}
views
是一个SparseArray<View>
类型集合。
在getViewOrNull()
方法中,首先先从views
中判断是否存在该viewId
,不存在再去调用findViewById
去获取,然后存入到views
集合中。
这样ViewHolder
发生复用onBindViewHolder
多次调用重新绑定数据时,就不用每次都要重新去findViewById
获取View了,而且SparseArray
内部是个二分查找
的实现,获取View
的效率更高。
题外话:ItemView
中子控件点击事件设置无效的BUG
BRVAH3.0.0
的版本存在一个BUG:在onBindViewHolder
中通过addChildClickViewIds()
添加ItemView
的子控件的点击事件,点击第一个Item无效,其他的正常,详情可见github issue列表中两条数据添加监听...。
想要解决这个问题,可以在基于BaseQuickAdapter
自定义的Adapter构造方法中添加:
class CustomAdapter: BaseQuickAdapter<String, BaseViewHolder>() {
init {
addChildClickViewIds()
}
}