各实现方法以及存在的问题
想要实现RecyclerView的Item的点击事件,你可能会想到各种方法。
比如:
1.在创建ItemView时添加点击监听。
也就是在 RecyclerView.Adapter<ViewHolder>内的onBindViewHolder()直接实现绑定。
问题是“逻辑跟adapter类耦合严重”。
2.通过RecyclerView已有的方法addOnItemTouchListener()实现.
重载SimpleOnItemTouchListener类,用GestureDetectorCompat捕获item的点击事件。具体代Google一下即可。
问题是“子view不能获取点击”
依据“OnChildAttachStateChangeListener”实现点击监听
今天我们重点不在于前面这两种方法。而是要具体讲解一下第三种Item点击事件的实现。也就是依托于OnChildAttachStateChangeListener的实现。
目标:
1.足够优雅。
2.能够获取Item的点击,也能够实现子View的点击事件。
实现:
知道“OnChildAttachStateChangeListener”这个关键词,我们便可以Google一下此实现了。下面便是千篇一律的代码:
class ItemClickSupport private constructor(private val mRecyclerView: RecyclerView) {
private var mOnItemClickListener: OnItemClickListener? = null
private var mOnItemLongClickListener: OnItemLongClickListener? = null
private val mOnClickListener = View.OnClickListener { v ->
if (mOnItemClickListener != null) {
val holder = mRecyclerView.getChildViewHolder(v)
mOnItemClickListener!!.onItemClicked(mRecyclerView, holder.adapterPosition, v)
}
}
private val mOnLongClickListener = View.OnLongClickListener { v ->
if (mOnItemLongClickListener != null) {
val holder = mRecyclerView.getChildViewHolder(v)
return@OnLongClickListener mOnItemLongClickListener!!.onItemLongClicked(mRecyclerView, holder.adapterPosition, v)
}
false
}
private val mAttachListener = object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener)
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener)
}
}
override fun onChildViewDetachedFromWindow(view: View) {
}
}
init {
mRecyclerView.setTag(R.id.item_click_support, this)
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener)
}
fun addOnItemClickListener(listener: OnItemClickListener): ItemClickSupport {
mOnItemClickListener = listener
return this
}
fun addOnItemLongClickListener(listener: OnItemLongClickListener): ItemClickSupport {
mOnItemLongClickListener = listener
return this
}
private fun detach(view: RecyclerView) {
view.removeOnChildAttachStateChangeListener(mAttachListener)
view.setTag(R.id.item_click_support, null)
}
// 点击接口
public interface OnItemClickListener {
fun onItemClicked(recyclerView: RecyclerView, position: Int, v: View)
}
// 长按接口
interface OnItemLongClickListener {
fun onItemLongClicked(recyclerView: RecyclerView, position: Int, v: View): Boolean
}
companion object {
fun addTo(view: RecyclerView): ItemClickSupport {
var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as ItemClickSupport?
if (support == null) {
support = ItemClickSupport(view)
}
return support
}
fun removeFrom(view: RecyclerView): ItemClickSupport? {
val support = view.getTag(R.id.item_click_support) as ItemClickSupport
support.detach(view)
return support
}
}
}
该方法是根据国外的一篇博客实现,原文链接如下:Getting your clicks on RecyclerView
具体使用时如下:
// 点击
ItemClickSupport.addTo(recycler_view).addOnItemClickListener(object : ItemClickSupport.OnItemClickListener {
override fun onItemClicked(recyclerView: RecyclerView, position: Int, v: View) {
Snackbar.make(v, "recycler_view click", Snackbar.LENGTH_LONG).show()
}
})
然而,此时这个类是不能实现子view的点击监听的。
真正实现RecyclerView的点击事件监听
为了让ItemClickSupport既可以实现Item的点击监听又可以实现子View的点击监听。我们需要改造上面的类。
首先找到核心代码:
private val mAttachListener = object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
if (mOnItemClickListener != null) {
view.setOnClickListener(mOnClickListener)
}
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(mOnLongClickListener)
}
}
override fun onChildViewDetachedFromWindow(view: View) {
}
}
此处我们根据“mOnItemClickListener”实现了对Item事件的监听,既然如此,
此时我们根据child = view.findViewById(resourceId)
来获取我们想监听的子View,然后绑定一个点击事件不就可以了么?
说干就干:
- 首先要有一个childId来保存子View的id,并且可以赋值。
- 要有一个OnChildClickListener接口来处理子View的点击事件
- 最好能够做到链式调用。这样使用会比较方便。
- 依旧将ItemClickSupport依托tag绑定到到recyclerView上,避免多个实例。
根据上面几点要求,所以我想到了Builder模式,直接上代码。
具体如下:
class ItemClickSupport private constructor(private val mRecyclerView: RecyclerView) {
private var mOnItemClickListener: OnItemClickListener? = null
private val childListenerMap = hashMapOf<Int, OnChildClickListener>()
private var mOnItemLongClickListener: OnItemLongClickListener? = null
private val mAttachListener = object : RecyclerView.OnChildAttachStateChangeListener {
override fun onChildViewAttachedToWindow(view: View) {
//item 点击
if (mOnItemClickListener != null) {
view.clickWithTrigger { v ->
if (mOnItemClickListener != null) {
val holder = mRecyclerView.findContainingViewHolder(v)
if (holder != null && holder.adapterPosition != -1) {
mOnItemClickListener?.onItemClicked(mRecyclerView, holder.adapterPosition, v)
}
}
}
}
//子 View 点击
if (childListenerMap.isNotEmpty()) {
for (key in childListenerMap.keys) {
view.findViewById<View>(key)?.clickWithTrigger { v ->
val holder = mRecyclerView.findContainingViewHolder(v)
if (holder != null && holder.adapterPosition != -1) {
childListenerMap[key]!!.onChildClicked(mRecyclerView, holder.adapterPosition, v)
}
}
}
}
//Item 长按点击
if (mOnItemLongClickListener != null) {
view.setOnLongClickListener(View.OnLongClickListener { v ->
if (mOnItemLongClickListener != null) {
val holder = mRecyclerView.getChildViewHolder(v)
return@OnLongClickListener mOnItemLongClickListener!!.onItemLongClicked(mRecyclerView, holder.adapterPosition, v)
}
false
})
}
}
override fun onChildViewDetachedFromWindow(view: View) {
}
}
init {
mRecyclerView.setTag(R.id.item_click_support, this)
mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener)
}
fun addOnChildClickListener(@IdRes resId: Int, @NotNull listener: OnChildClickListener): ItemClickSupport {
childListenerMap[resId] = listener
return this
}
fun addOnItemClickListener(@NotNull listener: OnItemClickListener): ItemClickSupport {
mOnItemClickListener = listener
return this
}
fun addOnItemLongClickListener(@NotNull listener: OnItemLongClickListener): ItemClickSupport {
mOnItemLongClickListener = listener
return this
}
private fun detach(view: RecyclerView) {
view.removeOnChildAttachStateChangeListener(mAttachListener)
view.setTag(R.id.item_click_support, null)
}
// 子 View点击接口
interface OnChildClickListener {
fun onChildClicked(recyclerView: RecyclerView, position: Int, v: View)
}
// 点击接口
interface OnItemClickListener {
fun onItemClicked(recyclerView: RecyclerView, position: Int, v: View)
}
// 长按接口
interface OnItemLongClickListener {
fun onItemLongClicked(recyclerView: RecyclerView, position: Int, v: View): Boolean
}
companion object {
fun addTo(view: RecyclerView): ItemClickSupport {
var support: ItemClickSupport? = view.getTag(R.id.item_click_support) as ItemClickSupport?
if (support == null) {
support = ItemClickSupport(view)
}
return support
}
fun removeFrom(view: RecyclerView): ItemClickSupport? {
val support = view.getTag(R.id.item_click_support) as ItemClickSupport
support.detach(view)
return support
}
}
}
如果只监听Item的,则可以这样写
ItemClickSupport.addTo(recycler_view).addOnItemClickListener(object : ItemClickSupport.OnItemClickListener {
override fun onItemClicked(recyclerView: RecyclerView, position: Int, v: View) {
Snackbar.make(v, "recycler_view click", Snackbar.LENGTH_LONG).show()
}
})
如果只监听子View的,则可以这样写
ItemClickSupport.addTo(recycler_view).addOnChildClickListener(R.id.child_id, object : ItemClickSupport.OnChildClickListener {
override fun onChildClicked(recyclerView: RecyclerView, position: Int, v: View) {
Snackbar.make(v, "child click", Snackbar.LENGTH_LONG).show()
}
})
如果两者都监听,可以这样写(当然也可以分开写):
ItemClickSupport.addTo(recycler_view).addOnChildClickListener(R.id.child_id, object : ItemClickSupport.OnChildClickListener {
override fun onChildClicked(recyclerView: RecyclerView, position: Int, v: View) {
Snackbar.make(v, "child click", Snackbar.LENGTH_LONG).show()
}
}).addOnItemClickListener(object : ItemClickSupport.OnItemClickListener {
override fun onItemClicked(recyclerView: RecyclerView, position: Int, v: View) {
Snackbar.make(v, "item click", Snackbar.LENGTH_LONG).show()
}
})
注意:代码全部为kotlin。建议大伙也用一下。如果实在还是想用java,而又不想动手转的话,那就给我留个言……