ViewDragerHelper(二)源碼簡析

寫在前面的幾句話

<p>
上一篇文章對ViewDragerHelper有了簡單的介紹,相信大家對于ViewDragerHelper有一定的認識了,但是我們還是不太清楚為什么會這樣,那么這篇文章主要對于源碼進行解析,通過源碼的角度,我們來看看ViewDragerHelper到底是怎么Helper的

實現方式

<p>
看前面的代碼我們可以看到在ViewGroup的onInterceptTouchEvent與onTouchEvent中的方法分別調用了ViewDragerHelper的方法,onInterceptTouchEvent與onTouchEvent這里大家應該都清楚了解吧,就不對這個進行更多的介紹了,所以其實ViewDragerHelper之所以能夠實現上面的效果是與在onInterceptTouchEvent與onTouchEvent調用的方法有關的

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    return viewDragHelper.shouldInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    viewDragHelper.processTouchEvent(event);
    return true;
}

那么接下來我們看看這兩個方法里面到底是做了什么

一.shouldInterceptTouchEvent

<p>
直接上源碼

 public boolean shouldInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        final int actionIndex = MotionEventCompat.getActionIndex(ev);

        if (action == MotionEvent.ACTION_DOWN) {
            // Reset things for a new event stream, just in case we didn't get
            // the whole previous stream.
            cancel();
        }

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        switch (action) {
            case MotionEvent.ACTION_DOWN: {
                final float x = ev.getX();
                final float y = ev.getY();
                final int pointerId = MotionEventCompat.getPointerId(ev, 0);
                saveInitialMotion(x, y, pointerId);

                final View toCapture = findTopChildUnder((int) x, (int) y);

                // Catch a settling view if possible.
                if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {
                    tryCaptureViewForDrag(toCapture, pointerId);
                }

                final int edgesTouched = mInitialEdgesTouched[pointerId];
                if ((edgesTouched & mTrackingEdges) != 0) {
                    mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
                }
                break;
            }

            case MotionEventCompat.ACTION_POINTER_DOWN: {
                final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
                final float x = MotionEventCompat.getX(ev, actionIndex);
                final float y = MotionEventCompat.getY(ev, actionIndex);

                saveInitialMotion(x, y, pointerId);

                // A ViewDragHelper can only manipulate one view at a time.
                if (mDragState == STATE_IDLE) {
                    final int edgesTouched = mInitialEdgesTouched[pointerId];
                    if ((edgesTouched & mTrackingEdges) != 0) {
                        mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
                    }
                } else if (mDragState == STATE_SETTLING) {
                    // Catch a settling view if possible.
                    final View toCapture = findTopChildUnder((int) x, (int) y);
                    if (toCapture == mCapturedView) {
                        tryCaptureViewForDrag(toCapture, pointerId);
                    }
                }
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                if (mInitialMotionX == null || mInitialMotionY == null) break;

                // First to cross a touch slop over a draggable view wins. Also report edge drags.
                final int pointerCount = MotionEventCompat.getPointerCount(ev);
                for (int i = 0; i < pointerCount; i++) {
                    final int pointerId = MotionEventCompat.getPointerId(ev, i);

                    // If pointer is invalid then skip the ACTION_MOVE.
                    if (!isValidPointerForActionMove(pointerId)) continue;

                    final float x = MotionEventCompat.getX(ev, i);
                    final float y = MotionEventCompat.getY(ev, i);
                    final float dx = x - mInitialMotionX[pointerId];
                    final float dy = y - mInitialMotionY[pointerId];

                    final View toCapture = findTopChildUnder((int) x, (int) y);
                    final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy);
                    if (pastSlop) {
                        // check the callback's
                        // getView[Horizontal|Vertical]DragRange methods to know
                        // if you can move at all along an axis, then see if it
                        // would clamp to the same value. If you can't move at
                        // all in every dimension with a nonzero range, bail.
                        final int oldLeft = toCapture.getLeft();
                        final int targetLeft = oldLeft + (int) dx;
                        final int newLeft = mCallback.clampViewPositionHorizontal(toCapture,
                                targetLeft, (int) dx);
                        final int oldTop = toCapture.getTop();
                        final int targetTop = oldTop + (int) dy;
                        final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,
                                (int) dy);
                        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;
                        }
                    }
                    reportNewEdgeDrags(dx, dy, pointerId);
                    if (mDragState == STATE_DRAGGING) {
                        // Callback might have started an edge drag
                        break;
                    }

                    if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) {
                        break;
                    }
                }
                saveLastMotion(ev);
                break;
            }

            case MotionEventCompat.ACTION_POINTER_UP: {
                final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
                clearMotionHistory(pointerId);
                break;
            }

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                cancel();
                break;
            }
        }

        return mDragState == STATE_DRAGGING;
    }

