RecyclerView的Item点击事件实现总结

来源:互联网 发布:js分割字符串成数组 编辑:程序博客网 时间:2024/06/05 11:41

自从开始使用RecyclerView代替ListView,会发现有很多地方需要学习。前一段时间的学习记录有:

  1. RecyclerView的滚动事件研究 - DevWiki

  2. RecyclerView的ViewHolder和Adapter的封装优化 - DevWiki

  3. RecyclerView问题记录 - DevWiki

实现 RecyclerView的Item的点击事件有三种方式:

  1. 在创建 ItemView时添加点击监听

  2. 当 ItemView attach RecyclerView时实现

  3. 通过RecyclerView已有的方法addOnItemTouchListener()实现

1.在创建ItemView时添加点击监听

      思路是:因为ViewHolder我们可以拿到每个Item的根布局,所以如果我们为根布局设置单独的OnClick监听并将其开放给Adapter,那不就可以在组装RecyclerView时就能够设置ItemClickListener,只不过这个Listener不是设置到RecyclerView上而是设置到Adapter。具体实现代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.SampleViewHolder> {  
  2.   
  3.     private List<DataBean> mDatas;  
  4.     private OnItemClickListener mListener; // Item点击事件  
  5.   
  6.     public DataBean getItem(int position) {  
  7.         return mDatas == null ? null : mDatas.get(position);  
  8.     }  
  9.   
  10.     @Override  
  11.     public SampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {  
  12.         View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent,false);  
  13.         return new SampleViewHolder(itemView);  
  14.     }  
  15.   
  16.     @Override  
  17.     public void onBindViewHolder(SampleViewHolder holder, int position) {  
  18.   
  19.     }  
  20.   
  21.     @Override  
  22.     public int getItemCount() {  
  23.         return mDatas == null ? 0 : mDatas.size();  
  24.     }  
  25.   
  26.     class SampleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {  
  27.   
  28.         public SampleViewHolder(View itemView) {  
  29.             super(itemView);  
  30.             // TODO:初始化View  
  31.             ...  
  32.   
  33.             itemView.setOnClickListener(this);  
  34.             itemView.setOnLongClickListener(this);  
  35.         }  
  36.   
  37.         @Override  
  38.         public void onClick(View v) {  
  39.             if (mListener != null) {  
  40.                 mListener.onItemClick(SampleAdapter.this, v, getLayoutPosition());  
  41.             }  
  42.         }  
  43.   
  44.         @Override  
  45.         public boolean onLongClick(View v) {  
  46.             if (mListener != null) {  
  47.                 mListener.onItemLongClick(SampleAdapter.this, v, getLayoutPosition());  
  48.                 return true;  
  49.             }  
  50.             return false;  
  51.         }  
  52.     }  
  53. }  

2.当ItemView attach RecyclerView时实现

      该实现方法是在阅读国外的一篇博客时发现的,原文链接如下Getting your clicks on RecyclerView

