android技术收藏Androidandroid基础

Android 基于RecyclerView的Item侧滑删除

2016-07-01  本文已影响21618人  SheHuan

RecyclerView的强大之处就不用多说了,谁用谁知道哦,本着学习的态度我们来给RecyclerView加上侧滑删除Item的功能,话不多说,先看图:


ItemRemoveRecyclerView

Gif效果不够理想,呜呜......

其实核心思想很简单,就是通过重写RecyclerView的onTouchEvent()方法来检测手势的变化实现的,大致的流程如下:
1、根据手指触摸的坐标点找到对应Item的ViewHolder,进而得到相应的Item布局View。
2、手指继续移动,在条件满足的情况下,通过scrollBy()使Item布局View内容跟随手指一起移动,当然要注意边界检测。
3、手指抬起时,根据Item布局View内容移动的距离以及手指的滑动速度,判断是否显示删除按钮,进而通过startScroll()使Item布局View自动滑动到目标位置。
4、点击删除按钮则删除对应Item,点击其它区域则隐藏删除按钮。

由于Item的侧滑删除效果需要通过Scroller辅助实现的,还不了解Scroller的同学可以看下这篇文章:Android Scroller实现View弹性滑动完全解析

接下来看一下具体的实现过程:
先看一下onTouchEvent的MotionEvent.ACTION_DOWN事件处理:

    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);

        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (mDeleteBtnState == 0) {
                    View view = findChildViewUnder(x, y);
                    if (view == null) {
                        return false;
                    }

                    MyViewHolder viewHolder = (MyViewHolder) getChildViewHolder(view);

                    mItemLayout = viewHolder.layout;
                    mPosition = viewHolder.getAdapterPosition();

                    mDelete = (TextView) mItemLayout.findViewById(R.id.item_delete);
                    mMaxLength = mDelete.getWidth();
                    mDelete.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            mListener.onDeleteClick(mPosition);
                            mItemLayout.scrollTo(0, 0);
                            mDeleteBtnState = 0;
                        }
                    });
                } else if (mDeleteBtnState == 3) {
                    mScroller.startScroll(mItemLayout.getScrollX(), 0, -mMaxLength, 0, 200);
                    invalidate();
                    mDeleteBtnState = 0;
                    return false;
                } else {
                    return false;
                }

                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);
    }

我们规定删除按钮有四个状态(mDeleteBtnState):0:关闭,1:将要关闭,2:将要打开,3:打开
当删除按钮未展示时,即if (mDeleteBtnState == 0)时,通过findChildViewUnder()方法得到触摸点对应的Item View,接下来通过getChildViewHolder()得到对应的ViewHolder,有了ViewHolder,我们就可以解析出Item的布局mItemLayout以及当前Item的下标mPosition,最后得到mMaxLength ,即删除按钮的宽度也就是Item的最大滑动距离,同时给删除按钮绑定事件。
else if (mDeleteBtnState == 3)
时,Item上的删除按钮完全展示,如果点击删除按钮外的任意区域则通过startScroll()方法使Item自动右滑直到删除按钮完全隐藏,并且onTouchEvent()方法返回flase,这样此次事件结束,不会继续传递。
如果前两个条件都不满足,表示上一次Item的滑动操作尚未结束,则直接返回false,保证上一次的滑动操作顺利完成。

onTouchEvent的MotionEvent.ACTION_MOVE事件处理代码如下:

    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);

        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                int dx = mLastX - x;
                int dy = mLastY - y;

                int scrollX = mItemLayout.getScrollX();
                if (Math.abs(dx) > Math.abs(dy)) {
                    isItemMoving = true;
                    if (scrollX + dx <= 0) {//左边界检测
                        mItemLayout.scrollTo(0, 0);
                        return true;
                    } else if (scrollX + dx >= mMaxLength) {//右边界检测
                        mItemLayout.scrollTo(mMaxLength, 0);
                        return true;
                    }
                    mItemLayout.scrollBy(dx, 0);//item跟随手指滑动
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }

        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);
    }

当手指滑动的时候,如果水平滑动距离大于垂直滑动距离,则通过scrollBy()方法使Item可跟随手指左右滑动,当然我们进行了滑动的边界检测,并不会出现滑动越界的情况哦!

