ViewDragHelper基本用法总结

概述

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();
        }
    }

六.移动范围

@Override
public int getViewHorizontalDragRange(View child)
{
//横向的移动范围
     return getMeasuredWidth()-child.getMeasuredWidth();
}

@Override
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ViewDragHelper是一个帮助我们实现View拖拽和滑动效果的工具类,使用它可以简单地实现一些常见的交互效果,例如拖拽、滑动、边缘拖拽等。以下是使用ViewDragHelper的一般步骤: 1. 创建ViewDragHelper对象 ``` ViewDragHelper mDragHelper = ViewDragHelper.create(parentView, 1.0f, new DragHelperCallback()); ``` 2. 编写DragHelperCallback类 ``` private class DragHelperCallback extends ViewDragHelper.Callback { // 重写tryCaptureView方法,判断是否捕获当前View @Override public boolean tryCaptureView(View child, int pointerId) { return true; } // 重写clampViewPositionHorizontal和clampViewPositionVertical方法,返回拖拽后View的位置 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - child.getWidth() - leftBound; final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); final int bottomBound = getHeight() - child.getHeight() - topBound; final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; } } ``` 3. 在View的onTouchEvent中处理事件 ``` @Override public boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true; } ``` 4. 在View的onDraw方法中绘制View ``` @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制View } ``` 以上是使用ViewDragHelper的一般步骤,具体使用还需要根据实际需求进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值