为RecyclerView定制可滑动的Item

  最近项目有需要弄一个可以像手机QQ会话页一样可以滑动的小菜单,每一个Item当用户在向左滑动的时候右侧会出现一个小菜单当时就想在也不是很难心想着找个开源的使用就好呢,但是我的项目是用的RecyclerView网上基本没有类似的没办法只能自己弄一个。

  先说一说我的实现原理我把每个Item看成一个LinearLayout它包含两个子控件一个是Item要显示的内容还有一个当然就是我们的右侧菜单了,这个顶部LinearLayout就是包含内容与菜单的容器,通过滚动这个容器的scrollX来显示我们的菜单,原理还是不难的,我把整个菜单的显示与隐藏把封闭在ItemSlideHelper里面这样想换一种方法实现也不用去别的代码很方便,当然ItemSlidHelper是要实现RecyclerView.OnItemTouchListener的因为我们的基本操作都是基于Touch事件的,有兴趣的可以看看,有BUG可以回复我。

  关于RecyclerView.OnItemTouchListener的几个方法我也学习了下也不是很难主要是拦截与操作这两个东西一定要配合好,还有就是RecyclerView的滚动状态,因为在RecyclerVIew滚动的时候我们的滑动菜单是不能操作的不然就会产生混乱,在项目开发的时候由于我的Item是有onClick事件的,那么在用户滑出菜单的时候也要把onClick事件给拦截但是又不能拦截菜单的Onclick事件我是通过容器的rect与scrollX的偏移来解决这个问题的可以看源码就知道呢。下面是拦截代码。

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

        if(!mCallback.isEnable())
            return false;

        int action =  MotionEventCompat.getActionMasked(e);
        int x = (int) e.getX();
        int y = (int) e.getY();

        /*
        * 当我们没有发生drag事件的时候cancel或up事件会发生interceptTouchEvent里面,如果TargetView等于空的时候直接
        * 返回false,不拦截事件
        * */
        if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP)
            if(mTargetView == null)
                return false;

        boolean needIntercept = false;
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                mActivePointerId = MotionEventCompat.getPointerId(e, 0);
                mLastX = (int) e.getX();
                mLastY = (int) e.getY();

                //查找需要显示菜单的view;
                mTargetView = mCallback.findTargetView(x, y);

                /*
                * 如果正在动画则拦截事件
                * */
                if (mExpandAndCollapseAnim != null) {
                    //mExpandAndCollapseAnim.cancel();
                    mExpandAndCollapseAnim = null;
                    needIntercept = true;
                }
                break;
            case MotionEvent.ACTION_MOVE:

                int deltaX = (x - mLastX);
                int deltaY = (y - mLastY);

                if(Math.abs(deltaY) > Math.abs(deltaX))
                    return false;

                //如果移动距离达到要求,则拦截
                needIntercept = mIsDragging = mTargetView != null && Math.abs(deltaX) >= mTouchSlop;
                break;

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:

                /*
                * 当一个up事件发生在正常的范围内且scrollX等于scrollRange则折叠view并拦截UP事件
                * 防止view响应点击事件
                * */
                if(isExpanded()){

                    if (inView(x, y)) {
                        //拦截事件,防止targetView执行onClick事件
                        needIntercept = true;
                    }else{
                        //如果走这那行这个ACTION_UP的事件会发生在右侧的菜单中
                    }

                    //折叠菜单
                    mTargetView.setScrollX(0);
                }
                dispatchCollapsedOrExpanded();
                break;
        }

        return  needIntercept && mTargetView != null;
    }

  对于滚动和动画的一些操作就比较简单了,没有DOWN事件是因为RecyclerView会在转发这个事件的事件故意不转发的可以看RecyclerViewr的dispatchOnItemTouch方法就知道了,在MOVE里面直接算一下移动的距离之类的就好了,通过deltaX来滚动容器的scrollX这样就可以实现在拖动时候显示或隐藏右侧的菜单。

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

        //如果要响应fling事件设置将mIsDragging设为false
        if (mGestureDetector.onTouchEvent(e)) {
            mIsDragging = false;
            return;
        }

        int x = (int) e.getX();
        int y = (int) e.getY();
        int action =  MotionEventCompat.getActionMasked(e);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //RecyclerView 不会转发这个Down事件

                break;
            case MotionEvent.ACTION_MOVE:
                int deltaX = (int) (mLastX - e.getX());
                horizontalDrag(deltaX);
                mLastX = x;
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

                 if(mIsDragging){
                    smoothHorizontalExpandOrCollapse(0);
                }

                dispatchCollapsedOrExpanded();
                mIsDragging = false;

                break;
        }

    }

  提示一下就是在RecyclerView滚动的时候我们应该要保存每个Item的展开和折叠的状态因为RecyclerVIew会回收重复利用Holder如果不保存的放就会就会混乱,这个看一下SlideItemAdapter的实现就好了很简单。

    @Override
    public void onCollapsed(RecyclerView.ViewHolder holder) {
        mItemState.put(holder.getAdapterPosition(), false);
        Log.d("slide", "onCollapsed");
    }

    @Override
    public void onExpanded(RecyclerView.ViewHolder holder) {
        mItemState.put(holder.getAdapterPosition(), true);
        Log.d("slide", "onExpanded");
    }

  在操作的时候我也写了一个回调接口,有些方法不是必需的但我还是写在了回调里因为我实在是想保存RecyclerView的实例。

 public interface Callback {

        void onCollapsed(RecyclerView.ViewHolder holder);

        void onExpanded(RecyclerView.ViewHolder holder);

        int getHorizontalRange(RecyclerView.ViewHolder holder);

        RecyclerView.ViewHolder getChildViewHolder(View childView);

        View findTargetView(float x, float y);

        boolean isEnable();
    }

  在使用的时候一定要调用下面代码将ItemslideHelper与RecyclerView关联起来.

        mRecyclerView.addOnItemTouchListener(new ItemSlideHelper(mRecyclerView.getContext(), this));

  