最后看一下onTouchEvent的MotionEvent.ACTION_UP事件处理:

    public boolean onTouchEvent(MotionEvent e) {
        mVelocityTracker.addMovement(e);

        int x = (int) e.getX();
        int y = (int) e.getY();
        switch (e.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:
                if (!isItemMoving && !isDragging && mListener != null) {
                    mListener.onItemClick(mItemLayout, mPosition);
                }
                isItemMoving = false;

                mVelocityTracker.computeCurrentVelocity(1000);//计算手指滑动的速度
                float xVelocity = mVelocityTracker.getXVelocity();//水平方向速度(向左为负)
                float yVelocity = mVelocityTracker.getYVelocity();//垂直方向速度

                int deltaX = 0;
                int upScrollX = mItemLayout.getScrollX();

                if (Math.abs(xVelocity) > 100 && Math.abs(xVelocity) > Math.abs(yVelocity)) {
                    if (xVelocity <= -100) {//左滑速度大于100,则删除按钮显示
                        deltaX = mMaxLength - upScrollX;
                        mDeleteBtnState = 2;
                    } else if (xVelocity > 100) {//右滑速度大于100,则删除按钮隐藏
                        deltaX = -upScrollX;
                        mDeleteBtnState = 1;
                    }
                } else {
                    if (upScrollX >= mMaxLength / 2) {//item的左滑动距离大于删除按钮宽度的一半,则则显示删除按钮
                        deltaX = mMaxLength - upScrollX;
                        mDeleteBtnState = 2;
                    } else if (upScrollX < mMaxLength / 2) {//否则隐藏
                        deltaX = -upScrollX;
                        mDeleteBtnState = 1;
                    }
                }

                //item自动滑动到指定位置
                mScroller.startScroll(upScrollX, 0, deltaX, 0, 200);
                isStartScroll = true;
                invalidate();

                mVelocityTracker.clear();
                break;
        }

        mLastX = x;
        mLastY = y;
        return super.onTouchEvent(e);
    }

当手指抬起时,如果之前没有发生Item水平滑动、上下滑动列表、回调接口不为空,则认为是Item的点击事件,执行回调接口里的方法mListener.onItemClick(mItemLayout, mPosition);。接下来计算出手指在水平以及垂直方向的滑动速度**xVelocity 、yVelocity ,如果if (Math.abs(xVelocity) > 100 && Math.abs(xVelocity) > Math.abs(yVelocity)),则根据速度判断手指抬起后Item的滑动情况,if (xVelocity <= -100)代表左滑速度大于等于100,则将mDeleteBtnState值改为2,代表删除按钮将要打开(展示),同理如果右滑速度大于100则删除按钮将要关闭(隐藏),同时计算出相应的滑动距离deltaX **。如果不满足通过速度的判断条件则根据Item的滑动距离来判断,如果if (upScrollX >= mMaxLength / 2),即Item左滑的距离大于等于删除按钮宽度的一半,则将mDeleteBtnState值改为2,否则将mDeleteBtnState值改为1,同时不要忘了计算deltaX的值。最后通过mScroller.startScroll(upScrollX, 0, deltaX, 0, 200);使Item滑动到指定位置。

到这里我们的onTouchEvent()实现原理就分析完了。

再贴一下computeScroll()的代码:

public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            mItemLayout.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            invalidate();
        } else if (isStartScroll) {
            isStartScroll = false;
            if (mDeleteBtnState == 1) {
                mDeleteBtnState = 0;
            }
            if (mDeleteBtnState == 2) {
                mDeleteBtnState = 3;
            }
        }
    }

其中isStartScroll代表手指抬起后Item自动滑动的状态,在MotionEvent.ACTION_DOWN我们将其赋值为true,代表开始自动滑动,如果自动滑动结束则会执行else if中的逻辑,重置isStartScroll、修改mDeleteBtnState最终的状态值(打开或者关闭)。

手指上下滑动列表时,我们通过onScrollStateChanged()方法,监听列表滑动的状态:

public void onScrollStateChanged(int state) {
        super.onScrollStateChanged(state);
        isDragging = state == SCROLL_STATE_DRAGGING;
    }

以判断是否正在上下滑动列表。

到此,我们把大致的实现方法就分析完了,如有不合理的地方欢迎指出!!!如有兴趣可下载源码看看:点我下载哦

上一篇下一篇

猜你喜欢

热点阅读