您的位置:首页 > 移动开发 > Android开发

菜鸟进阶之Android Touch事件传递(四)

2015-03-17 16:13 267 查看
尊重他人劳动成果,转载请说明出处:http://blog.csdn.net/bingospunky/article/details/44343477

在该系列文章第四篇,我准备介绍一下viewpager的touch事件处理。

如果想了解touch和click的那些事,请浏览touch事件传递系列的第一篇http://blog.csdn.net/bingospunky/article/details/43603397

如果想了解touch事件一步一步传递的路线,请浏览touch事件传递系列的第二篇http://blog.csdn.net/bingospunky/article/details/43735497

如果想从源码角度什么理解viewgroup的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent如何实现,请浏览touch事件传递系列的第二篇http://blog.csdn.net/bingospunky/article/details/44156771

源码

代码A:boolean android.support.v4.view.ViewPager.onInterceptTouchEvent(MotionEvent ev)

[java] view
plaincopyprint?

public boolean onInterceptTouchEvent(MotionEvent ev) {

/*

* This method JUST determines whether we want to intercept the motion.

* If we return true, onMotionEvent will be called and we do the actual

* scrolling there.

*/

final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;

// Always take care of the touch gesture being complete.

if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {

// Release the drag.

if (DEBUG) Log.v(TAG, "Intercept done!");

mIsBeingDragged = false;

mIsUnableToDrag = false;

mActivePointerId = INVALID_POINTER;

if (mVelocityTracker != null) {

mVelocityTracker.recycle();

mVelocityTracker = null;

}

return false;

}

// Nothing more to do here if we have decided whether or not we

// are dragging.

if (action != MotionEvent.ACTION_DOWN) {

if (mIsBeingDragged) {

if (DEBUG) Log.v(TAG, "Intercept returning true!");

return true;

}

if (mIsUnableToDrag) {

if (DEBUG) Log.v(TAG, "Intercept returning false!");

return false;

}

}

switch (action) {

case MotionEvent.ACTION_MOVE: {

/*

* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check

* whether the user has moved far enough from his original down touch.

*/

/*

* Locally do absolute value. mLastMotionY is set to the y value

* of the down event.

*/

final int activePointerId = mActivePointerId;

if (activePointerId == INVALID_POINTER) {

// If we don't have a valid id, the touch down wasn't on content.

break;

}

final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);

final float x = MotionEventCompat.getX(ev, pointerIndex);

final float dx = x - mLastMotionX;

final float xDiff = Math.abs(dx);

final float y = MotionEventCompat.getY(ev, pointerIndex);

final float yDiff = Math.abs(y - mInitialMotionY);

if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);

if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&

canScroll(this, false, (int) dx, (int) x, (int) y)) {

// Nested view has scrollable area under this point. Let it be handled there.

mLastMotionX = x;

mLastMotionY = y;

mIsUnableToDrag = true;

return false;

}

if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {

if (DEBUG) Log.v(TAG, "Starting drag!");

mIsBeingDragged = true;

requestParentDisallowInterceptTouchEvent(true);

setScrollState(SCROLL_STATE_DRAGGING);

mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :

mInitialMotionX - mTouchSlop;

mLastMotionY = y;

setScrollingCacheEnabled(true);

} else if (yDiff > mTouchSlop) {

// The finger has moved enough in the vertical

// direction to be counted as a drag... abort

// any attempt to drag horizontally, to work correctly

// with children that have scrolling containers.

if (DEBUG) Log.v(TAG, "Starting unable to drag!");

mIsUnableToDrag = true;

}

if (mIsBeingDragged) {

// Scroll to follow the motion event

if (performDrag(x)) {

ViewCompat.postInvalidateOnAnimation(this);

}

}

break;

}

case MotionEvent.ACTION_DOWN: {

/*

* Remember location of down touch.

* ACTION_DOWN always refers to pointer index 0.

*/

mLastMotionX = mInitialMotionX = ev.getX();

mLastMotionY = mInitialMotionY = ev.getY();

mActivePointerId = MotionEventCompat.getPointerId(ev, 0);

mIsUnableToDrag = false;

mScroller.computeScrollOffset();

if (mScrollState == SCROLL_STATE_SETTLING &&

Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {

// Let the user 'catch' the pager as it animates.

mScroller.abortAnimation();

mPopulatePending = false;

populate();

mIsBeingDragged = true;

requestParentDisallowInterceptTouchEvent(true);

setScrollState(SCROLL_STATE_DRAGGING);

} else {

completeScroll(false);

mIsBeingDragged = false;

}

if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY

+ " mIsBeingDragged=" + mIsBeingDragged

+ "mIsUnableToDrag=" + mIsUnableToDrag);

break;

}

case MotionEventCompat.ACTION_POINTER_UP:

onSecondaryPointerUp(ev);

break;

}

if (mVelocityTracker == null) {

mVelocityTracker = VelocityTracker.obtain();

}

mVelocityTracker.addMovement(ev);

/*

* The only time we want to intercept motion events is if we are in the

* drag mode.

*/

return mIsBeingDragged;

}

代码B:boolean android.support.v4.view.ViewPager.onTouchEvent(MotionEvent ev)

[java] view
plaincopyprint?

