ViewDragHelper——打造你的专属ViewGroup

ViewDragHelper介绍

在自定义控件中,很多效果都需要重写onInterceptTouchEvent和onTouchEvent这两个类,但要考虑到各种情况,写出很好的手势处理控件是比较麻烦的。比如多点触控,速度监测等。ViewDragHelper是谷歌io大会上隆重推出的手势处理的类。在android.support.v4中。像Google官方侧滑菜单,DrawerLayout就是用这个类实现的。本节先介绍它的基本用法,以后会介绍如何用ViewDragHelper自定义侧滑菜单。

基本实现

创建实例

mViewDragHelper = ViewDragHelper.create(this,1.0f,callback);

需要传入三个参数,第一个参数传入当前的ViewGroup;第二个参数是灵敏度,数值越大,灵敏度越低;第三个参数传入ViewDragHelper.Callback,后面详细介绍。

触摸相关的方法

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 让ViewDragHelper帮我们判断是否应该拦截
        boolean result = viewDragHelper.shouldInterceptTouchEvent(ev);
        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // 将触摸事件交给ViewDragHelper来解析处理
        viewDragHelper.processTouchEvent(event);
        return true;
    }

ViewDragHelper.Callback的相关方法

callback的方法,大家参考注释。


private ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        /** * 用于判断是否捕获当前child的触摸事件 child: 当前触摸的子View return: true:就捕获并解析 false:不处理 */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == blueView || child == redView;
        }

        /** * 当view被开始捕获和解析的回调 capturedChild:当前被捕获的子view */
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
            // Log.e("tag", "onViewCaptured");
        }

        /** * 获取view水平方向的拖拽范围,但是目前不能限制边界,返回的值目前用在手指抬起的时候view缓慢移动的动画世界的计算上面; 最好不要返回0 */
        @Override
        public int getViewHorizontalDragRange(View child) {
            return getMeasuredWidth() - child.getMeasuredWidth();
        }

        /** * 获取view垂直方向的拖拽范围,最好不要返回0 */
        public int getViewVerticalDragRange(View child) {
            return getMeasuredHeight() - child.getMeasuredHeight();
        }

        /** * 控制child在水平方向的移动 left: * 表示ViewDragHelper认为你想让当前child的left改变的值,left=chile.getLeft()+dx dx: * 本次child水平方向移动的距离 return: 表示你真正想让child的left变成的值 */
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            if (left < 0) {
                // 限制左边界
                left = 0;
            } else if (left > (getMeasuredWidth() - child.getMeasuredWidth())) {
                // 限制右边界
                left = getMeasuredWidth() - child.getMeasuredWidth();
            }
            return left;
        }

        /** * 控制child在垂直方向的移动 top: * 表示ViewDragHelper认为你想让当前child的top改变的值,top=chile.getTop()+dy dy: * 本次child垂直方向移动的距离 return: 表示你真正想让child的top变成的值 */
        public int clampViewPositionVertical(View child, int top, int dy) {
            if (top < 0) {
                top = 0;
            } else if (top > getMeasuredHeight() - child.getMeasuredHeight()) {
                top = getMeasuredHeight() - child.getMeasuredHeight();
            }
            return top;
        };

        /** * 当child的位置改变的时候执行,一般用来做其他子View的伴随移动 changedView:位置改变的child * left:child当前最新的left top: child当前最新的top dx: 本次水平移动的距离 dy: 本次垂直移动的距离 */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top,
                int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            if (changedView == blueView) {
                // blueView移动的时候需要让redView跟随移动
                redView.layout(redView.getLeft() + dx, redView.getTop() + dy,
                        redView.getRight() + dx, redView.getBottom() + dy);
            } else if (changedView == redView) {
                // redView移动的时候需要让blueView跟随移动
                blueView.layout(blueView.getLeft() + dx,
                        blueView.getTop() + dy, blueView.getRight() + dx,
                        blueView.getBottom() + dy);
            }

            //1.计算view移动的百分比
            float fraction = changedView.getLeft()*1f/(getMeasuredWidth()-changedView.getMeasuredWidth());
            Log.e("tag", "fraction:"+fraction);
            //2.执行一系列的伴随动画
            executeAnim(fraction);
        }

        /** * 手指抬起的执行该方法, releasedChild:当前抬起的view xvel: x方向的移动的速度 正:向右移动, 负:向左移动 * yvel: y方向移动的速度 */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            int centerLeft = getMeasuredWidth() / 2
                    - releasedChild.getMeasuredWidth() / 2;
            if (releasedChild.getLeft() < centerLeft) {
                // 在左半边,应该向左缓慢移动
                viewDragHelper.smoothSlideViewTo(releasedChild, 0,
                        releasedChild.getTop());
                ViewCompat.postInvalidateOnAnimation(DragLayout.this);
            } else {
                // 在右半边,应该向右缓慢移动
                viewDragHelper.smoothSlideViewTo(releasedChild,
                        getMeasuredWidth() - releasedChild.getMeasuredWidth(),
                        releasedChild.getTop());
                ViewCompat.postInvalidateOnAnimation(DragLayout.this);
            }
        }
    };