分成幾個部分分析

1.準備工作

final int action = MotionEventCompat.getActionMasked(ev);
final int actionIndex = MotionEventCompat.getActionIndex(ev);

if (action == MotionEvent.ACTION_DOWN) {
    // Reset things for a new event stream, just in case we didn't get
    // the whole previous stream.
    cancel();
}

if (mVelocityTracker == null) {
    mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);

那這里主要做一些準備工作,

  • 獲取action
  • 獲取action對應的index
  • 如果是按下的action則重置一些信息
  • 初始化VelocityTracker,VelocityTracker(用于追蹤滑動速度)是什么不做介紹,前面的文章有介紹

2.ACTION_DOWN相關解析

case MotionEvent.ACTION_DOWN: {
    final float x = ev.getX();
    final float y = ev.getY();
    final int pointerId = MotionEventCompat.getPointerId(ev, 0);
    saveInitialMotion(x, y, pointerId);

    final View toCapture = findTopChildUnder((int) x, (int) y);

    // Catch a settling view if possible.
    if (toCapture == mCapturedView && mDragState == STATE_SETTLING) {
        tryCaptureViewForDrag(toCapture, pointerId);
    }

    final int edgesTouched = mInitialEdgesTouched[pointerId];
    if ((edgesTouched & mTrackingEdges) != 0) {
        mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
    }
    break;
}

依次介紹里面的做了什么

  • (1).獲取按下的x,y位置與獲取這個事件對應的pointerId,然后保存(saveInitialMotion)這些信息

    注意pointerId一般情況下只有一個手指觸摸時為0,兩個手指觸摸時第二個手指觸摸返回的pointerId為1,以此類推

    看下保存信息這里的方法

private void saveInitialMotion(float x, float y, int pointerId) {
//確保各個數組的大小足夠存放數據
ensureMotionHistorySizeForId(pointerId);
//保存x坐標
mInitialMotionX[pointerId] = mLastMotionX[pointerId] = x;
//保存y坐標
mInitialMotionY[pointerId] = mLastMotionY[pointerId] = y;
//保存是否觸摸到邊緣
mInitialEdgesTouched[pointerId] = getEdgesTouched((int) x, (int) y);
//保存當前id是否在觸摸,用于后續驗證
mPointersDown |= 1 << pointerId;
}


* (2).獲取當前觸摸點下最頂層的子View(findTopChildUnder),并捕獲(tryCaptureViewForDrag)

 先看下findTopChildUnder方法

 ```
public View findTopChildUnder(int x, int y) {
 final int childCount = mParentView.getChildCount();
 for (int i = childCount - 1; i >= 0; i--) {
     final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
     if (x >= child.getLeft() && x < child.getRight() &&
             y >= child.getTop() && y < child.getBottom()) {
         return child;
     }
 }
 return null;
}
 ```

這里做的就是遍歷整個整個ViewGroup的子View通過位置來查找指定的View,這里有個**mCallback.getOrderedChildIndex(i)**,大家應該通過上面一篇文章,對這個方法有點眼熟吧,(如果需要改變子View的遍歷查詢順序可改寫此方法,例如讓下層的View優先于上層的View被選中。)所以我們可以在這里返回指定的View的index

接下來看一下tryCaptureViewForDrag方法

boolean tryCaptureViewForDrag(View toCapture, int pointerId) {
//如果已經捕獲該View 直接返回true
if (toCapture == mCapturedView && mActivePointerId == pointerId) {
// Already done!
return true;
}
//根據mCallback.tryCaptureView()方法來最終決定是否可以捕獲View
if (toCapture != null && mCallback.tryCaptureView(toCapture, pointerId)) {
mActivePointerId = pointerId;
captureChildView(toCapture, pointerId);
return true;
}
return false;
}

public void captureChildView(View childView, int activePointerId) {
if (childView.getParent() != mParentView) {
throw new IllegalArgumentException("captureChildView: parameter must be a descendant " +
"of the ViewDragHelper's tracked parent view (" + mParentView + ")");
}

mCapturedView = childView;
mActivePointerId = activePointerId;
mCallback.onViewCaptured(childView, activePointerId);
setDragState(STATE_DRAGGING);

}


