写在前面的几句话
<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更多的使用方式了,组织能力不是很好。。。