/** * 执行伴随动画 * @param fraction */
    private void executeAnim(float fraction){
        //fraction: 0 - 1
        //缩放
// ViewHelper.setScaleX(redView, 1+0.5f*fraction);
// ViewHelper.setScaleY(redView, 1+0.5f*fraction);
        //旋转
// ViewHelper.setRotation(redView,360*fraction);//围绕z轴转
        ViewHelper.setRotationX(redView,360*fraction);//围绕x轴转
// ViewHelper.setRotationY(redView,360*fraction);//围绕y轴转
        ViewHelper.setRotationX(blueView,360*fraction);//围绕z轴转
        //平移
// ViewHelper.setTranslationX(redView,80*fraction);
        //透明
// ViewHelper.setAlpha(redView, 1-fraction);

        //设置过度颜色的渐变
        redView.setBackgroundColor((Integer) ColorUtil.evaluateColor(fraction,Color.RED,Color.GREEN));
// setBackgroundColor((Integer) ColorUtil.evaluateColor(fraction,Color.RED,Color.GREEN));
    }

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

其他代码


    /** * 当DragLayout的xml布局的结束标签被读取完成会执行该方法,此时会知道自己有几个子View了 一般用来初始化子View的引用 */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        redView = getChildAt(0);
        blueView = getChildAt(1);
    }

     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     //要测量我自己的子View
     // int size = getResources().getDimension(R.dimen.width);//100dp
     // int measureSpec =
     MeasureSpec.makeMeasureSpec(redView.getLayoutParams().width,MeasureSpec.EXACTLY);
     // redView.measure(measureSpec,measureSpec);
     // blueView.measure(measureSpec, measureSpec);

     //如果说没有特殊的对子View的测量需求,可以用如下方法
     measureChild(redView, widthMeasureSpec, heightMeasureSpec);
     measureChild(blueView, widthMeasureSpec, heightMeasureSpec);
     }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = getPaddingLeft();
        int top = getPaddingTop();
        redView.layout(left, top, left + redView.getMeasuredWidth(), top
                + redView.getMeasuredHeight());
        blueView.layout(left, redView.getBottom(),
                left + blueView.getMeasuredWidth(), redView.getBottom()
                        + blueView.getMeasuredHeight());
    }

布局文件


<xyz.ibat.circleview.test.DragLayout  android:layout_width="match_parent" android:layout_height="match_parent" >

        <TextView  android:layout_width="@dimen/width" android:layout_height="@dimen/width" android:background="#ff0000" />
        <TextView  android:layout_width="@dimen/width" android:layout_height="@dimen/width" android:background="#0000ff" />

    xyz.ibat.circleview.test.DragLayout>

OK~~源码里注释写的很详细,就不过多描述了。开始打造属于你的ViewGroup吧!

你可能感兴趣的:(Android)