代碼的注釋其實大家大致可以理解是什么意思了吧,主要這里有一個**mCallback.tryCaptureView()**的方法,而這個tryCaptureView()方法,其實我們可以自己去定義的,所以是否捕獲某個子View,其實是我們可以控制的,當可以捕獲這個View后就會把這個View賦值給mCapturedView,同時會回調方法**mCallback.onViewCaptured()**并且設定mDragState的狀態為STATE_DRAGGING

*(3). 如果觸摸了邊緣,回調callback的onEdgeTouched()方法

在第一步保存(saveInitialMotion)信息的時候,保存是否觸摸到邊緣,這里就直接拿出來判斷是否觸摸到了邊緣,還有一個參數為mTrackingEdges,這個參數其實是我們來設置的

viewDragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);


除了EDGE_ALL,還有EDGE_LEFT,EDGE_TOP,EDGE_RIGHT,EDGE_BOTTOM,具體表示什么大家應該一眼明了,

所以當我們設置了這個mTrackingEdges并且觸摸到了邊緣會回調**mCallback.onEdgeTouched()**這個方法

**3.ACTION_POINTER_DOWN(又有一個手指觸摸時)相關解析**

case MotionEventCompat.ACTION_POINTER_DOWN: {
final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
final float x = MotionEventCompat.getX(ev, actionIndex);
final float y = MotionEventCompat.getY(ev, actionIndex);

saveInitialMotion(x, y, pointerId);

//因為同一時間ViewDragHelper只能操控一個View,所以當有新的手指觸摸時 
//只討論當無觸摸發生時,回調邊緣觸摸的callback
//或者正在處于釋放狀態時重新捕獲View
// A ViewDragHelper can only manipulate one view at a time.
if (mDragState == STATE_IDLE) {
    final int edgesTouched = mInitialEdgesTouched[pointerId];
    if ((edgesTouched & mTrackingEdges) != 0) {
        mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
    }
} else if (mDragState == STATE_SETTLING) {
    // Catch a settling view if possible.
    final View toCapture = findTopChildUnder((int) x, (int) y);
    if (toCapture == mCapturedView) {
        tryCaptureViewForDrag(toCapture, pointerId);
    }
}
break;

}


這里面的方法在ACTION_DOWN中基本都已經說明過了,就不做過多的解釋了

**4.ACTION_MOVE相關解析**

case MotionEvent.ACTION_MOVE: {
if (mInitialMotionX == null || mInitialMotionY == null) break;
//得到觸摸點的數量,并循環處理,只處理第一個發生了拖拽的事件
// First to cross a touch slop over a draggable view wins. Also report edge drags.
final int pointerCount = MotionEventCompat.getPointerCount(ev);
for (int i = 0; i < pointerCount; i++) {
final int pointerId = MotionEventCompat.getPointerId(ev, i);

    // If pointer is invalid then skip the ACTION_MOVE.
    if (!isValidPointerForActionMove(pointerId)) continue;

    final float x = MotionEventCompat.getX(ev, i);
    final float y = MotionEventCompat.getY(ev, i);
    final float dx = x - mInitialMotionX[pointerId];
    final float dy = y - mInitialMotionY[pointerId];

    final View toCapture = findTopChildUnder((int) x, (int) y);
    final boolean pastSlop = toCapture != null && checkTouchSlop(toCapture, dx, dy);
    if (pastSlop) {
        // check the callback's
        // getView[Horizontal|Vertical]DragRange methods to know
        // if you can move at all along an axis, then see if it
        // would clamp to the same value. If you can't move at
        // all in every dimension with a nonzero range, bail.
        final int oldLeft = toCapture.getLeft();
        final int targetLeft = oldLeft + (int) dx;
        final int newLeft = mCallback.clampViewPositionHorizontal(toCapture,
                targetLeft, (int) dx);
        final int oldTop = toCapture.getTop();
        final int targetTop = oldTop + (int) dy;
        final int newTop = mCallback.clampViewPositionVertical(toCapture, targetTop,
                (int) dy);
        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;
        }
    }
    reportNewEdgeDrags(dx, dy, pointerId);
    if (mDragState == STATE_DRAGGING) {
        // Callback might have started an edge drag
        break;
    }

    if (pastSlop && tryCaptureViewForDrag(toCapture, pointerId)) {
        break;
    }
}
saveLastMotion(ev);
break;

}


依次介紹里面的做了什么

* (1). 獲取當前觸摸點下最頂層的子View(findTopChildUnder),判斷是否產生拖動(checkTouchSlop)

  findTopChildUnder上面已經做過介紹了,那對checkTouchSlop進行分析

