public class ViewDragTest extends LinearLayout { ViewDragHelper mViewDragHelper; @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //中间参数表示灵敏度,卡片比如滑动了多少像素才视为触发了滑动.值越大越灵敏. mViewDragHelper = ViewDragHelper.create(this, 1f, new DragCallback()); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //固定写法 int action = MotionEventCompat.getActionMasked(ev); if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { mViewDragHelper.cancel(); return false; } return mViewDragHelper.shouldInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { //固定写法 mViewDragHelper.processTouchEvent(event); return true; } @Override public void computeScroll() { //固定写法 //此方法用于自动滚动,比如自动回滚到默认位置. if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } }
2、ViewDragHelper.Callback
//这个类的手进使用实现回调方法,才是ViewDragHelper的重点 private class ViewDragCallback extends ViewDragHelper.Callback{ @Override public boolean tryCaptureView(View child, int pointerId) { //child 表示想要滑动的view //pointerId 表示触摸点的id, 比如多点按压的那个id //返回值表示,是否可以capture,也就是是否可以滑动.可以根据不同的child决定是否可以滑动 return true; } @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //child 表示当前正在移动的view //left 表示当前的view正要移动到左边距为left的地方 //dx 表示和上一次滑动的距离间隔 //返回值就是child要移动的目标位置.可以通过控制返回值,从而控制child只能在ViewGroup的范围中移动. return left; } @Override public int clampViewPositionVertical(View child, int top, int dy) { //child 表示当前正在移动的view //top 表示当前的view正要移动到上边距为top的地方 //dx 表示和上一次滑动的距离间隔 return top; } }
重写以上3个方法,可以正常工作了.子View就可以被任意拖动了;
3、控制child的阶之及拖移动范围在父view中
//控制child只能在ViewGroup的免费源码下载横向中移动 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { final int leftBound = getPaddingLeft(); final int rightBound = getWidth() - mDragView.getWidth(); final int newLeft = Math.min(Math.max(left, leftBound), rightBound); return newLeft; } //控制child只能在ViewGroup的纵向中移动 @Override public int clampViewPositionVertical(View child, int top, int dy) { final int topBound = getPaddingTop(); final int bottomBound = getHeight() - mDragView.getHeight(); final int newTop = Math.min(Math.max(top, topBound), bottomBound); return newTop; }
4、开启边界滑动
//开启4个边 mViewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL); //各个边 public static final int EDGE_LEFT = 1 << 0; public static final int EDGE_RIGHT = 1 << 1; public static final int EDGE_TOP = 1 << 2; public static final int EDGE_BOTTOM = 1 << 3; //当开启边界滑动之后,详解下滑 此方法就会回调 @Override public void onEdgeTouched(int edgeFlags, int pointerId) { //通常开启边界之后, 都需要手动capture view.之后就可以滑动view了. mViewDragHelper.captureChildView(getChildAt(1), pointerId); } @Override public boolean tryCaptureView(View child, int pointerId) { //开启边界之后, 这个方法的返回值可能需要进一步处理.要不然开边界就没啥意思了. return false; }
5、释放后的动上回弹效果
有些时候, 当释放的时候, 需要将View回到原来的位置;
//释放的时候, 会回调下面的方法 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { //调用这个方法,就可以设置releasedChild回弹得位置. mViewDragHelper.settleCapturedViewAt(0, 100);//参数就是x,y的坐标 postInvalidate();//注意一定要调用这个方法,否则没效果. } //以下2个方法最终调用的都是forceSettleCapturedViewAt(). mViewDragHelper.settleCapturedViewAt(0, 100); mViewDragHelper.smoothSlideViewTo(getChildAt(1), 0, 100); //所以...发挥你的想象力,看看有什么妙用!!! //如果你还没有忘记的话...前文应该有说过,涉及到scroll,需要重写view的此方法. //此方法一定要重写,否则没效果 @Override public void computeScroll() { //固定写法 if (mViewDragHelper.continueSettling(true)) { postInvalidate();//注意此处. } } 通过上面2个方法的设置, 当手指释放的时候, View就会自动滑动到指定的位置...(不是一下子就到指定的位置哦,有一个滑动的过程.) 注意:如果需要滑动的View,会消耗touch事件,比如:Button,那么需要重写以下方法. @Override public int getViewHorizontalDragRange(View child) { return child.getMeasuredWidth();//只要返回大于0的值就行 } @Override public int getViewVerticalDragRange(View child) { return child.getMeasuredHeight();//只要返回大于0的值就行 }