Closed Jikuai closed 1 year ago
@Jikuai 一切的疑惑都可以在源码中得到答案。 看一下 BaseNodeAdapter 的继承关系:
BaseNodeAdapter -> BaseProviderMultiAdapter -> BaseQuickAdapter -> RecyclerView.Adapter
创建 ViewHolder 以及绑定相关点击事件监听的逻辑执行顺序:
// 创建 ViewHolder
val viewHolder = onCreateDefViewHolder(parent, viewType)
// 绑定 View 点击事件到 viewHolder 上,我们关注这个流程
bindViewClickListener(viewHolder, viewType)
// 如果外部通过 adapter 设置了 OnItemClickListener; 那么绑定 item 的点击事件
mOnItemClickListener?.let {
viewHolder.itemView.setOnClickListener { v ->
var position = viewHolder.adapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnClickListener
}
position -= headerLayoutCount
setOnItemClick(v, position)
}
}
// 如果外部设置了 OnItemChildClickListner,那么就去 childClickViewIds 集合中寻找需要需要被点击的 item 中的元素(child),然后设置点击事件
mOnItemChildClickListener?.let {
for (id in getChildClickViewIds()) {
viewHolder.itemView.findViewById<View>(id)?.let { childView ->
if (!childView.isClickable) {
childView.isClickable = true
}
childView.setOnClickListener { v ->
var position = viewHolder.adapterPosition
if (position == RecyclerView.NO_POSITION) {
return@setOnClickListener
}
position -= headerLayoutCount
setOnItemChildClick(v, position)
}
}
}
}
override fun bindViewClickListener(viewHolder: BaseViewHolder, viewType: Int) {
// 先走父类的逻辑,也就是步骤2的逻辑
super.bindViewClickListener(viewHolder, viewType)
bindClick(viewHolder)
bindChildClick(viewHolder, viewType)
}
protected open fun bindClick(viewHolder: BaseViewHolder) { // 如果外部adapter没有设置 item 的点击监听,则回调给 itemProvider 的 onClick 方法 if (getOnItemClickListener() == null) { viewHolder.itemView.setOnClickListener { var position = viewHolder.adapterPosition if (position == RecyclerView.NO_POSITION) { return@setOnClickListener } position -= headerLayoutCount
val itemViewType = viewHolder.itemViewType
val provider = mItemProviders.get(itemViewType)
provider.onClick(viewHolder, it, data[position], position)
}
} //... 省略 }
protected open fun bindChildClick(viewHolder: BaseViewHolder, viewType: Int) {
// 如果外部adapter没有设置 item 内元素(child)的点击监听,则回调给 itemProvider 的 onChildClick 方法
if (getOnItemChildClickListener() == null) {
val provider = getItemProvider(viewType) ?: return
// 同样地,也需要在 provider 的 childClickViewIds 集合中寻找需要被点击的元素(child)
val ids = provider.getChildClickViewIds()
ids.forEach { id ->
viewHolder.itemView.findViewById
所以,BaseNodeAdapter 有以下推荐的设置item中的child元素的点击事件的方式:
1. 通过 BaseNodeProvider 体系来设置:
```kotlin
// 1. 向 BaseNodeProvider 中添加可点击的child元素id,比如在Adapter初始化的时候设置
class XXAdapter(nodes: MutableList<BaseNode>) : BaseNodeAdapter(nodes) {
init {
val myNodeProvider = XXNodeProvider().apply {
this.addChildClickViewIds(R.id.title)
}
addNodeProvider(myNodeProvider)
}
}
// 2. 在 BaseNodeProvider 实现类中实现 onChildClick 方法
class XXNodeProvider : BaseNodeProvider() {
//.. 省略其他方法
override fun onChildClick(helper: BaseViewHolder, view: View, data: BaseNode, position: Int) {
when(view.id) {
R.id.title -> {
// ...
}
}
}
}
// 1. 向 BaseQuickAdapter 中添加可点击的child元素id
xxAdapter.addChildClickViewIds(R.id.title)
// 2. BaseQuickAdapter 设置 OnItemChildClickListener
xxAdapter.setOnItemChildClickListener { adapter, view, position ->
when(view.id) {
R.id.title -> {
// ...
}
}
}
最后,在 convert
中设置child元素的点击事件是可行的,但是 convert
是用来刷新 ViewHolder
中的数据的,所以即便是同一个 ViewHolder
对象,可能也会因为 RecyclerView
item 回收机制的原因,调用多次的 convert
方法。所以如无必要,不推荐这样设置。
version : 3.0.11 sample code:
`public class FirstProvider extends BaseNodeProvider {
} `
請教各位大佬,我在onClick嘗試去取得R.id.title的OnClicklistener,但該方法好像只監聽item是否被click 當使用view.getId()時,獲得的值都是-1 若需要監聽R.id.title的話,是不是直接在convert設定監聽就可以? 如: ` TextView tvTitle = helper.getView(R.id.tv_title);
`
請問這樣操作是否造成效能低落?還是有更好的監聽方法,謝謝大家