项目我已经传到Github上了

https://github.com/qq542529039/slideitem.git

时间: 2024-10-15 14:18:34

为RecyclerView定制可滑动的Item的相关文章

滑动删除Item,拖拽切换Item,你想了解的都在这儿

1. 概述 如果上两篇对RecyclerView介绍后,依然没有引起你的兴趣,那么下面关于RecyclerView的使用我相信一定会让你如获珍宝.直接看运行效果. 图-1 RecyclerView滑动删除 图-2 RecyclerView拖拽切换 用ListView或者GridView实现上图中的功能非常麻烦,而如果用RecyclerView来实现,则非常简单. 如果对RecyclerView还没有认识的同学,可以查看这两篇文章. RecyclerView使用一 RecyclerView使用二

RecyclerView使用 及 滑动时加载图片优化方案

RecyclerView使用 及 滑动时加载图片优化方案 简述 本篇博文主要给大家分享关于RecyclerView控件的使用及通过继承RecyclerView来实现滑动时加载图片的优化方案,也同样能解决防止图片乱序的问题,之前有在网上有看到大神对Android中ListView异步加载图片乱序问题进行过分析,并深入剖析原理后分别给出了3种对应的解决方案:一 .使用findViewWithTag.二.使用弱引用关联.三.使用Volley框架提供的NetworkImageView. 看了之后思索了很

【转】Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

原文网址:http://blog.csdn.net/xiaanming/article/details/17539199 我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListVi

本文出自xiaanming的博客(http://blog.csdn.net/xiaanming/article/details/18311877)Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果,之前使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就知道

Android 使用NineOldAndroids实现绚丽的ListView左右滑动删除Item效果

今天还是给大家带来自定义控件的编写,自定义一个ListView的左右滑动删除Item的效果,这个效果之前已经实现过了,有兴趣的可以看下Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果, 之前使用的是滑动类Scroller来实现的,但是看了下通知栏的左右滑动删除效果,确实很棒,当我们滑动Item超过一半的时候,item的透明度就变 成了0,我们就知道抬起手指的时候item就被删除了,当item的透明度不为0的时候,我们抬起手指Item会回到起始位置,这样我们就

android 下拉刷新上拉加载更多,高仿ios左滑动删除item,解决了众多手势问题

一.前言 老规矩,别的不说,这demo是找了很相关知识集合而成的,可以说对我这种小白来说是绞尽脑汁!程序员讲的是无图无真相!现在大家一睹为快! 二.比较关键的还是scroller这个类的 package com.icq.slideview.view; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; i

[转]Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该

ListView 实现带有Filpper效果的左右滑动删除 Item

ListView 实现带有Filpper效果的左右滑动删除 Item  的实现最主要的方法还是 对 Listview 的继承重写 .然后是在删除过程中添加 TranslateAnimation 滑动事件. <span style="font-size:14px;">public class FilpperActivity extends Activity { private FilpperListvew flipperListView; private MyAdapter

Android 使用Scroller实现绚丽的ListView左右滑动删除Item效果

我在上一篇文章中Android 带你从源码的角度解析Scroller的滚动实现原理从源码的角度介绍了Scroller的滚动实现原理,相信大家对Scroller的使用有一定的了解,这篇文章就给大家带来使用Scroller的小例子,来帮助大家更加熟悉的掌握Scroller的使用,掌握好了Scroller的使用我们就能实现很多滑动的效果.例如侧滑菜单,launcher,ListView的下拉刷新等等效果,我今天实现的是ListView的item的左右滑动删除item的效果,现在很多朋友看到这个效果应该