RecycleView 点击事件
Posted dsliang12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RecycleView 点击事件相关的知识,希望对你有一定的参考价值。
RecycleView 点击事件
此文章讨论RecycleView点击事件的设置,参考网上两钟主流的设置方法并且对比两张方法的使用场景.
一开始我接触的就是通过Adapter设置RecycleView的点击事件监听函数.但是发现其实这样相当于把点击事件从RecycleView里面分离出来.然后在后期发现可以在onTouchEvent函数里面拦截触摸事件然后判断是否触发点击事件.在一些场景下这两钟情况都能很好的把问题处理好.直到现在我的布局文件里面还包含更多的控件的时候,发现第二种办法的弊端.然后把这两种实现方法的原理细看一遍,发现其实他们实现的原理是赤裸裸的不一样.
通过Adapter设置点击事件
通过Adapter设置点击事件,很好理解.本质就是给View绑定事件监听函数.所以可以看到RecycleView是在Adapter#onCreateViewHolder函数里面构造出我们需要的View.当然在这个时候,我们可以在Adapter#onCreateViewHolder或者Adapter#onBindViewHolder函数里面设置点击事件监听函数即可.
- 在onCreateViewHolder函数里面设置点击事件监听函数
public RvAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
View view;
final RvAdapter.ViewHolder holder;
view = mLayoutInflater.inflate(R.layout.item_simple_textview, parent, false);
holder = new ViewHolder(view);
view.findViewById(R.id.txtTag).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
Log.i(TAG, mList.get(holder.getLayoutPosition()) + " is click");
);
return holder;
在构造View的同时给View绑定点击事件监听函数,并在监听函数里面持有holder的引用.当回调监听函数的时候通过ViewHolder#getLayoutPosition函数获取position.
- 在onBindViewHolder函数里面设置设置点击事件监听函数
public void onBindViewHolder(final RvAdapter.ViewHolder holder, final int position)
holder.txtTag.setText(mList.get(position));
holder.itemView.findViewById(R.id.txtTag).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
Log.i(TAG, mList.get(position) + " is click");
);
在绑定事件里面给View绑定点击事件监听函数.
两种办法的区别是也是很明显的.对于第一种方法,因为onCreateViewHolder调用的次数有限的.只当需要创建ViewHolder的时候才会调用,所以我们知道它只需对每一个ViewHolder内含的View创建相应的点击事件监听函数就可以了.但是对于第二种方法,就是每次绑定数据的时候都会创建点击事件监听函数.当然喜欢怎么使用是大家的自由.
总结:通过Adapter设置点击事件,本质上是对View直接设置点击事件监听函数.
通过在onTouchEvent函数拦截触摸事件并且派发点击事件
首先我们看看RecycleView的部分源码判断
在RecycleView的onTouchEvent函数里面,首先会调用dispatchOnItemTouch函数进行一些工作.当然我们顺藤摸瓜进去函数里面看看到底里面做了些什么操作?
其实这里就是调用我们通过addOnItemTouchListener函数添加的OnItemTouchListener对象.明白了把?在RecycleView分发点击事情之前会调用我们添加的OnItemTouchListener对象.然而如果我们在OnItemTouchListener对象里面进行点击事件的判断不就实现了点击事件的处理?事实上,第二种设置点击事件的方法原理归根到底就是这样,通过对onTouchEvent的拦截判断是否回调点击事件函数.从而显示点击事件回调函数.
接下来看看种方法代码是怎么实现的
首先继承RecycleView实现一个自定义控件
public class InterceptorRecycleView extends RecyclerView
OnItemClickListener mOnItemClickListener;
public InterceptorRecycleView(Context context)
this(context,null);
public InterceptorRecycleView(Context context, @Nullable AttributeSet attrs)
this(context, attrs,0);
public InterceptorRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
...
在内部添加设置点击事件回调函方法
public void setOnItemClickListener(OnItemClickListener listener)
if (listener == mOnItemClickListener)
return;
mOnItemClickListener = listener;
继承RecyclerView.SimpleOnItemTouchListener,在ItemClickInterceptor函数判断什么时候调用点击事件监听函数.
class ItemClickInterceptor extends RecyclerView.SimpleOnItemTouchListener
Context mContext;
GestureDetector mGestureDetector;
ItemClickInterceptor(Context context)
mContext = context;
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener()
@Override
public boolean onSingleTapUp(MotionEvent e)
return true;
@Override
public void onLongPress(MotionEvent e)
View view;
int position;
view = findChildViewUnder(e.getX(), e.getY());
if (null != view && null != mOnItemClickListener)
position = getChildLayoutPosition(view);
mOnItemClickListener.onLongClick(InterceptorRecycleView.this, view, position);
);
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)
View view;
int position;
view = findChildViewUnder(e.getX(), e.getY());
if (null != view && mGestureDetector.onTouchEvent(e) && null != mOnItemClickListener)
position = getChildLayoutPosition(view);
mOnItemClickListener.onClick(InterceptorRecycleView.this, view, position);
return true;
return false;
其实我们是通过GestureDetector类实现点击事件的监听
在GestureDetector类的onLongPress方法调用长按事件回调函数.因为GestureDetector类已经能帮我们判断什么时候是长按.
但是GestureDetector类并没有实现什么时候是点击.但是当没发生滑动的时候,当事件派发完毕最后是调用onSingleTapUp函数.代表手指已经抬起.我们可以在onSingleTapUp函数里面调用点击事件回调函数.
但是我的做法并不是这样.我选择onSingleTapUp返回ture,并且在onInterceptTouchEvent里面调用点击事件回调函数.(其实两种效果是一样的)
最后添加mOnItemTouchListeners链表里面
public InterceptorRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle)
super(context, attrs, defStyle);
addOnItemTouchListener(new ItemClickInterceptor(context));
注意,判断点击是否在RecycleView的某一个控件上是通过findChildViewUnder(e.getX(), e.getY())方法完成.仔细看findChildViewUnder函数只能判断点位点击落到RecycleView的哪一个item并没办法判断是落在item内部的哪一个控件.
有两个致命的缺点
没办法拿到和点击的View绑定的数据
没办法定位点击了哪一个内部控件,只能定位到点击了RecycleView的哪一个item
此外这样的方式也有一个问题,我只是想设置点击事件回调函数而已.居然要动用到自定义控件?太小题大做了!
针对上面的两个致命缺点,也给出思路.
- 把ItemClickInterceptor内部类移到Adapter里面,ItemClickInterceptor作为Adapter的内部类就可以访问mList,这个时候我拿到position还担心拿不到和View绑定的数据?但是这样一来还不是把点击事件回调函数放回了Adapter里面?
- 通过阅读findChildViewUnder函数,发现通过x,坐标只能定位到RecycleView的item这个问题.没办法从根本上解决,如果有需求需要给内部的控件添加点击事件回调函数.对不起这种办法没办法实现.只能采用第一种方法.
总结
综合上述,作为一个精明的开发者你应该心里早就有自己的见解了把!
然而还是需要来个总结:
- 第一种办法能够很精确的定位到内部控件,并且可以针对控件设置相应的点击事件回调函数.只是代码有点多.
- 第二章办法本质上只能定位到RecycleView的item.如果给item内部的控件添加点击事件回调函数那是不可能实现的.如果只需对item设置点击事件可以考虑这种方法.
以上是关于RecycleView 点击事件的主要内容,如果未能解决你的问题,请参考以下文章
Recycleview点击事件监听器(转自:http://www.jianshu.com/p/f2e0463e5aef)