Android RecyclerView的Item点击事件实现整理

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

RecyclerView的滚动事件研究 - DevWiki
RecyclerView的ViewHolder和Adapter的封装优化 - DevWiki
RecyclerView问题记录 - DevWiki

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

  1. 在创建 ItemView时添加点击监听
  2. 当 ItemView attach RecyclerView时实现
  3. 通过RecyclerView已有的方法addOnItemTouchListener()实现

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

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

public class SampleAdapter extends RecyclerView.Adapter<SampleAdapter.SampleViewHolder> { 

  private List<DataBean> mDatas;
  private OnItemClickListener mListener; // Item点击事件 

  public DataBean getItem(int position) {
    return mDatas == null ? null : mDatas.get(position);
  } 

  @Override
  public SampleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent,false);
    return new SampleViewHolder(itemView);
  } 

  @Override
  public void onBindViewHolder(SampleViewHolder holder, int position) { 

  } 

  @Override
  public int getItemCount() {
    return mDatas == null ? 0 : mDatas.size();
  } 

  class SampleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { 

    public SampleViewHolder(View itemView) {
      super(itemView);
      // TODO:初始化View
      ... 

      itemView.setOnClickListener(this);
      itemView.setOnLongClickListener(this);
    } 

    @Override
    public void onClick(View v) {
      if (mListener != null) {
        mListener.onItemClick(SampleAdapter.this, v, getLayoutPosition());
      }
    } 

    @Override
    public boolean onLongClick(View v) {
      if (mListener != null) {
        mListener.onItemLongClick(SampleAdapter.this, v, getLayoutPosition());
        return true;
      }
      return false;
    }
  }
}

2.当ItemView attach RecyclerView时实现

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

public class ItemClickSupport { 

  private static final int KEY = 0x99999999;
  private final RecyclerView mRecyclerView;
  private OnItemClickListener mOnItemClickListener;
  private OnItemLongClickListener mOnItemLongClickListener; 

  private View.OnClickListener mOnClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      if (mOnItemClickListener != null) {
        RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
        mOnItemClickListener.onItemClicked(mRecyclerView, v, holder.getAdapterPosition());
      }
    }
  }; 

  private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
      if (mOnItemLongClickListener != null) {
        RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
        return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, v, holder.getAdapterPosition());
      }
      return false;
    }
  }; 

  private RecyclerView.OnChildAttachStateChangeListener mAttachListener = new RecyclerView.OnChildAttachStateChangeListener() { 

    @Override
    public void onChildViewAttachedToWindow(View view) {
      if (mOnItemClickListener != null) {
        view.setOnClickListener(mOnClickListener);
      }
      if (mOnItemLongClickListener != null) {
        view.setOnLongClickListener(mOnLongClickListener);
      }
    } 

    @Override
    public void onChildViewDetachedFromWindow(View view) {
    }
  }; 

  /**
   * ItemClickSupport的私有构造方法
   */
  private ItemClickSupport(RecyclerView recyclerView) {
    mRecyclerView = recyclerView;
    mRecyclerView.setTag(KEY, this);
    // 为RecyclerView设置OnChildAttachStateChangeListener事件监听
    mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
  } 

  /**
   * 为RecyclerView设置ItemClickSupport
   */
  public static ItemClickSupport addTo(RecyclerView view) {
    ItemClickSupport support = (ItemClickSupport) view.getTag(KEY);
    if (support == null) {
      support = new ItemClickSupport(view);
    }
    return support;
  } 

  /**
   * 为RecyclerView移除ItemClickSupport
   */
  public static ItemClickSupport removeFrom(RecyclerView view) {
    ItemClickSupport support = (ItemClickSupport) view.getTag(KEY);
    if (support != null) {
      support.detach(view);
    }
    return support;
  } 

  /**
   * 为RecyclerView设置点击事件监听
   */
  public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
    mOnItemClickListener = listener;
    return this;
  } 

  /**
   * 为RecyclerView设置长按事件监听
   */
  public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
    mOnItemLongClickListener = listener;
    return this;
  } 

  /**
   * 为RecyclerView移除OnChildAttachStateChangeListener事件监听
   */
  private void detach(RecyclerView view) {
    view.removeOnChildAttachStateChangeListener(mAttachListener);
    view.setTag(KEY, null);
  } 

  /**
   * RecyclerView的点击事件监听接口
   */
  public interface OnItemClickListener {
    void onItemClicked(RecyclerView recyclerView, View itemView, int position);
  } 

  /**
   * RecyclerView的长按事件监听接口
   */
  public interface OnItemLongClickListener {
    boolean onItemLongClicked(RecyclerView recyclerView, View itemView, int position);
  }
}

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

