Android开发之滑动选择菜单(仿QQ滑动删除)

前言:关于滑动删除的内容相信大家都在QQ上使用过,滑动删除的出现主要替换了长按item弹出的菜单,这两个方法各有优点,QQ使用的是滑动删除,则微信使用的是长按item选项,各位看官自己酌情使用,实现起来这个效果方法也很多,可以借助Scroller帮助类,也可以使用ViewDragHelper类,本篇博客讲述借助ViewDragHelper类来实现滑动选择菜单。

-----------------------分割线----------------------

ViewDragHelper类是滑动帮助类,v4包下的类,难点是理解ViewDragHelper.CallBack的相关方法,(Hongyang讲解的《Android ViewDragHelper完全解析 自定义ViewGroup神器》大家可以参考下)下面是我总结的介绍CallBack里面的方法:

一:创建实例:

viewDragHelper = ViewDragHelper.create(this, callback);

二:触摸相关方法:
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return viewDragHelper.shouldInterceptTouchEvent(ev);
	}
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		viewDragHelper.processTouchEvent(event);
		return true;
	}

三:CallBack里面的方法

1.public boolean tryCaptureView(View child, int pointerId):用于判断是否捕获当前child的触摸事件,child: 当前触摸的子View , return: true:就捕获并解析 false:不处理。

2.public int getViewHorizontalDragRange(View child):获取view水平方向的拖拽范围,但是目前不能限制边界,返回的值目前用在手指抬起的时候view缓慢移动的动画世界的计算上面,切记最好不要返回0。

3.public int clampViewPositionHorizontal(View child, int left, int dx):left表示控制child在水平方向的移动,dx表示ViewDragHelper认为你想让当前child的left改变的值,return 表示你真正想让child的left变成的值

4.public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy):top表示控制child在垂直方向的移动,dx表示ViewDragHelper认为你想让当前child的top改变的值

5.public void onViewReleased(View releasedChild, float xvel, float yvel):当child的位置改变的时候执行,一般用来做其他子View的伴随移动

------------------------分割线--------------------------

实现滑动选择菜单逻辑:

1.自定义一个可以滑动的布局(这个我们可以借助ViewDragHelper)。

2.将该布局放入adapter的布局中,需要处理滑动冲突。

----------------------------分割线------------------------

ok,我们先来实现滑动的布局。

1.首先初始化ViewDragHelper类,这点毋容置疑。

2.先在onFinishInflate里面获取contentView和deleteView。

 @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        contentView = getChildAt(0);
        deleteView = getChildAt(1);
    }

3.然后在onSizeChanged里面获取宽高属性。(在这里强调一点由于我们继承的是FrameLayout所以不需要重写onMeasure方法来获取宽高)

  @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        deleteHeight = deleteView.getMeasuredHeight();
        deleteWidth = deleteView.getMeasuredWidth();
        contentWidth = contentView.getMeasuredWidth();
    }

4.在onLayout里面摆放deleteView和contentView的位置。

@Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        // super.onLayout(changed, left, top, right, bottom);
        contentView.layout(0, 0, contentWidth, deleteHeight);
        deleteView.layout(contentView.getRight(), 0, contentView.getRight()
                + deleteWidth, deleteHeight);
    }

5.然后在重写触摸相关的方法。

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);

        //如果当前有打开的,则需要直接拦截,交给onTouch处理
        if (!SwipeLayoutManager.getInstance().isShouldSwipe(this)) {
            //先关闭已经打开的                                                                                                                                        layoutSwipeLayoutManager.getInstance().closeCurrentLayout();
            result = true;
        }

        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //如果当前有打开的,则下面的逻辑不能执行
        if (!SwipeLayoutManager.getInstance().isShouldSwipe(this)) {
            requestDisallowInterceptTouchEvent(true);
            return true;
        }

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                //1.获取x和y方向移动的距离
                float moveX = event.getX();
                float moveY = event.getY();
                float delatX = moveX - downX;//x方向移动的距离
                float delatY = moveY - downY;//y方向移动的距离
                if (Math.abs(delatX) > Math.abs(delatY)) {
                    //表示移动是偏向于水平方向,那么应该SwipeLayout应该处理,请求listview不要拦截
                    requestDisallowInterceptTouchEvent(true);
                }
                //更新downX,downY
                downX = moveX;
                downY = moveY;
                break;
            case MotionEvent.ACTION_UP:

                break;
        }
        viewDragHelper.processTouchEvent(event);
        return true;
    }
