Android-->ViewDragHelper的詳細使用方法

weixin_33758863發表於2017-04-27

ViewDragHelper是Android系統原生封裝用於ViewGroup滑動的類庫.(ViewDragHelper只能用在ViewGroup中.)

使用ViewDragHelper,可以非常方便的在ViewGroup中移動,滑動任意一個子View,並且控制相當方便.


1:基礎程式碼模版

public class ViewDragTestLayout extends RelativeLayout {
    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個方法, 你的ViewGroup就可以正常工作了.子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;  
}  

回撥順序

//一次滑動週期的回撥順序.(開始拖動到放手)
getOrderedChildIndex - 0
getOrderedChildIndex - 0
tryCaptureView - tryCaptureView -1 0
onViewCaptured - null
onViewDragStateChanged - null
clampViewPositionHorizontal - left:11 dx:11
clampViewPositionVertical - top:5 dy:5
onViewPositionChanged - null
...
clampViewPositionHorizontal - left:100 dx:23
clampViewPositionVertical - top:44 dy:11
onViewPositionChanged - null
onViewReleased - null
onViewDragStateChanged - null

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

回撥順序:

getOrderedChildIndex - 2
onEdgeTouched - edgeFlags:4 pointerId:0
getOrderedChildIndex - 2
tryCaptureView - tryCaptureView -1 0
...

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的值就行
}

到這裡, 就結束啦...


相關閱讀:
http://blog.csdn.net/lmj623565791/article/details/46858663
http://blog.csdn.net/pi9nc/article/details/39583377


至此: 文章就結束了,如有疑問: QQ群:274306954 歡迎您的加入.

相關文章