private RecyclerView.OnChildAttachStateChangeListener mAttachListener = new RecyclerView.OnChildAttachStateChangeListener() { 

 @Override
  public void onChildViewAttachedToWindow(View view) {
    if (mOnItemClickListener != null) {
      view.setOnClickListener(mOnClickListener);
    }
    if (mOnItemLongClickListener != null) {
      view.setOnLongClickListener(mOnLongClickListener);
    }
  } 

  @Override
  public void onChildViewDetachedFromWindow(View view) {}
};

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

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

3.1、查看源码

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

/**
 * Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
 * to child views or this view's standard scrolling behavior.
 *
 * <p>Client code may use listeners to implement item manipulation behavior. Once a listener
 * returns true from
 * {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
 * {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
 * for each incoming MotionEvent until the end of the gesture.</p>
 *
 * @param listener Listener to add
 * @see SimpleOnItemTouchListener
 */
public void addOnItemTouchListener(OnItemTouchListener listener) {
  mOnItemTouchListeners.add(listener);
}

通过注释我们可知,此方法是在滚动事件之前调用,需要传入一个OnItemTouchListener对象。OnItemTouchListener的代码如下:

public static interface OnItemTouchListener {  

  public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e); 

  public void onTouchEvent(RecyclerView rv, MotionEvent e); 

  public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}

此接口还提供了一个实现类,且官方推荐使用该实现类SimpleOnItemTouchListener:

/**
 * An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
 * default return values.
 *
 * You may prefer to extend this class if you don't need to override all methods. Another
 * benefit of using this class is future compatibility. As the interface may change, we'll
 * always provide a default implementation on this class so that your code won't break when
 * you update to a new version of the support library.
 */
public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
  <span style="font-family:'Microsoft YaHei';">
</span>  @Override
  public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    return false;
  } 

  @Override
  public void onTouchEvent(RecyclerView rv, MotionEvent e) {
  } 

  @Override
  public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
  }
}

在触摸接口中,当触摸时会回调一个MotionEvent对象,通过使用GestureDetectorCompat来解析用户的操作。

3.2、了解GestureDetector的工作原理

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

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

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

// 单击,触摸屏按下时立刻触发
abstract boolean onDown(MotionEvent e);
// 抬起,手指离开触摸屏时触发(长按、滚动、滑动时,不会触发这个手势)
abstract boolean onSingleTapUp(MotionEvent e);
// 短按,触摸屏按下后片刻后抬起,会触发这个手势,如果迅速抬起则不会
abstract void onShowPress(MotionEvent e);
// 长按,触摸屏按下后既不抬起也不移动,过一段时间后触发
abstract void onLongPress(MotionEvent e);
// 滚动,触摸屏按下后移动
abstract boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
// 滑动,触摸屏按下后快速移动并抬起,会先触发滚动手势,跟着触发一个滑动手势
abstract boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
OnDoubleTapListener的接口有这几个:
// 双击,手指在触摸屏上迅速点击第二下时触发
abstract boolean onDoubleTap(MotionEvent e);
// 双击的按下跟抬起各触发一次
abstract boolean onDoubleTapEvent(MotionEvent e);
// 单击确认,即很快的按下并抬起,但并不连续点击第二下
abstract boolean onSingleTapConfirmed(MotionEvent e);

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

3.3、实现点击事件监听

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

/**
 * RecyclerView的Item点击事件监听
 *
 * @author liyunlong
 * @date 2016/11/21 9:42
 */
public class SimpleRecycleViewItemClickListener extends RecyclerView.SimpleOnItemTouchListener { 

  private OnItemClickListener mListener;
  private GestureDetectorCompat mGestureDetector; 

  public SimpleRecycleViewItemClickListener(OnItemClickListener listener) {
    this.mListener = listener;
  } 

