Android 事件分发实例之右滑结束Activity(二)

前言

本篇同上一篇实现同样的功能,此篇采用之前音乐锁屏壁纸的功能里面使用的ViewDragHelper来实现,最终的效果与上篇效果相同,因此本篇只挑新内容讲解。

具体步骤

事件分发与冲突解决

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean canScrollHorizontally = canScrollHorizontally(-1, this);
        if (!canScrollHorizontally) {
            return mViewDragHelper.shouldInterceptTouchEvent(ev);
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

说明:上一篇已经说清一个事件里面包含三个小事件,因为此篇主要是把事件交由ViewDragHelper处理,因此处理canScrollHorizontally()方法时,判断条件不能只放在Move事件中处理,否则Down、Up的事件ViewDragHelper接收不到,查看源码知道mViewDragHelper.shouldInterceptTouchEvent(ev)和mViewDragHelper.processTouchEvent(event)方法必须要三个事件同时存在。

滑动处理

通过ViewDragHelper实现滑动效果,主要是通过实现ViewDragHelper的回调ViewDragHelper.Callback实现滑动效果。

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return false;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //当前回调,松开手时触发,比较触发条件和当前的滑动距离
            int left = releasedChild.getLeft();
            if (left <= mMaxSlideWidth) {
                //缓慢滑动的方法,小于触发条件,滚回去
                mViewDragHelper.settleCapturedViewAt(0, 0);
            } else {
                //大于触发条件,滚出去...
                mViewDragHelper.settleCapturedViewAt(mScreenWidth, 0);
            }

            //需要手动调用更新界面的方法
            invalidate();
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            mScrollPercent = Math.abs((float) left
                    / (mScreenWidth + mEdgeShadow.getIntrinsicWidth()));
            //重绘
            invalidate();

            //当滚动位置到达屏幕最右边,则关掉Activity
            if (changedView == mRootView && left >= mScreenWidth) {
                mActivity.finish();
            }
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return ViewDragHelper.EDGE_LEFT;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //限制左右拖拽的位移
            left = left >= 0 ? left : 0;
            return left;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            //上下不能移动,返回0
            return 0;
        }

        @Override
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {
            //触发边缘时,主动捕捉mRootView
            mViewDragHelper.captureChildView(mRootView, pointerId);
        }

说明:1、tryCaptureView():由用户决定捕获哪个子View的行为,由于我们是移动整个ViewGroup,直接返回false。
2、onViewReleased():当视图拖拽完成时回调,主要处理松手之后视图最终位置,mViewDragHelper.settleCapturedViewAt(0, 0)方法内部也是通过调用Scroller来实现移动
3、onViewPositionChanged():看方法名称就知道View位置改变的时候回调,因此可以通过判断最后的位置是否超过右边缘来结束activity
4、getViewHorizontalDragRange():获取当前水平方向的范围,右滑,设置左边缘即可
5、clampViewPositionHorizontal():限制水平方向的位置,默认不限制,因此需要重写限制大于0,这样就可以实现右滑
6、clampViewPositionVertical():限制垂直方向上的位置,右滑可以不重写
7、onEdgeDragStarted():无子类捕获,父类从之前设置的边缘拖拽的时候回调,此时去捕获activity的视图

@Override
    public void computeScroll() {
        //使用settleCapturedViewAt方法是,必须重写computeScroll方法,传入true
        //持续滚动期间,不断刷新ViewGroup
        if (mViewDragHelper.continueSettling(true))
            ViewCompat.postInvalidateOnAnimation(this);
    }

说明:mViewDragHelper.continueSettling(true)方法在拖拽过程中通过返回值不断的回调,使拖拽过程更加平滑,与mViewDragHelper.settleCapturedViewAt(0, 0)呼应,主要还是利用Scroller的滑动机制。

触摸范围

完成上述步骤已经可以实现右滑,只是ViewDragHelper内部检测是否是边缘,然后才能去拖拽。ViewDragHelper源码默认的边缘是20dp,如果想实现全屏拖拽的效果必须修改此值,查看ViewDragHelper源码并没有可以修改边缘宽度的方法,因此采用反射来实现。

/**
     * 设置可以拖拽的触点范围
     *
     * @param touchRange
     */
    private void setTouchRange(int touchRange) {
        Class<?> aClass = mViewDragHelper.getClass();
        Field mDividerHeight = null;
        try {
            mDividerHeight = aClass.getDeclaredField("mEdgeSize");
            mDividerHeight.setAccessible(true);
            mDividerHeight.setInt(mViewDragHelper, touchRange);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

总结

本篇采用与上篇不同的方式,主要不同点是事件处理的时机问题和触摸范围的修改,还有ViewDragHelper的各个回调方法处理,其他方面没区别,现已完成测试,效果与上篇完全一致。现在还在做进一步的封装,后续会持续更新最新代码。
右滑结束Activity

推荐阅读更多精彩内容