private boolean checkTouchSlop(View child, float dx, float dy) {
if (child == null) {
return false;
}
final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0;
final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0;

if (checkHorizontal && checkVertical) {
    return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
} else if (checkHorizontal) {
    return Math.abs(dx) > mTouchSlop;
} else if (checkVertical) {
    return Math.abs(dy) > mTouchSlop;
}
return false;

}


根據mTouchSlop最小拖動的距離來判斷是否屬于拖動,mTouchSlop根據我們設定的靈敏度決定,同時我們可以看到這里有獲取**mCallback.getViewHorizontalDragRange(child)**與**mCallback.getViewVerticalDragRange(child)**的值,而這個值則是我們通過Callback設置的

* (2).根據callback的四個方法
**getView[Horizontal|Vertical]DragRange和clampViewPosition[Horizontal|Vertical]**來檢查是否可以拖動

* (3).記錄并回調是否有邊緣觸摸(reportNewEdgeDrags)

* (4).保存觸摸點的信息

**5.剩余部分**

//當有一個手指抬起時,清除這個手指的觸摸數據
case MotionEventCompat.ACTION_POINTER_UP: {
final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
clearMotionHistory(pointerId);
break;
}

//清除所有觸摸數據
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
cancel();
break;
}


這里不做解釋了
 
**6.注意**

return mDragState == STATE_DRAGGING;


最后的這個則是說明當在STATE_DRAGGING狀態下將事件消費掉,不像子View傳遞了

###二. processTouchEvent
<p>
先直接上源碼

public void processTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
final int actionIndex = MotionEventCompat.getActionIndex(ev);

if (action == MotionEvent.ACTION_DOWN) {
    // Reset things for a new event stream, just in case we didn't get
    // the whole previous stream.
    cancel();
}

if (mVelocityTracker == null) {
    mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);

switch (action) {
    case MotionEvent.ACTION_DOWN: {
        final float x = ev.getX();
        final float y = ev.getY();
        final int pointerId = MotionEventCompat.getPointerId(ev, 0);
        final View toCapture = findTopChildUnder((int) x, (int) y);

        saveInitialMotion(x, y, pointerId);

        // Since the parent is already directly processing this touch event,
        // there is no reason to delay for a slop before dragging.
        // Start immediately if possible.
        tryCaptureViewForDrag(toCapture, pointerId);

        final int edgesTouched = mInitialEdgesTouched[pointerId];
        if ((edgesTouched & mTrackingEdges) != 0) {
            mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
        }
        break;
    }

    case MotionEventCompat.ACTION_POINTER_DOWN: {
        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
        final float x = MotionEventCompat.getX(ev, actionIndex);
        final float y = MotionEventCompat.getY(ev, actionIndex);

        saveInitialMotion(x, y, pointerId);

        // A ViewDragHelper can only manipulate one view at a time.
        if (mDragState == STATE_IDLE) {
            // If we're idle we can do anything! Treat it like a normal down event.

            final View toCapture = findTopChildUnder((int) x, (int) y);
            tryCaptureViewForDrag(toCapture, pointerId);

            final int edgesTouched = mInitialEdgesTouched[pointerId];
            if ((edgesTouched & mTrackingEdges) != 0) {
                mCallback.onEdgeTouched(edgesTouched & mTrackingEdges, pointerId);
            }
        } else if (isCapturedViewUnder((int) x, (int) y)) {
            // We're still tracking a captured view. If the same view is under this
            // point, we'll swap to controlling it with this pointer instead.
            // (This will still work if we're "catching" a settling view.)

            tryCaptureViewForDrag(mCapturedView, pointerId);
        }
        break;
    }

    case MotionEvent.ACTION_MOVE: {
        if (mDragState == STATE_DRAGGING) {
            // If pointer is invalid then skip the ACTION_MOVE.
            if (!isValidPointerForActionMove(mActivePointerId)) break;

            final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
            final float x = MotionEventCompat.getX(ev, index);
            final float y = MotionEventCompat.getY(ev, index);
            final int idx = (int) (x - mLastMotionX[mActivePointerId]);
            final int idy = (int) (y - mLastMotionY[mActivePointerId]);

            dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);

            saveLastMotion(ev);
        } else {
            // Check to see if any pointer is now over a draggable view.
            final int pointerCount = MotionEventCompat.getPointerCount(ev);
            for (int i = 0; i < pointerCount; i++) {
                final int pointerId = MotionEventCompat.getPointerId(ev, i);

                // If pointer is invalid then skip the ACTION_MOVE.
                if (!isValidPointerForActionMove(pointerId)) continue;

                final float x = MotionEventCompat.getX(ev, i);
                final float y = MotionEventCompat.getY(ev, i);
                final float dx = x - mInitialMotionX[pointerId];
                final float dy = y - mInitialMotionY[pointerId];

                reportNewEdgeDrags(dx, dy, pointerId);
                if (mDragState == STATE_DRAGGING) {
                    // Callback might have started an edge drag.
                    break;
                }

                final View toCapture = findTopChildUnder((int) x, (int) y);
                if (checkTouchSlop(toCapture, dx, dy) &&
                        tryCaptureViewForDrag(toCapture, pointerId)) {
                    break;
                }
            }
            saveLastMotion(ev);
        }
        break;
    }

    case MotionEventCompat.ACTION_POINTER_UP: {
        final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
        if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
            // Try to find another pointer that's still holding on to the captured view.
            int newActivePointer = INVALID_POINTER;
            final int pointerCount = MotionEventCompat.getPointerCount(ev);
            for (int i = 0; i < pointerCount; i++) {
                final int id = MotionEventCompat.getPointerId(ev, i);
                if (id == mActivePointerId) {
                    // This one's going away, skip.
                    continue;
                }

                final float x = MotionEventCompat.getX(ev, i);
                final float y = MotionEventCompat.getY(ev, i);
                if (findTopChildUnder((int) x, (int) y) == mCapturedView &&
                        tryCaptureViewForDrag(mCapturedView, id)) {
                    newActivePointer = mActivePointerId;
                    break;
                }
            }

            if (newActivePointer == INVALID_POINTER) {
                // We didn't find another pointer still touching the view, release it.
                releaseViewForPointerUp();
            }
        }
        clearMotionHistory(pointerId);
        break;
    }

    case MotionEvent.ACTION_UP: {
        if (mDragState == STATE_DRAGGING) {
            releaseViewForPointerUp();
        }
        cancel();
        break;
    }

    case MotionEvent.ACTION_CANCEL: {
        if (mDragState == STATE_DRAGGING) {
            dispatchViewReleased(0, 0);
        }
        cancel();
        break;
    }
}

}