6.实现CallBack里面的方法:
private ViewDragHelper.Callback callback = new Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == contentView || child == deleteView;
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return deleteWidth;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (child == contentView) {
                if (left > 0) left = 0;
                if (left < -deleteWidth) left = -deleteWidth;
            } else if (child == deleteView) {
                if (left > contentWidth) left = contentWidth;
                if (left < (contentWidth - deleteWidth)) left = contentWidth - deleteWidth;
            }
            return left;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top,
                                          int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            if (changedView == contentView) {
                //手动移动deleteView
                deleteView.layout(deleteView.getLeft() + dx, deleteView.getTop() + dy,
                        deleteView.getRight() + dx, deleteView.getBottom() + dy);
            } else if (deleteView == changedView) {
                //手动移动contentView
                contentView.layout(contentView.getLeft() + dx, contentView.getTop() + dy,
                        contentView.getRight() + dx, contentView.getBottom() + dy);
            }

            //判断开和关闭的逻辑
            if (contentView.getLeft() == 0 && currentState != SwipeState.Close) {
                //说明应该将state更改为关闭
                currentState = SwipeState.Close;

                //回调接口关闭的方法
                if (listener != null) {
                    listener.onClose(getTag());
                }

                //说明当前的SwipeLayout已经关闭,需要让Manager清空一下
                SwipeLayoutManager.getInstance().clearCurrentLayout();
            } else if (contentView.getLeft() == -deleteWidth && currentState != SwipeState.Open) {
                //说明应该将state更改为开
                currentState = SwipeState.Open;

                //回调接口打开的方法
                if (listener != null) {
                    listener.onOpen(getTag());
                }
                //当前的Swipelayout已经打开,需要让Manager记录一下下
                SwipeLayoutManager.getInstance().setSwipeLayout(SwipeLayout.this);
            }
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (contentView.getLeft() < -deleteWidth / 2) {
                //应该打开
                open();
            } else {
                //应该关闭
                close();
            }
        }
    };

    /**
     * 打开的方法
     */
    public void open() {
        viewDragHelper.smoothSlideViewTo(contentView, -deleteWidth, contentView.getTop());
        ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
    }

    /**
     * 关闭的方法
     */
    public void close() {
        viewDragHelper.smoothSlideViewTo(contentView, 0, contentView.getTop());
        ViewCompat.postInvalidateOnAnimation(SwipeLayout.this);
    }

    ;

    public void computeScroll() {
        if (viewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

-----------------------分割线---------------------

完成以上代码可以滑动View,在listView中可以滑动多个item,这个肯定不是我们希望看到的,所以我们需要管理一下这个SwipeLayout。

逻辑:1.记录打开的SwipeLayout

2.当钱打开的SwipeLayout关闭的时候,需要清空记录。

3.判断当前是否应该能够滑动,如果没有打开的,则可以滑动,如果有打开的,则判断打开的layout是否和当前按下的layout是否是同一个。

以下是管理类的完整代码:

public class SwipeLayoutManager {

	private SwipeLayoutManager(){}
	private static SwipeLayoutManager mInstance = new SwipeLayoutManager();
	
	public static SwipeLayoutManager getInstance(){
		return mInstance;
	}
	
	private SwipeLayout currentLayout;//用来记录当前打开的SwipeLayout
	public void setSwipeLayout(SwipeLayout layout){
		this.currentLayout = layout;
	}
	
	/**
	 * 清空当前所记录的已经打开的layout
	 */
	public void clearCurrentLayout(){
		currentLayout = null;
	}
	
	/**
	 * 关闭当前已经打开的SwipeLayout
	 */
	public void closeCurrentLayout(){
		if(currentLayout!=null){
			currentLayout.close();
		}
	}
	
	/**
	 * 判断当前是否应该能够滑动,如果没有打开的,则可以滑动。
	 * 如果有打开的,则判断打开的layout和当前按下的layout是否是同一个
	 * 
	 * @return
	 */
	public boolean isShouldSwipe(SwipeLayout swipeLayout){
		if(currentLayout==null){
			//说明当前木有打开的layout
			return true;
		}else {
			//说明有打开的layout
			return currentLayout==swipeLayout;
		}
	}
}

---------------------------完整代码下载-----------------------------------

下载:Android开发之滑动选择菜单(仿QQ滑动删除)代码

-------------------欢迎关注我的博客,并给我积极留言--------------

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

等待着冬天的风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值