  @Override
  public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    if (mGestureDetector == null) {
      initGestureDetector(rv);
    }
    if (mGestureDetector.onTouchEvent(e)) { // 把事件交给GestureDetector处理
      return true;
    } else {
      return false;
    }
  } 

  /**
   * 初始化GestureDetector
   */
  private void initGestureDetector(final RecyclerView recyclerView) {
    mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new GestureDetector.SimpleOnGestureListener() { // 这里选择SimpleOnGestureListener实现类,可以根据需要选择重写的方法 

      /**
       * 单击事件
       */
      @Override
      public boolean onSingleTapUp(MotionEvent e) {
        View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && mListener != null) {
          mListener.onItemClick(childView, recyclerView.getChildLayoutPosition(childView));
          return true;
        }
        return false;
      } 

      /**
       * 长按事件
       */
      @Override
      public void onLongPress(MotionEvent e) {
        View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && mListener != null) {
          mListener.onItemLongClick(childView, recyclerView.getChildLayoutPosition(childView));
        }
      } 

      /**
       * 双击事件
       */
      @Override
      public boolean onDoubleTapEvent(MotionEvent e) {
        int action = e.getAction();
        if (action == MotionEvent.ACTION_UP) {
          View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
          if (childView != null && mListener != null) {
            mListener.onItemDoubleClick(childView, recyclerView.getChildLayoutPosition(childView));
            return true;
          }
        }
        return false;
      } 

    }); 

  } 

  /**
   * RecyclerView的Item点击事件监听接口
   *
   * @author liyunlong
   * @date 2016/11/21 9:43
   */
  public interface OnItemClickListener { 

    /**
     * 当ItemView的单击事件触发时调用
     */
    void onItemClick(View view, int position); 

    /**
     * 当ItemView的长按事件触发时调用
     */
    void onItemLongClick(View view, int position); 

    /**
     * 当ItemView的双击事件触发时调用
     */
    void onItemDoubleClick(View view, int position);
  } 

  /**
   * RecyclerView的Item点击事件监听实现
   *
   * @author liyunlong
   * @date 2016/11/21 10:05
   */
  public class SimpleOnItemClickListener implements OnItemClickListener { 

    @Override
    public void onItemClick(View view, int position) { 

    } 

    @Override
    public void onItemLongClick(View view, int position) { 

    } 

    @Override
    public void onItemDoubleClick(View view, int position) { 

    }
  }
}

在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()实现

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

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

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

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

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

综上所述:

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

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

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

(0)