实现的代码如下

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class ItemClickSupport {  
  2.   
  3.     private static final int KEY = 0x99999999;  
  4.     private final RecyclerView mRecyclerView;  
  5.     private OnItemClickListener mOnItemClickListener;  
  6.     private OnItemLongClickListener mOnItemLongClickListener;  
  7.   
  8.     private View.OnClickListener mOnClickListener = new View.OnClickListener() {  
  9.         @Override  
  10.         public void onClick(View v) {  
  11.             if (mOnItemClickListener != null) {  
  12.                 RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);  
  13.                 mOnItemClickListener.onItemClicked(mRecyclerView, v, holder.getAdapterPosition());  
  14.             }  
  15.         }  
  16.     };  
  17.   
  18.     private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {  
  19.         @Override  
  20.         public boolean onLongClick(View v) {  
  21.             if (mOnItemLongClickListener != null) {  
  22.                 RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);  
  23.                 return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, v, holder.getAdapterPosition());  
  24.             }  
  25.             return false;  
  26.         }  
  27.     };  
  28.   
  29.     private RecyclerView.OnChildAttachStateChangeListener mAttachListener = new RecyclerView.OnChildAttachStateChangeListener() {  
  30.   
  31.         @Override  
  32.         public void onChildViewAttachedToWindow(View view) {  
  33.             if (mOnItemClickListener != null) {  
  34.                 view.setOnClickListener(mOnClickListener);  
  35.             }  
  36.             if (mOnItemLongClickListener != null) {  
  37.                 view.setOnLongClickListener(mOnLongClickListener);  
  38.             }  
  39.         }  
  40.   
  41.         @Override  
  42.         public void onChildViewDetachedFromWindow(View view) {  
  43.         }  
  44.     };  
  45.   
  46.     /** 
  47.      * ItemClickSupport的私有构造方法 
  48.      */  
  49.     private ItemClickSupport(RecyclerView recyclerView) {  
  50.         mRecyclerView = recyclerView;  
  51.         mRecyclerView.setTag(KEY, this);  
  52.         // 为RecyclerView设置OnChildAttachStateChangeListener事件监听  
  53.         mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);  
  54.     }  
  55.   
  56.     /** 
  57.      * 为RecyclerView设置ItemClickSupport 
  58.      */  
  59.     public static ItemClickSupport addTo(RecyclerView view) {  
  60.         ItemClickSupport support = (ItemClickSupport) view.getTag(KEY);  
  61.         if (support == null) {  
  62.             support = new ItemClickSupport(view);  
  63.         }  
  64.         return support;  
  65.     }  
  66.   
  67.     /** 
  68.      * 为RecyclerView移除ItemClickSupport 
  69.      */  
  70.     public static ItemClickSupport removeFrom(RecyclerView view) {  
  71.         ItemClickSupport support = (ItemClickSupport) view.getTag(KEY);  
  72.         if (support != null) {  
  73.             support.detach(view);  
  74.         }  
  75.         return support;  
  76.     }  
  77.   
  78.     /** 
  79.      * 为RecyclerView设置点击事件监听 
  80.      */  
  81.     public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {  
  82.         mOnItemClickListener = listener;  
  83.         return this;  
  84.     }  
  85.   
  86.     /** 
  87.      * 为RecyclerView设置长按事件监听 
  88.      */  
  89.     public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {  
  90.         mOnItemLongClickListener = listener;  
  91.         return this;  
  92.     }  
  93.   
  94.     /** 
  95.      * 为RecyclerView移除OnChildAttachStateChangeListener事件监听 
  96.      */  
  97.     private void detach(RecyclerView view) {  
  98.         view.removeOnChildAttachStateChangeListener(mAttachListener);  
  99.         view.setTag(KEY, null);  
  100.     }  
  101.   
  102.     /** 
  103.      * RecyclerView的点击事件监听接口 
  104.      */  
  105.     public interface OnItemClickListener {  
  106.         void onItemClicked(RecyclerView recyclerView, View itemView, int position);  
  107.     }  
  108.   
  109.     /** 
  110.      * RecyclerView的长按事件监听接口 
  111.      */  
  112.     public interface OnItemLongClickListener {  
  113.         boolean onItemLongClicked(RecyclerView recyclerView, View itemView, int position);  
  114.     }  
  115. }  

      上面的代码中给RecyclerView设置了OnChildAttachStateChangeListener事件监听当子View attach RecyclerView时设置事件监听

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. private RecyclerView.OnChildAttachStateChangeListener mAttachListener = new RecyclerView.OnChildAttachStateChangeListener() {  
  2.   
  3.  @Override  
  4.     public void onChildViewAttachedToWindow(View view) {  
  5.         if (mOnItemClickListener != null) {  
  6.             view.setOnClickListener(mOnClickListener);  
  7.         }  
  8.         if (mOnItemLongClickListener != null) {  
  9.             view.setOnLongClickListener(mOnLongClickListener);  
  10.         }  
  11.     }  
  12.    
  13.     @Override  
  14.     public void onChildViewDetachedFromWindow(View view) {}  
  15. };  

      使用时只需要调用addTo(RecycleView view)方法得到ItemClickSupport对象,然后调用setOnItemClickListener()方法和setOnItemLongClickListener()方法设置ItemView的点击事件和长按事件监听即可。