public boolean onTouchEvent(MotionEvent ev) {

if (mFakeDragging) {

// A fake drag is in progress already, ignore this real one

// but still eat the touch events.

// (It is likely that the user is multi-touching the screen.)

return true;

}

if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {

// Don't handle edge touches immediately -- they may actually belong to one of our

// descendants.

return false;

}

if (mAdapter == null || mAdapter.getCount() == 0) {

// Nothing to present or scroll; nothing to touch.

return false;

}

if (mVelocityTracker == null) {

mVelocityTracker = VelocityTracker.obtain();

}

mVelocityTracker.addMovement(ev);

final int action = ev.getAction();

boolean needsInvalidate = false;

switch (action & MotionEventCompat.ACTION_MASK) {

case MotionEvent.ACTION_DOWN: {

mScroller.abortAnimation();

mPopulatePending = false;

populate();

// Remember where the motion event started

mLastMotionX = mInitialMotionX = ev.getX();

mLastMotionY = mInitialMotionY = ev.getY();

mActivePointerId = MotionEventCompat.getPointerId(ev, 0);

break;

}

case MotionEvent.ACTION_MOVE:

if (!mIsBeingDragged) {

final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);

final float x = MotionEventCompat.getX(ev, pointerIndex);

final float xDiff = Math.abs(x - mLastMotionX);

final float y = MotionEventCompat.getY(ev, pointerIndex);

final float yDiff = Math.abs(y - mLastMotionY);

if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);

if (xDiff > mTouchSlop && xDiff > yDiff) {

if (DEBUG) Log.v(TAG, "Starting drag!");

mIsBeingDragged = true;

requestParentDisallowInterceptTouchEvent(true);

mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :

mInitialMotionX - mTouchSlop;

mLastMotionY = y;

setScrollState(SCROLL_STATE_DRAGGING);

setScrollingCacheEnabled(true);

// Disallow Parent Intercept, just in case

ViewParent parent = getParent();

if (parent != null) {

parent.requestDisallowInterceptTouchEvent(true);

}

}

}

// Not else! Note that mIsBeingDragged can be set above.

if (mIsBeingDragged) {

// Scroll to follow the motion event

final int activePointerIndex = MotionEventCompat.findPointerIndex(

ev, mActivePointerId);

final float x = MotionEventCompat.getX(ev, activePointerIndex);

needsInvalidate |= performDrag(x);

}

break;

case MotionEvent.ACTION_UP:

if (mIsBeingDragged) {

final VelocityTracker velocityTracker = mVelocityTracker;

velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);

int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(

velocityTracker, mActivePointerId);

mPopulatePending = true;

final int width = getClientWidth();

final int scrollX = getScrollX();

final ItemInfo ii = infoForCurrentScrollPosition();

final int currentPage = ii.position;

final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;

final int activePointerIndex =

MotionEventCompat.findPointerIndex(ev, mActivePointerId);

final float x = MotionEventCompat.getX(ev, activePointerIndex);

final int totalDelta = (int) (x - mInitialMotionX);

int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,

totalDelta);

setCurrentItemInternal(nextPage, true, true, initialVelocity);

mActivePointerId = INVALID_POINTER;

endDrag();

needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();

}

break;

case MotionEvent.ACTION_CANCEL:

if (mIsBeingDragged) {

scrollToItem(mCurItem, true, 0, false);

mActivePointerId = INVALID_POINTER;

endDrag();

needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();

}

break;

case MotionEventCompat.ACTION_POINTER_DOWN: {

final int index = MotionEventCompat.getActionIndex(ev);

final float x = MotionEventCompat.getX(ev, index);

mLastMotionX = x;

mActivePointerId = MotionEventCompat.getPointerId(ev, index);

break;

}

case MotionEventCompat.ACTION_POINTER_UP:

onSecondaryPointerUp(ev);

mLastMotionX = MotionEventCompat.getX(ev,

MotionEventCompat.findPointerIndex(ev, mActivePointerId));

break;

}

if (needsInvalidate) {

ViewCompat.postInvalidateOnAnimation(this);

}

return true;

}

代码C:void android.support.v4.view.ViewPager.onSecondaryPointerUp(MotionEvent ev)

[java] view
plaincopyprint?

private void onSecondaryPointerUp(MotionEvent ev) {

final int pointerIndex = MotionEventCompat.getActionIndex(ev);

final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);

if (pointerId == mActivePointerId) {

// This was our active pointer going up. Choose a new

// active pointer and adjust accordingly.

final int newPointerIndex = pointerIndex == 0 ? 1 : 0;

mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);

mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);

if (mVelocityTracker != null) {

mVelocityTracker.clear();

}

}

}

总结

在这里就不想上篇文章那样分各种情况去讨论了,那样有些无聊,这里就重要点的说明一下我的理解,最后附一个我写的demo供大家参考学习。

1、viewpager处理touch的思路:不截断touch,如果touch移动满足了一定的条件,再截断touch由该viewgroup处理。

2、我们可以看到onInterceptTouchEvent和onTouchEvent方法里的方法很相似,对于一个touch事件,这两个方法基本不会被都执行,只有很少的情况下这两个方法都会被执行。

3、viewpager有趣的现象,操作:单点操作viewpager,使viewpager响应事件,再加一个点触碰viewpager,如此不断加点,可以看到viewpager响应后加上去点的事件,这是为什么呢?代码B第108--111行已经告诉我们了,不再解释。

4、viewpager有趣的现象,操作:对于前面不不断加上去的触点,假设现在一共有4个触点,现在响应第4个触点的事件,如果第4个触点抬起,那么viewpager响应哪个触点的事件呢?答案是第一个。规律是什么呢?抬起的触点不是当前响应的,那么没影响;如果是当前响应的:如果抬起的触点的pointerIndex不是0,那么由pointerIndex最小的触点来响应,所以由第一个来响应。代码C已经很明显告诉我们了。如果这里你看不懂,那么你要学习一下android是怎么处理多触点事件的就会明白了。

Demo

下载地址:http://download.csdn.net/detail/u011647962/8507523
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: