ViewDragHelper的使用

        ViewDragHelper是framework中不为人知却非常有用的一个工具。SlidingPaneLayout和DrawerLayout都是通过他来实现拖动的过程的,他里面有对TouchEvent时间进行判断。相比之前的gesturedetector手势操作类,他的功能更加强大。用于实现某个viewgroup中的某个控件的拖动过程。

使用方法:

1.初始化ViewDragHelper

public class MyDragLayout extends LinearLayout {
	private ViewDragHelper mDragHelper;
	private View mDragView;

	public MyDragLayout(Context context) {
		super(context);
		mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
	}

	public MyDragLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
	}

	public MyDragLayout(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
	}

	public MyDragLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
		super(context, attrs, defStyleAttr, defStyleRes);
		mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
	}
mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());该方法中传入的1.0,是为了在ViewDragHelper中判断helper.mTouchSlop = (int) (helper.mTouchSlop * (1 / sensitivity));mTouchSlop就是可以拖动的最小距离,只有这个距离大于这个值才可以拖动某个控件。


2.重写onInterceptTouchEvent方法

@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final int action = MotionEventCompat.getActionMasked(ev);
		if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
			mDragHelper.cancel();
			return false;
		}
		return mDragHelper.shouldInterceptTouchEvent(ev);
	}
如果是ACTION_CANCEL或者ACTION_UP事件就让事件传递到viewgroup内部去处理,否则就通过在ViewDragHelper中通过mDragHelper.shouldInterceptTouchEvent(ev)去判断事件是否需要处理,如果事件确定拦截了,就会调用当前viewGroup的onTouchEvent事件,如下:


3.重写onTouchEvent方法

@Override
	public boolean onTouchEvent(MotionEvent ev) {
		mDragHelper.processTouchEvent(ev);
		return true;
	}
通过ViewDragHelper里面的processTouchEvent来分析拖动的各种事件,然后该方法中会回调ViewDragHelper.Callback中的各个方法,如下:


3.ViewDragHelper.Callback

class DragHelperCallback extends ViewDragHelper.Callback {
		/**
		 * 用来判断拖动的是哪个view
		 */
		@Override
		public boolean tryCaptureView(View arg0, int arg1) {
			return mDragView == arg0;
		}

		/**
		 * 在shouldInterceptTouchEvent中的ACTION_DOWN和ACTION_POINTER_DOWN判断中会调用该方法,说明正在触摸边缘
		 */
		@Override
		public void onEdgeTouched(int edgeFlags, int pointerId) {
			super.onEdgeTouched(edgeFlags, pointerId);
			Toast.makeText(getContext(), "edgeTouched", Toast.LENGTH_SHORT).show();
		}

		/**
		 * shouldInterceptTouchEvent中的ACTION_MOVE中调用
		 */
		@Override
		public void onEdgeDragStarted(int edgeFlags, int pointerId) {
			super.onEdgeDragStarted(edgeFlags, pointerId);
			Toast.makeText(getContext(), "onEdgeDragStarted", Toast.LENGTH_SHORT).show();
		}

		/**
		 * 返回将要移动到新的位置,processTouchEvent中的ACTION_MOVE中调用,返回的就是子view新的位置, 
		 * 通过子view和之前view的位置的距离差 通过 mCapturedView.offsetLeftAndRight(clampedX - oldLeft);来实现子view的拖动
		 */
		@Override
		public int clampViewPositionHorizontal(View child, int left, int dx) {
			// left为当前拖动的view距离父布局左边的距离,dx为移动的距离,之前的left加上现在移动的dx,等于现在的left
			//一般来说left就是等于newleft的值,就是当前手指拖动后的位置,但是为了防止view被脱出屏幕之外,这个时候left的值可能会是负数,所以取
                        //left和leftBound的最大值来防止view被拉出左边界时任然停留在初始位置,同时应该防止被拉出右边边界,所以leftBound和rightBound都要计算
                        //排除padding的值
                        Log.d("DragLayout", "clampViewPositionHorizontal " + left + "," + dx);
			final int leftBound = getPaddingLeft();
			final int rightBound = getWidth() - mDragView.getWidth() - getPaddingRight();
			final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
			return newLeft;
		}