3.通过RecyclerView已有的方法addOnItemTouchListener()实现

3.1、查看源码

查看RecyclerView源码可以看到,RecyclerView预留了一个Item的触摸事件方法:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched 
  3.  * to child views or this view's standard scrolling behavior. 
  4.  * 
  5.  * <p>Client code may use listeners to implement item manipulation behavior. Once a listener 
  6.  * returns true from 
  7.  * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its 
  8.  * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called 
  9.  * for each incoming MotionEvent until the end of the gesture.</p> 
  10.  * 
  11.  * @param listener Listener to add 
  12.  * @see SimpleOnItemTouchListener 
  13.  */  
  14. public void addOnItemTouchListener(OnItemTouchListener listener) {  
  15.     mOnItemTouchListeners.add(listener);  
  16. }  
      通过注释我们可知,此方法是在滚动事件之前调用需要传入一个OnItemTouchListener对象OnItemTouchListener的代码如下
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static interface OnItemTouchListener {   
  2.    
  3.     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);  
  4.    
  5.     public void onTouchEvent(RecyclerView rv, MotionEvent e);  
  6.    
  7.     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);  
  8. }  
此接口还提供了一个实现类且官方推荐使用该实现类SimpleOnItemTouchListener:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and 
  3.  * default return values. 
  4.  *  
  5.  * You may prefer to extend this class if you don't need to override all methods. Another 
  6.  * benefit of using this class is future compatibility. As the interface may change, we'll 
  7.  * always provide a default implementation on this class so that your code won't break when 
  8.  * you update to a new version of the support library. 
  9.  */  
  10. public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {  
  11.    <span style="font-family:'Microsoft YaHei';">   
  12. </span>    @Override  
  13.     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {  
  14.         return false;  
  15.     }  
  16.    
  17.     @Override  
  18.     public void onTouchEvent(RecyclerView rv, MotionEvent e) {  
  19.     }  
  20.    
  21.     @Override  
  22.     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {  
  23.     }  
  24. }  
      在触摸接口中,当触摸时会回调一个MotionEvent对象,通过使用GestureDetectorCompat来解析用户的操作。

3.2、了解GestureDetector的工作原理

      对于触摸屏,其原生的消息无非按下、抬起、移动这几种,我们只需要简单重载onTouch或者设置触摸侦听器setOnTouchListener即可进行处理。不过,为了提高我们的APP的用户体验,有时候我们需要识别用户的手势,Android给我们提供的手势识别工具GestureDetector就可以帮上大忙了。

       GestureDetector的工作原理是,当我们接收到用户触摸消息时,将这个消息交给GestureDetector去加工,我们通过设置侦听器获得GestureDetector处理后的手势。

      GestureDetector提供了两个侦听器接口,OnGestureListener处理单击类消息,OnDoubleTapListener处理双击类消息。

OnGestureListener的接口有这几个:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // 单击,触摸屏按下时立刻触发    
  2. abstract boolean onDown(MotionEvent e);    
  3. // 抬起,手指离开触摸屏时触发(长按、滚动、滑动时,不会触发这个手势)    
  4. abstract boolean onSingleTapUp(MotionEvent e);    
  5. // 短按,触摸屏按下后片刻后抬起,会触发这个手势,如果迅速抬起则不会    
  6. abstract void onShowPress(MotionEvent e);    
  7. // 长按,触摸屏按下后既不抬起也不移动,过一段时间后触发    
  8. abstract void onLongPress(MotionEvent e);    
  9. // 滚动,触摸屏按下后移动    
  10. abstract boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);    
  11. // 滑动,触摸屏按下后快速移动并抬起,会先触发滚动手势,跟着触发一个滑动手势    
  12. abstract boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);    