相关推荐

  • android RecyclerView实现条目Item拖拽排序与滑动删除

    效果演示 需求和技术分析 RecyclerView Item拖拽排序::长按RecyclerView的Item或者触摸Item的某个按钮. RecyclerView Item滑动删除:RecyclerView Item滑动删除:RecyclerView的Item滑动删除. 实现方案与技术 利用ItemTouchHelper绑定RecyclerView.ItemTouchHelper.Callback来实现UI更新,并且实现动态控制是否开启拖拽功能和滑动删除功能. 实现步骤 继承抽象类ItemTo

  • Android RecyclerView选择多个item的实现代码

    模仿网易新闻客户端阅读偏好的频道选择,先看实现的页面: 直接上代码: import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.DefaultItemAnimator; import an

  • Android 中RecyclerView多种item布局的写法(头布局+脚布局)

    RecyclerView多个item布局的写法(头布局+脚布局) 上图 github 下载源码 Initial commit第一次提交的代码,为本文内容 以下的为主要代码,看注释即可,比较简单 MainActivity 含上拉加载更多 package com.anew.recyclerviewall; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivi

  • android使用ItemDecoration给RecyclerView 添加水印

    前言 项目中有使用到水印效果,如下图所示.在实现过程中,最终选用ItemDecoration来实现,其中有两大步骤:自定义Drawable来完成水印图片.使用ItemDecoration来布局水印. Demo在 WatermarkFragment 中,效果图如下: 1. 自定义Drawable完成水印图片 public class MyDrawable extends Drawable { Paint mPaint; public MyDrawable() { mPaint = new Pain

  • Android RecyclerView显示Item布局不一致解决办法

    RecyclerView显示Item布局不一致 在自定义RecyclerAdapter的时候,在重写onCreateViewHolder方法是使用了 @Override public H onCreateViewHolder(ViewGroup parent, int viewType) { View view=View.inflate(context,layoutId,null); return view; } 进行生成布局,结果发现生成的布局没有LayoutParams.以前自定义View的

  • Android RecyclerView的Item自定义动画及DefaultItemAnimator源码分析

    这是关于RecyclerView的第二篇,说的是如何自定义Item动画,但是请注意,本文不包含动画的具体实现方法,只是告诉大家如何去自定义动画,如何去参考源代码. 我们知道,RecyclerView默认会使用DefaultItemAnimator,所以如果我们需要自定义动画,那么应该好好的读读这个类的源代码,这样不仅仅是学习怎么自定义,还要学习Android的设计模式. 先弄明白一件事,DefaultItemAnimator继承自SimpleItemAnimator,SimpleItemAnim

  • Android中RecyclerView的item宽高问题详解

    前言 本文主要给大家介绍了关于Android中RecyclerView的item宽高问题的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 在创建viewholder传入的View时,如果不指定其viewgroup,就会出现宽高只包裹显示内容的问题. View view = LayoutInflater.from(context).inflate(R.layout.test_test,null); 上面的做法就会出问题 改成这样就可以正常显示设置的宽高 View vie

  • Android中RecyclerView点击Item设置事件

    在上一篇Android RecylerView入门教程中提到,RecyclerView不再负责Item视图的布局及显示,所以RecyclerView也没有为Item开放OnItemClick等点击事件,这就需要开发者自己实现.博客最下面有Demo程序运行动画. 奉上Demo的Github链接. 在调研过程中,发现有同学修改RecyclerView源码来实现Item的点击监听,但认为这不是一个优雅的解决方案,最终决定在RecyclerView.ViewHolder上做文章. 思路是:因为ViewH

  • Android RecyclerView的Item点击事件实现整理

    自从开始使用RecyclerView代替ListView,会发现有很多地方需要学习.前一段时间的学习记录有: RecyclerView的滚动事件研究 - DevWiki RecyclerView的ViewHolder和Adapter的封装优化 - DevWiki RecyclerView问题记录 - DevWiki 实现 RecyclerView的Item的点击事件有三种方式: 在创建 ItemView时添加点击监听 当 ItemView attach RecyclerView时实现 通过Rec

  • Android  RecyclerView的Item点击事件实现整理

    自从开始使用RecyclerView代替ListView,会发现有很多地方需要学习.前一段时间的学习记录有: RecyclerView的滚动事件研究 - DevWiki RecyclerView的ViewHolder和Adapter的封装优化 - DevWiki RecyclerView问题记录 - DevWiki 实现 RecyclerView的Item的点击事件有三种方式: 在创建 ItemView时添加点击监听 当 ItemView attach RecyclerView时实现 通过Rec

  • Recyclerview添加头布局和尾布局、item点击事件详解

    简介: 本篇博客主要包括recyclerview添加多种布局以及添加头布局和尾布局,还有item点击事件 思路: 主要重写Recyclerview.Adapter中的一些方法 1.public int getItemCount()  item熟练  +2(头布局和尾布局) 2.public int getItemViewType(int position)   判断position 设置itemType 3.创建不同的ViewHolder,分别用来加载头布局,正常布局,尾布局 4.public

  • Android ListView的Item点击效果的定制

    Android ListView的Item点击效果的定制           前言:           对于listview Android开发的朋友都知道用的很多,网上关于Android ListView的Item点击特效的文章很多,我自己也看了不少关于listview的文章,这里就记录下不错的文章,大家可以参考下, 在之前弄这个效果说真的很不明智,我是在Item的布局文件加个selector的xml文件来实现ListView的Item点击效果.. 这个算是我自己记录以后该如何使用的另一种方

  • Android给TextView添加点击事件的实现方法

    首先设定TextView的clickable属性为true. 可以在布局文件中进行设定,比如: <TextView android:id="@+id/phone" android:clickable="true" --------->设定此属性 android:layout_marginLeft="10dp" android:layout_below="@id/address" android:layout_toR

  • Android编程出现Button点击事件无效的解决方法示例

    本文实例讲述了Android编程出现Button点击事件无效的解决方法.分享给大家供大家参考,具体如下: 遇到这样一个问题,给一个界面上方的按钮添加了点击事件,但死活没反应,而放在界面下方的3个按钮,都有相应点击事件,百度了一下无非有两种可能: 1.button没有初始化或者button初始化多次,导致混乱. 2.button点击事件写错,无法监听. 但我确定的是这些都是没有错的,后来找到的原因是下方的scroll布局覆盖了上方的button的布局,使用了fill_parent,所以获取不到点击

  • Android fragment实现按钮点击事件的示例讲解

    fragment无法直接进行点击事件,需要放到oncreatActivity中 代码如下: @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_first, null); return view; } 点击事件代码: @Override

  • Android 中ListView的Item点击事件失效的快速解决方法

    在平常的开发过程中,我们的ListView可能不只是简单的显示下文本或者按钮,更多的是显示复杂的布局,这样的话,我们就得自己写布局和自定义adapter了,一般是继承于BaseAdapter,示例代码见下方.写ListView的点击事件时OnItemClickListener,onItemClick方法没有执行,导致ListView中Item条目点击事件失效,而Item中的View点击事件可以在getView方法中进行处理.导致整个Item点击失效的原因多半是由于在[你自己定义的Item中存在诸

  • Android自定义Notification添加点击事件

    前言 在上一篇文章中<Notification自定义界面>中我们实现了自定义的界面,那么我们该怎么为自定义的界面添加点击事件呢?像酷狗在通知栏 有"上一首","下一首"等控制按钮,我们需要对按钮的点击事件进行响应,不过方法和之前的点击设置不一样,需要另外处理,下面我将进行简单的说明. 实现 同样,我们需要一个Service的子类MyService,然后在MyService的onCreate中设置,如下代码: public class MyService

随机推荐