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更多的使用方式了,组织能力不是很好。。。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容

  • ViewDragHelper实例的创建 ViewDragHelper重载了两个create()静态方法public...
    傀儡世界阅读 654评论 0 3
  • 简介: 提供一个让有限的窗口变成一个大数据集的灵活视图。 术语表: Adapter:RecyclerView的子类...
    酷泡泡阅读 5,140评论 0 16
  • 声明:本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 本篇为该系列的第二篇,侧重讲解ViewD...
    一枕黄粱终成梦阅读 1,266评论 1 6
  • 我不喜欢蚊子,也许是恶屋及乌的原因,我也不太喜欢文字,特别不喜欢写作,你会问蚊子和文字有什么关系吗?谁让他们...
    luckywby阅读 336评论 6 2
  • 投影的基础 使用光与阴影的方向来定义光平面。要想正确的投影必须掌握好这两个要素。无论光来自于太阳,月亮或人造光源这...
    _Z_阅读 150评论 0 0