ViewDragHelper让你处理View拖动时,代码减半!

出处:ViewDragHelper是V4包下的一个文件。

我们在自定义ViewGroup的时候,有时候觉得很头疼,其中很大一部分原因就是因为事件处理太麻烦,需要记录大量的成员变量,还有各种判断等等。
Google也感觉到了这个麻烦,所以ViewDragHelper就出现了,ViewDragHelper功能到底是什么呢?从字面意思上看是View拖拽的帮助类,简而言之就是,在简化View拖拽的时候的代码量。我们先来看一看到底这个类的帮助有多大?
先来看一个测拉菜单效果

先来分析一下,如果我们不借助这个帮助类实现情况:
1、重写一个RelativeLayout;
2、重写其中的onInterceptTouchEvent(做相应的事件拦截操作)
2、重写其中的onTouchEvent方法(这里面做大量的代码)
3、定义一个Scroller变量,用来控制手指松开以后的操作
这里我就不去写代码了,代码量肯定很大!
再来看看借助ViewDragHelper类实现的代码

/** * Created by gyzhong on 15/4/8. */
public class VdhLayout01 extends RelativeLayout {

    private ViewDragHelper mViewDragHelper;
    private View mCaptureView;
    private float mInitialMotionX;
    private float mInitialMotionY;
    private boolean mIsUnableToDrag;
    private int mSlideRange;
    private float mSlideOffset;

    public VdhLayout01(Context context) {
        this(context, null);
    }

    public VdhLayout01(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VdhLayout01(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        mViewDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCall());
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mCaptureView = findViewById(R.id.id_capture_view);
        TextView textView = (TextView) findViewById(R.id.id_text);
        textView.setText(Shakespeare.DIALOGUE[0]);
    }
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mCaptureView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                mCaptureView.getViewTreeObserver().removeOnPreDrawListener(this);
                mSlideRange = mCaptureView.getMeasuredWidth();
                return false;
            }
        });
    }

    private class DragHelperCall extends ViewDragHelper.Callback {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return child == mCaptureView;
        }
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            mSlideOffset = left * 1.0f / mSlideRange*2;
        }
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {

            return clamp(left, 0, mSlideRange / 2);
        }
        @Override
        public int getViewHorizontalDragRange(View child) {
            return mSlideRange/2;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            int finalLeft;
            if (xvel > 0 || xvel == 0 && mSlideOffset > .5f) {
                finalLeft = mSlideRange/2  ;
            }else {
                finalLeft = 0 ;
            }
            mViewDragHelper.settleCapturedViewAt( finalLeft, mCaptureView.getTop());
            invalidate();
        }
    }
    @Override
    public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)){
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    private int clamp(int value, int min, int max) {
        return Math.min(max, Math.max(min, value));
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mViewDragHelper.cancel();
            return false;
        }
        if (!isEnabled() || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
            mViewDragHelper.cancel();
            return super.onInterceptTouchEvent(ev);
        }
        int index = MotionEventCompat.getActionIndex(ev) ;
        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                final float x = ev.getX();
                final float y = ev.getY();
                mInitialMotionX = x;
                mInitialMotionY = y;
                mIsUnableToDrag = false;
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                final float x = ev.getX();
                final float y = ev.getY();
                final float adx = Math.abs(x - mInitialMotionX);
                final float ady = Math.abs(y - mInitialMotionY);
                int slop = mViewDragHelper.getTouchSlop();
                if (adx > slop && adx < ady) {
                    mIsUnableToDrag = true;
                    mViewDragHelper.cancel();
                    return false;
                }
                break;
            }
        }
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

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

可以看到这里我们只是处理了事件拦截的操作,因为这里涉及到了ScrollView,如果没有涉及到事件拦截的话,代码量更简单!而最最复杂的onTouchEvent方法,我们什么都没做,只是让他交给ViewDragHelper类去处理。
如果看了DrawerLayout和SlidingPaneLayout源码的朋友应该知道,这两个控件就使用了这个帮助类。
看了ViewDragHelper的使用效果,我们再来看看它的用法,

/** * ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number * of useful operations and state tracking for allowing a user to drag and reposition * views within their parent ViewGroup. */

上面那段话是ViewDragHelper类的一个说明。大致意思实说ViewDragHelper是自定义ViewGroup的一个工具类,在我们对子View拖拽或者复位的时候它提供了一系列有用的操作。
从这段话中我们可以知道一个信息,这个类大部分用于自定义ViewGroup中,当然像上面的例子,重写RelativeLayout其实也相当于自定义ViewGroup。
实例化ViewDragHelper,ViewDragHelper的构造方法私有化了,所以我们不能直接new,需要通过

public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb) {
        //...
        return helper;
}

public static ViewDragHelper create(ViewGroup forParent, Callback cb) {
        return new ViewDragHelper(forParent.getContext(), forParent, cb);
}

来实例化,这列有三个参数,分别代表什么意思呢?
ViewGroup forParent 就是我们自定义的ViewGroup
float sensitivity 是一个拖拽的灵敏度
Callback cb 是ViewDragHelper中定义的一个抽象类,需要我们在自定义的ViewGroup中重写它,而核心的操作也就在在各类中,了解了这个类中的方法,就基本掌握这个Helper的运用。

public static abstract class Callback {
        //但拖拽状态改变的时候会触发这个方法,比如:从一开始不能拖拽,到拖拽
        public void onViewDragStateChanged(int state) {}

        //关键方法:顾名思义,这里就是记录了一些值得变化,可用于我们处理其他的操作,比如,改变背景颜色
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {}

        //这个方法很少用到,意义不大,可忽略
        public void onViewCaptured(View capturedChild, int activePointerId) {}

        //关键方法,手指松开会触发这个方法,做复位操作就在此方法中实现
        public void onViewReleased(View releasedChild, float xvel, float yvel) {}

        //当边缘被触摸的时候调用
        public void onEdgeTouched(int edgeFlags, int pointerId) {}

        //边缘设置不可用,
        public boolean onEdgeLock(int edgeFlags) {
            return false;
        }

        //这个方法也很少用到
        public void onEdgeDragStarted(int edgeFlags, int pointerId) {}

        //给自定义的ViewGroup中的字View 从新排序
        public int getOrderedChildIndex(int index) {
            return index;
        }

        //关键方法:设置水平拖动的距离
        public int getViewHorizontalDragRange(View child) {
            return 0;
        }

        //关键方法:设置垂直拖动的距离 0 表示不可拖动
        public int getViewVerticalDragRange(View child) {
            return 0;
        }

        //关键方法:返回true表示可以拖动
        public abstract boolean tryCaptureView(View child, int pointerId);

        //关键方法:重新定位水平移动的位置,返回left表示不受限制
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return 0;
        }
        //关键方法:重新定位垂直移动的位置,返回top表示不受限制
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }
}

看了以上注释,再回过头来看上面的例子是不是觉得很简单。

总结:

1、ViewDragHelper的作用是一个简化View拖动的帮助类
2、ViewDragHelper大部分用在自定义ViewGroup中
3、ViewDragHelper的实例化通过

create(ViewGroup forParent, float sensitivity, Callback cb) 

方法创建
4、在自定义的ViewGroup中的onInterceptTouchEvent方法中别忘记调用ViewDragHelper中的shouldInterceptTouchEvent(ev),
同理onTouchEvent(MotionEvent event)中需要调用
ViewDragHelper.processTouchEvent(event);

源码下载

你可能感兴趣的:(拖拽,ViewDrag)