分成幾個部分分析

**1.準備工作**

**2.ACTION_DOWN相關解析**

**3.ACTION_POINTER_DOWN(又有一個手指觸摸時)相關解析**

與上面shouldInterceptTouchEvent部分類似不做更多的分析了

**4. ACTION_MOVE相關解析**

    case MotionEvent.ACTION_MOVE: {
        if (mDragState == STATE_DRAGGING) {
            // If pointer is invalid then skip the ACTION_MOVE.
            if (!isValidPointerForActionMove(mActivePointerId)) break;

            final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
            final float x = MotionEventCompat.getX(ev, index);
            final float y = MotionEventCompat.getY(ev, index);
            final int idx = (int) (x - mLastMotionX[mActivePointerId]);
            final int idy = (int) (y - mLastMotionY[mActivePointerId]);

            dragTo(mCapturedView.getLeft() + idx, mCapturedView.getTop() + idy, idx, idy);

            saveLastMotion(ev);
        } else {
            // Check to see if any pointer is now over a draggable view.
            final int pointerCount = MotionEventCompat.getPointerCount(ev);
            for (int i = 0; i < pointerCount; i++) {
                final int pointerId = MotionEventCompat.getPointerId(ev, i);

                // If pointer is invalid then skip the ACTION_MOVE.
                if (!isValidPointerForActionMove(pointerId)) continue;

                final float x = MotionEventCompat.getX(ev, i);
                final float y = MotionEventCompat.getY(ev, i);
                final float dx = x - mInitialMotionX[pointerId];
                final float dy = y - mInitialMotionY[pointerId];

                reportNewEdgeDrags(dx, dy, pointerId);
                if (mDragState == STATE_DRAGGING) {
                    // Callback might have started an edge drag.
                    break;
                }

                final View toCapture = findTopChildUnder((int) x, (int) y);
                if (checkTouchSlop(toCapture, dx, dy) &&
                        tryCaptureViewForDrag(toCapture, pointerId)) {
                    break;
                }
            }
            saveLastMotion(ev);
        }
        break;
    }

這里其實很好很好理解,因為其實本身自帶的英文解釋也解釋的很清楚了

* 1.當mDragState為STATE_DRAGGING狀態時,拖拽至指定位置(dragTo)

  看下dragTo方法

