ViewDragHelper基本用法总结

来源:互联网 发布:数据分析师薪酬待遇 编辑:程序博客网 时间:2024/05/23 13:20

概述

ViewDragHelperAndroid中专为手势处理的类,用在自定义ViewGroup中将大大简化我们的工作.
其实在Android中还有一个类GestureDetector也是相同的作用.

相关概念

  • ViewDragHelper.Callback是一个回调处理类,是连接ViewDragHelperViewGroup的桥梁
  • ViewDragHelper的本质其实是分析onInterceptTouchEventonTouchEventMotionEvent参数,之后通过offsetTopAndBottom(int offset)offsetLeftAndRight(int offset)方法来改变View的位置.

一. create

public static ViewDragHelper create(ViewGroup forParent, float sensitivity, Callback cb)

参数说明:

  • forParent: 当前的自定义ViewGroup
  • sensitivity: 敏感的参数,设置值越大,touchSlop值就越小.
    作用位置:
helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));
  • cb: 各种事件回调,可以在里面处理子View的各种事件

二. 事件拦截

ViewDragHelper既然将我们的事件接管了,在自定义ViewGroup的时候,需要复写事件处理的方法.

    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {    //将拦截事件交个mDragHelper来处理,看是否需要拦截        return mDragHelper.shouldInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {    //通过mDragHelper来分析事件,返回true将 down 后的一些列事件都交个helper来处理        mDragHelper.processTouchEvent(event);        return true;    }

三. View的滑动

//是否拦截相关`View`事件,其中的 `child`参数就是发生事件的`View`@Override public boolean tryCaptureView(View child, int pointerId) {        return true;      }// 水平方向的滑动,left表示x轴坐标@Override public int clampViewPositionHorizontal(View child, int left, int dx) {//这里过滤掉边界          int leftBound = getPaddingLeft();          int rightBound = getWidth() - child.getWidth() - leftBound;          left = Math.min(Math.max(left, leftBound), rightBound);        return left;      }// 垂直方向上的滑动,top表示y轴坐标      @Override public int clampViewPositionVertical(View child, int top, int dy) {// 过滤掉边界          int topBound = getPaddingTop();          int bottomBound = getHeight() - child.getHeight() - topBound;          top = Math.min(Math.max(top, topBound), bottomBound);        return top;      }

通过重写上面的CallBack方法,我们自定义的ViewGroup内的子View就可以在布局内来回拖动了.

四.拖动方向(setEdgeTrackingEnabled)

如果我们自定义一个侧边栏.那么这时候就需要可以从左边或者右边才能拉出,可以通过如下API

/**     * Enable edge tracking for the selected edges of the parent view.     * The callback's {@link Callback#onEdgeTouched(int, int)} and     * {@link Callback#onEdgeDragStarted(int, int)} methods will only be invoked     * for edges for which edge tracking has been enabled.     *     * @param edgeFlags Combination of edge flags describing the edges to watch     * @see #EDGE_LEFT     * @see #EDGE_TOP     * @see #EDGE_RIGHT     * @see #EDGE_BOTTOM     */    public void setEdgeTrackingEnabled(int edgeFlags) {        mTrackingEdges = edgeFlags;    }

这是ViewDrugHelper的方法,可以看出其是用一个常量来表示拉动的方向.
通常情况下,我们在设置了如上的值后,还需要复写CallBack的相关方法才能生效.

//边界拖动时回调,这里先判断拖动的是否是左边界,之后拦截相关View的事件@Override public void onEdgeDragStarted(int edgeFlags, int pointerId) {     if(edgeFlags==ViewDragHelper.EDGE_LEFT){         mDragHelper.captureChildView(mDragView, pointerId);     } }

可以看到,这里并不需要再CallBacktryCaptureView中拦截相关View的事件.

五.释放回调(onViewReleased)

@Override public void onViewReleased(View releasedChild, float xvel, float yvel) {     //mAutoBackView手指释放时当前child回到什么地方去     if (releasedChild == mDragView) {         mDragHelper.settleCapturedViewAt( finalLeft,  finalTop);         invalidate();     } }

如果是侧滑菜单,一般当我们拉出超过一半松手会全部拉出,否则就会回去.就可以在这里实现.

在这里内部其实是通过Scroller实现的,因此我们还需要复写一些computeScroll方法

 @Override    public void computeScroll() {    //判断当前截获的子view滑动是否完成        if (mDragHelper.continueSettling(true)) {            invalidate();        }    }

六.移动范围

@Overridepublic int getViewHorizontalDragRange(View child){//横向的移动范围     return getMeasuredWidth()-child.getMeasuredWidth();}@Overridepublic int getViewVerticalDragRange(View child){//纵向的移动范围     return getMeasuredHeight()-child.getMeasuredHeight();}

如果子View是可以消耗事件的,则需要复写上面的方法,因为在shouldInterceptTouchEvent方法的MOVE中会通过此判断后才拦截tryCaptureView

    public boolean shouldInterceptTouchEvent(MotionEvent ev) {        //...        switch (action) {            //...            case MotionEvent.ACTION_MOVE: {            //...                for (int i = 0; i < pointerCount; i++) {                //...                    if (pastSlop) {                        //...                        final int horizontalDragRange = mCallback.getViewHorizontalDragRange(                                toCapture);                        final int verticalDragRange = mCallback.getViewVerticalDragRange(toCapture);                        if ((horizontalDragRange == 0 || horizontalDragRange > 0                                && newLeft == oldLeft) && (verticalDragRange == 0                                || verticalDragRange > 0 && newTop == oldTop)) {                            break;                        }                    }                    //...                }                saveLastMotion(ev);                break;            }//...        }

从上面源码中可以看出,如果滑动范围为0,则直接跳出循环.

七. 其他API

  • onViewDragStateChanged: 当状态发生变化时回调[IDLE,DRAGGING,SETTING]
  • onViewPositionChanged : 当captureview的位置发生改变时回调
  • onViewCaptured : 当captureview被捕获时回调
  • onEdgeTouched : 当触摸到边界时回调
  • onEdgeLock : true的时候会锁住当前的边界,falseunLock
  • getOrderedChildIndex : 改变同一个坐标(x,y)去寻找captureView位置的方法.

参考:Android ViewDragHelper完全解析 自定义ViewGroup神器
扩展阅读:
Android ViewDragHelper实例

0 0
原创粉丝点击