RecyclerView默认没有像ListView同样提供setOnItemClickListener()
接口,网上大部分的解决方案都是经过给每一个item添加onClickListener来模仿一个伪onItemClickListener,这种为每一个item添加点击监听的解决方案浪费性能。java
查阅RecyclerView的api发现虽然没有提供onItemClickListener可是提供了addOnItemTouchListener
方法:web
RecyclerView.addOnItemTouchListener(OnItemTouchListener listener)
既然能够添加触摸监听,那么咱们彻底能够获取触摸手势来识别点击事件,而后经过触摸坐标来判断点击的是哪个item。api
使用方法:ide
recyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(recyclerView) { @Override public void onItemClick(RecyclerView.ViewHolder vh) { //item点击事件 } });
其中OnRecyclerItemClickListener
是自定义的一个触摸监听器:svg
public abstract class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener{ private GestureDetectorCompat mGestureDetector; private RecyclerView recyclerView; public OnRecyclerItemClickListener(RecyclerView recyclerView){ this.recyclerView = recyclerView; mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(),new ItemTouchHelperGestureListener()); } @Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { mGestureDetector.onTouchEvent(e); return false; } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { mGestureDetector.onTouchEvent(e); } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { } private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child!=null) { RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child); onItemClick(vh); } return true; } //长点击事件,本例不须要不处理 //@Override //public void onLongPress(MotionEvent e) { // View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); // if (child!=null) { // RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child); // onItemLongClick(vh); // } //} public abstract void onItemClick(RecyclerView.ViewHolder vh); //public abstract void onItemLongClick(RecyclerView.ViewHolder vh); }
RecyclerView提供了设置触摸监听的方法,那么咱们定义一个类OnRecyclerItemClickListener实现OnItemTouchListener
,咱们须要实现其3个方法:性能
@Override public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { } @Override public void onTouchEvent(RecyclerView rv, MotionEvent e) { } @Override public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
其中第三个方法是处理触摸事件冲突的,前两个方法是View的事件分发机制里面的事件拦截和事件处理的两个方法。参数里为咱们提供了触摸事件的数据MotionEvent
,咱们要作的就是去解析坐标点和触摸规律来识别触摸手势,而后获取触摸的是哪个item,再执行咱们的回调this
GestureDetectorCompat
就是处理手势的类:手势探测器,它比GestureDetector
能更好兼容低版本的api,但使用方法是一致的,咱们实例化一个手势探测器:spa
mGestureDetector = new GestureDetectorCompat(context,new GestureListener(){...});
咱们实例化手势探测器的时候须要提供一个手势监听器:OnGestureListener
,探测器识别出手势后就会回调手势监听器中对应的方法,咱们就能够在回调方法中作咱们想作的事情了。code
sdk提供了两个手势监听器:OnGestureListener
,OnDoubleTapListener
xml
OnGestureListener的回调接口以下:
//用户按下屏幕就会触发 public boolean onDown(MotionEvent e); //若是是按下的时间超过瞬间,并且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行 public void onShowPress(MotionEvent e); //一次单独的轻击抬起操做,也就是轻击一下屏幕,就是普通点击事件 public boolean onSingleTapUp(MotionEvent e); //在屏幕上拖动事件 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY); //长按触摸屏,超过必定时长,就会触发这个事件 public void onLongPress(MotionEvent e); //滑屏,用户按下触摸屏、快速移动后松开 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
OnDoubleTapListener的回调接口以下
//单击事件。用来断定该次点击是SingleTap而不是DoubleTap, //若是连续点击两次就是DoubleTap手势,若是只点击一次, //系统等待一段时间后没有收到第二次点击则断定该次点击为SingleTap而不是DoubleTap, //而后触发SingleTapConfirmed事件 public boolean onSingleTapConfirmed(MotionEvent e); //双击事件 public boolean onDoubleTap(MotionEvent e); //双击间隔中发生的动做。指触发onDoubleTap之后,在双击之间发生的其它动做 public boolean onDoubleTapEvent(MotionEvent e);
能够看出OnGestureListener主要回调各类单击事件,而OnDoubleTapListener回调各类双击事件。而咱们须要处理的点击事件其实就是上面的:onSingleTapUp()
定义一个ItemTouchHelperGestureListener 继承自SimpleOnGestureListener ,实现onSingleTapUp方法:
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent e) { } }
到这里,已经获取到了RecyclerView的点击事件和触摸事件数据MotionEvent ,那么咱们怎么知道点击的是哪个item呢?RecyclerView已经为咱们提供了这样的方法:findChildViewUnder()
,咱们能够经过这个方法得到点击的item,同时咱们调用RecyclerView的另外一个方法getChildViewHolder()
,能够得到该item的ViewHolder,最后再回调咱们定义的虚方法onItemClick()
就ok了,这样咱们就能够在外部实现该方法来得到item的点击事件了:
@Override public boolean onSingleTapUp(MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child!=null) { RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child); onItemClick(vh); } return true; }
前述的方案经过recyclerView.addOnItemTouchListener(...)
添加点击事件的方法,其实咱们也彻底能够把点击事件写在Adapter的onBindViewHolder()中,不暴露出来,即:
public void onBindViewHolder(VH holder, int position) { holder.itemView.setOnClickListener(...); }