private void dragTo(int left, int top, int dx, int dy) {
int clampedX = left;
int clampedY = top;
final int oldLeft = mCapturedView.getLeft();
final int oldTop = mCapturedView.getTop();
if (dx != 0) {
//回調callback來決定View最終被拖拽的x方向上的偏移量
clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
ViewCompat.offsetLeftAndRight(mCapturedView, clampedX - oldLeft);
}
if (dy != 0) {
//回調callback來決定View最終被拖拽的y方向上的偏移量
clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
ViewCompat.offsetTopAndBottom(mCapturedView, clampedY - oldTop);
}

if (dx != 0 || dy != 0) {
    final int clampedDx = clampedX - oldLeft;
    final int clampedDy = clampedY - oldTop;
    //回調callback
    mCallback.onViewPositionChanged(mCapturedView, clampedX, clampedY,
            clampedDx, clampedDy);
}

}


這里主要回調了callback的三個方法

* 2.當mDragStat不為STATE_DRAGGING狀態時,就檢測當前的位置是否經在一個View上,進行重新捕獲View

方法內部與shouldInterceptTouchEvent的ACTION_MOVE類似,大家理解就好了

**5.ACTION_POINTER_UP(當多個手指中的一個手機松開時)相關解析**

case MotionEventCompat.ACTION_POINTER_UP: {
final int pointerId = MotionEventCompat.getPointerId(ev, actionIndex);
if (mDragState == STATE_DRAGGING && pointerId == mActivePointerId) {
// Try to find another pointer that's still holding on to the captured view.
int newActivePointer = INVALID_POINTER;
final int pointerCount = MotionEventCompat.getPointerCount(ev);
for (int i = 0; i < pointerCount; i++) {
final int id = MotionEventCompat.getPointerId(ev, i);
if (id == mActivePointerId) {
// This one's going away, skip.
continue;
}

            final float x = MotionEventCompat.getX(ev, i);
            final float y = MotionEventCompat.getY(ev, i);
            if (findTopChildUnder((int) x, (int) y) == mCapturedView &&
                    tryCaptureViewForDrag(mCapturedView, id)) {
                newActivePointer = mActivePointerId;
                break;
            }
        }

        if (newActivePointer == INVALID_POINTER) {
            // We didn't find another pointer still touching the view, release it.
            releaseViewForPointerUp();
        }
    }
    clearMotionHistory(pointerId);
    break;
}

這里主要做的工作是,當正在STATE_DRAGGING狀態時多個手指中的一個松開,則再剩余還在觸摸的點鐘尋找是否正在View上(findTopChildUnder((int) x, (int) y) == mCapturedView && tryCaptureViewForDrag(mCapturedView, id))如果沒找到則釋放View(releaseViewForPointerUp())

**6.剩余部分**

case MotionEvent.ACTION_UP: {
//如果是拖拽狀態的釋放則調用
//releaseViewForPointerUp()
if (mDragState == STATE_DRAGGING) {
releaseViewForPointerUp();
}
cancel();
break;
}

case MotionEvent.ACTION_CANCEL: {
if (mDragState == STATE_DRAGGING) {
dispatchViewReleased(0, 0);
}
cancel();
break;
}


這里注釋也已經寫的很清楚了

###寫在后面的幾句話
<p>
好了到這里ViewDragerHelper源碼部分基本介紹完畢了,當然并不是特別詳細,但是大家對照看下就可以理解了,后面會介紹下ViewDragerHelper更多的使用方式了,組織能力不是很好。。。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • ViewDragHelper實例的創建 ViewDragHelper重載了兩個create()靜態方法public...
    傀儡世界閱讀 673評論 0 3
  • 簡介: 提供一個讓有限的窗口變成一個大數據集的靈活視圖。 術語表: Adapter:RecyclerView的子類...
    酷泡泡閱讀 5,217評論 0 16
  • 聲明:本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發布 本篇為該系列的第二篇,側重講解ViewD...
    一枕黃粱終成夢閱讀 1,295評論 1 6
  • 我不喜歡蚊子,也許是惡屋及烏的原因,我也不太喜歡文字,特別不喜歡寫作,你會問蚊子和文字有什么關系嗎?誰讓他們...
    luckywby閱讀 353評論 6 2
  • 投影的基礎 使用光與陰影的方向來定義光平面。要想正確的投影必須掌握好這兩個要素。無論光來自于太陽,月亮或人造光源這...
    _Z_閱讀 158評論 0 0