		/**
		 * 返回将要移动到新的位置,processTouchEvent中的ACTION_MOVE中调用,返回的就是子view新的位置,
		 *  通过子view和之前view的位置的距离差 通过 mCapturedView.offsetLeftAndRight(clampedX - oldLeft);来实现子view的拖动
		 */
		@Override
		public int clampViewPositionVertical(View child, int top, int dy) {
			Log.d("DragLayout", "clampViewPositionVertical " + top + "," + dy);
                        //在最上面的top值
                        final int topBound = getPaddingTop();
                        //在最底部时候的top值
                        final int bottomBound = getHeight() - mDragView.getHeight()-getPaddingBottom();
			//移动的时候只能在这两个值之间移动,避免移出这两个值设置的边界
                        final int newTop = Math.min(Math.max(top, topBound), bottomBound);
			return newTop;
		}

		/**
		 * 会在拖动的过程中调用,在clampViewPositionHorizontal之后调用,如果view有移动就会调用,
		  * 推动的过程中调用,在view被释放后利用continueSettling来继续移动的过程中也会移动,是通过offsetLeftAndRight来移动的,
		 */
		@Override
		public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
			super.onViewPositionChanged(changedView, left, top, dx, dy);
			// 和clampViewPositionHorizontal中的值一样,这里可以做一些动画效果
			Log.d("DragLayout", "onViewPositionChanged " + left + "," + dx);
		}

		@Override
		public void onViewCaptured(View capturedChild, int activePointerId) {
			super.onViewCaptured(capturedChild, activePointerId);
			Log.d("DragLayout", "onViewCaptured ");
		}
        /**
         * 刚开始拖动,状态为1,释放后先调用onViewReleased,由于在onViewReleased中会调用settleCapturedViewAt或者smoothSlideViewTo来使view沉淀下来,就会调用forceSettleCapturedViewAt
         * 然后在forceSettleCapturedViewAt中会将状态置位沉淀的状态2,并调用onViewDragStateChanged会回掉,接着就是不停用continueSettling判断并持续调用onViewPositionChanged,当停止移动的 
         * 时候将状态置为0,并用onViewDragStateChanged回调
         */
		@Override
		public void onViewDragStateChanged(int state) {
			super.onViewDragStateChanged(state);
			Log.d("DragLayout", "onViewDragStateChanged " + state);
		}

		/**
		 * 拖出边界,或者主动释放的时候调用,该方法中需要调用settleCapturedViewAt或者smoothSlideViewTo,
		 * 在这两个方法中会启动 mScroller.startScroll计算,然后需要调用invalidate(),这样就会调用到computeScroll,
		 * 然后在continueSettling实现 view的位移,并持续调用onViewPositionChanged
		 */
		@Override
		public void onViewReleased(View releasedChild, float xvel, float yvel) {
			Log.d("DragLayout", "onViewReleased ");
			// mDragHelper.settleCapturedViewAt(20, 20);
                       //如果受释放的时候不需要view回到原来的位置,可以不设置以下两行代码
                         mDragHelper.smoothSlideViewTo(mDragView, 20, 20);
			invalidate();
		}

		/**
		 * 似乎只要不为0就可以拖动
		 */
		@Override
		public int getViewHorizontalDragRange(View child) {
			return 10;
		}

		/**
		 * 似乎只要不为0就可以拖动
		 */
		@Override
		public int getViewVerticalDragRange(View child) {
			return 10;
		}
	}

 

4.需要实现computeScroll方法

@Override
	public void computeScroll() {
		if (mDragHelper.continueSettling(true)) {
			Log.d("DragLayout", "computeScroll ");
			ViewCompat.postInvalidateOnAnimation(this);
		}
	}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值