OnDoubleTapListener的接口有这几个:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. // 双击,手指在触摸屏上迅速点击第二下时触发    
  2. abstract boolean onDoubleTap(MotionEvent e);    
  3. // 双击的按下跟抬起各触发一次    
  4. abstract boolean onDoubleTapEvent(MotionEvent e);    
  5. // 单击确认,即很快的按下并抬起,但并不连续点击第二下    
  6. abstract boolean onSingleTapConfirmed(MotionEvent e);    

有时候我们并不需要处理上面所有手势,方便起见,Android提供了另外一个类SimpleOnGestureListener实现了如上接口,我们只需要继承SimpleOnGestureListener然后重载需要的手势即可。

3.3、实现点击事件监听

      了解了GestureDetector的工作原理之后,便开始实现RecycleView的Item的点击事件。首先写一个SimpleRecycleViewItemClickListener类继承SimpleOnItemTouchListener,构造时传入Item点击回调OnItemClickListener,并覆写父类的boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e)方法,具体代码如下:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * RecyclerView的Item点击事件监听 
  3.  * 
  4.  * @author liyunlong 
  5.  * @date 2016/11/21 9:42 
  6.  */  
  7. public class SimpleRecycleViewItemClickListener extends RecyclerView.SimpleOnItemTouchListener {  
  8.   
  9.     private OnItemClickListener mListener;  
  10.     private GestureDetectorCompat mGestureDetector;  
  11.   
  12.     public SimpleRecycleViewItemClickListener(OnItemClickListener listener) {  
  13.         this.mListener = listener;  
  14.     }  
  15.   
  16.     @Override  
  17.     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {  
  18.         if (mGestureDetector == null) {  
  19.             initGestureDetector(rv);  
  20.         }  
  21.         if (mGestureDetector.onTouchEvent(e)) { // 把事件交给GestureDetector处理  
  22.             return true;  
  23.         } else {  
  24.             return false;  
  25.         }  
  26.     }  
  27.   
  28.     /** 
  29.      * 初始化GestureDetector 
  30.      */  
  31.     private void initGestureDetector(final RecyclerView recyclerView) {  
  32.         mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new GestureDetector.SimpleOnGestureListener() { // 这里选择SimpleOnGestureListener实现类,可以根据需要选择重写的方法  
  33.   
  34.             /** 
  35.              * 单击事件 
  36.              */  
  37.             @Override  
  38.             public boolean onSingleTapUp(MotionEvent e) {  
  39.                 View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());  
  40.                 if (childView != null && mListener != null) {  
  41.                     mListener.onItemClick(childView, recyclerView.getChildLayoutPosition(childView));  
  42.                     return true;  
  43.                 }  
  44.                 return false;  
  45.             }  
  46.   
  47.             /** 
  48.              * 长按事件 
  49.              */  
  50.             @Override  
  51.             public void onLongPress(MotionEvent e) {  
  52.                 View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());  
  53.                 if (childView != null && mListener != null) {  
  54.                     mListener.onItemLongClick(childView, recyclerView.getChildLayoutPosition(childView));  
  55.                 }  
  56.             }  
  57.   
  58.             /** 
  59.              * 双击事件 
  60.              */  
  61.             @Override  
  62.             public boolean onDoubleTapEvent(MotionEvent e) {  
  63.                 int action = e.getAction();  
  64.                 if (action == MotionEvent.ACTION_UP) {  
  65.                     View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());  
  66.                     if (childView != null && mListener != null) {  
  67.                         mListener.onItemDoubleClick(childView, recyclerView.getChildLayoutPosition(childView));  
  68.                         return true;  
  69.                     }  
  70.                 }  
  71.                 return false;  
  72.             }  
  73.   
  74.         });  
  75.   
  76.     }  
  77.   
  78.     /** 
  79.      * RecyclerView的Item点击事件监听接口 
  80.      * 
  81.      * @author liyunlong 
  82.      * @date 2016/11/21 9:43 
  83.      */  
  84.     public interface OnItemClickListener {  
  85.   
  86.         /** 
  87.          * 当ItemView的单击事件触发时调用 
  88.          */  
  89.         void onItemClick(View view, int position);  
  90.   
  91.         /** 
  92.          * 当ItemView的长按事件触发时调用 
  93.          */  
  94.         void onItemLongClick(View view, int position);  
  95.   
  96.         /** 
  97.          * 当ItemView的双击事件触发时调用 
  98.          */  
  99.         void onItemDoubleClick(View view, int position);  
  100.     }  
  101.   
  102.   
  103.     /** 
  104.      * RecyclerView的Item点击事件监听实现 
  105.      * 
  106.      * @author liyunlong 
  107.      * @date 2016/11/21 10:05 
  108.      */  
  109.     public class SimpleOnItemClickListener implements OnItemClickListener {  
  110.   
  111.         @Override  
  112.         public void onItemClick(View view, int position) {  
  113.   
  114.         }  
  115.   
  116.         @Override  
  117.         public void onItemLongClick(View view, int position) {  
  118.   
  119.         }  
  120.   
  121.         @Override  
  122.         public void onItemDoubleClick(View view, int position) {  
  123.   
  124.         }  
  125.     }  
  126. }  

      在GestureDetectorCompat的手势回调中我们覆写

  1. boolean onSingleTapUp(MotionEvent e):单击事件回调

  2. void onLongPress(MotionEvent e):长按事件回调

  3. boolean onDoubleTapEvent(MotionEvent e):双击事件回调

      如果我们只需要监听单击事件,而不需要监听长按事件和双击事件,构造SimpleRecycleViewItemClickListener时只需要传入SimpleOnItemClickListener即可,如果需要处理其它的手势监听,也可以覆写对应的手势回调方法。

