开发问题记录随笔之关于轻击RecyclerView中Item原设置点击水波纹效果无效的反思与修改

如何设置item点击水波纹效果:

  1. 在drawable文件夹下创建 linearlayout_water_selector.xml



    
    


  1. 在drawable-v24文件夹下创建 linearlayout_water_selector.xml


    
    



  1. 使用的颜色代码添加到res-values-colors下
#c1c1c2

  #ffffff
  1. 将布局引用到RecyclerView子项布局里
android:background="@drawable/linearlayout_water_selecto"
    android:clickable="true"
    android:focusable="true"

此时设置完水波纹的点击效果


但是不要急,我们再来看看RecyclerView中如何设置item可点击,这里笔者采用的是addOnItemTouchListener方法,由api提供:

RecyclerView.addOnItemTouchListener(OnItemTouchListener listener)

接下来看看完整代码

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) {
    }

    //OnGestureListener主要回调各种单击事件,而OnDoubleTapListener回调各种双击事件
    //sdk 还提供了一个外部类SimpleOnGestureListener,这个类实现了上面两个接口的所有方法。
    // 但全都是空实现,函数体里什么也没写,其中就是把上面两个接口合并一下,给出默认的空实现。
    // 这样继承SimpleOnGestureListener的时候就不用实现每一个方法了。
    private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
        //单击
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                int position = recyclerView.getChildLayoutPosition(child);
                onItemClick(vh,position);
            }
            return true;
        }

        //双击
        public boolean onDoubleTap(MotionEvent e){
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                int position = recyclerView.getChildLayoutPosition(child);
                onItemDoubleClick(vh,position);
            }
            return true;
        }

        //长按
        public void onLongPress(MotionEvent e){
            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
                int position = recyclerView.getChildLayoutPosition(child);
                onItemLongClick(vh, position);
            }
        }
    }

    public abstract void onItemClick(RecyclerView.ViewHolder vh,int position);
    public abstract void onItemDoubleClick(RecyclerView.ViewHolder vh, int position);
    public abstract void onItemLongClick(RecyclerView.ViewHolder vh, int position);
}

这段代码很长,我们分析一下大概流程:

  1. 首先是自定义一个抽象类偶OnRecyclerItemClickListener实现接口RecyclerView.onItemTouchListener,并实例化手势检测对象和RecyclerView对象。

  1. 接着是一个构造器,下面的三个方法一个处理事件拦截,一个处理事件点击,一个处理事件冲突,都写了注释,相信了解事件分发的同学一定不陌生。

  1. 往下走是一个私有内部类ItemTouchHelperGestureListener继承了GestureDetector.SimpleOnGestureListener,注释上也写明了这个类可以实现OnGestureListener和OnDoubleTapListener两个接口的全部方法,那我们就来看看这两个接口分别实现了什么方法吧

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);

  1. 关于分析我们一会再说,接上步骤,最后在你想要设置点击的页面添加如下代码
//我自己实例化的对象,读者按需修改
private RecyclerView recyclerViewForNews;
        //添加点击事件
        recyclerViewForNews.addOnItemTouchListener(new OnRecyclerItemClickListener(recyclerViewForNews){
            @Override
            public void onItemClick(RecyclerView.ViewHolder vh,int position) {
                //item点击事件
                String url = mUrlList.get(position);
                // 跳转传值
                Intent intent = new Intent(getActivity(), NewsActivity.class);
                intent.putExtra("url", url);
                startActivity(intent);

            }

            public void onItemDoubleClick(RecyclerView.ViewHolder vh, int position){
                //item双击事件
                String url = mUrlList.get(position);
                // 跳转传值
                Intent intent = new Intent(getActivity(), NewsActivity.class);
                intent.putExtra("url", url);
                startActivity(intent);
            }

            public void onItemLongClick(RecyclerView.ViewHolder vh, int position){
                //item长按事件
                String url = mUrlList.get(position);
                // 跳转传值
                Intent intent = new Intent(getActivity(), NewsActivity.class);
                intent.putExtra("url", url);
                startActivity(intent);
            }
        });

简单说明一下,RecyclerView的对象是我自己设置的,仅服务于自己的项目,包括三个方法里的所有逻辑都是为了我自己项目中所需要的跳转,读者按需修改即可。

  1. 分析:为什么这篇文章的标题叫做“反思与修改”呢?一般在别的文章中仅仅让你重写几个方法,设置一下xml文件,就完事了。但是细心的你一定会发现public boolean onSingleTapUp(MotionEvent e)和public boolean onSingleTapConfirmed(MotionEvent e)方法非常相似,只差了一个Confirmed,但是应用在项目中效果是完全不同的。

public boolean onSingleTapUp(MotionEvent e)应用在我们的项目中的话,假设你点击item时非常轻,几乎不怎么用力触摸屏幕,你会发现有时我们上述设置的水波纹效果是无法显示出来的,这明显与我们的需求不符。况且经过笔者本身的试验,点击一些有水波纹效果的app无论是轻是重都会显示出水波纹效果。
public boolean onSingleTapConfirmed(MotionEvent e)应用于我们的项目中的话,由于需要一段时间确认你触摸了屏幕几次,所以无论你点击屏幕是轻是重都会显示出我们的水波纹效果,于是我们在上述代码中选择重写这个方法。

接下来重写的两个方法就非常符合逻辑了,假如用户真的点了两次呢?或者假如用户长按不放呢?于是我们选择重写public boolean onDoubleTap(MotionEvent e)和public void onLongPress(MotionEvent e)方法,也就完成了基本的点击,双击,长按都可以显示出水波纹效果的基本需求了。

欢迎指正。

你可能感兴趣的:(开发问题记录随笔之关于轻击RecyclerView中Item原设置点击水波纹效果无效的反思与修改)