4.三种方法对比

以上三种方式分别是

  1. 在创建ItemView时添加点击监听

  2. ItemView attach RecyclerView时实现

  3. 通过RecyclerView已有的方法addOnItemTouchListener()实现

从以上三种方式的实现过程可知:

  1. 三种均可实现ItemView的点击事件和长按事件的监听

  2. 第一种和第二种方式可以很方便对ItemView中的子View进行监听。

  3. 第三种方式可以很方便获取用户点击的坐标。

  4. 种方式和第三种方式可以写在单独的类中相对于第种写在Adapter的方式可使代码更独立整洁。

综上所述:

      如果你只想监听ItemView的点击事件或长按事件三种方式均可

      如果你想监听ItemView中每个子View的点击事件采用第种或者第种比较方便。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 如果迷路了你会怎么办 吃多了抽烟想吐怎么办 晚上牙疼得要命怎么办 楼梯被火封锁后怎么办 牙齿疼怎么办能快速不疼 我被短信轰炸了怎么办 火警响了在家该怎么办 痔疮肉球变大了怎么办 痔疮长了好几个怎么办 苹果7手机丢了怎么办 如果油锅着火了怎么办 家里电气著火了怎么办 你家油锅起火了怎么办 交通事故后对方不肯去处理怎么办 租的车出了事故怎么办 借的车出了事故怎么办 台风来了怎么办小知识 台中班台风来了怎么办 地震时在五楼怎么办 小事故对方不来怎么办 发生交通事故对方不处理怎么办 当地震来了该怎么办 地震来了该怎么办教案 地震来了怎么办的问题 住30楼的地震了怎么办 在家里地震来了怎么办? 乐高地震来了怎么办? 地震来了该怎么办300字 地震来了该怎么办200 外地在北京生孩子建档怎么办 怀孕2个月头疼怎么办 怀孕6个月头疼怎么办 轮胎扎了个钉子怎么办 顶菅遇到填土区怎么办 12墙没砌在梁上怎么办 孕32周胎盘二级怎么办 孕36周胎盘三级怎么办 孕24周胎盘一级怎么办 孕24周 胎盘1级怎么办 抗核抗体1:1000怎么办 抗核抗体1:320怎么办