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

尊重他人劳动成果,转载请说明出处: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)

  1. public boolean onInterceptTouchEvent(MotionEvent ev) {  
  2.         /* 
  3.          * This method JUST determines whether we want to intercept the motion. 
  4.          * If we return true, onMotionEvent will be called and we do the actual 
  5.          * scrolling there. 
  6.          */  
  7.   
  8.         final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;  
  9.   
  10.         // Always take care of the touch gesture being complete.  
  11.         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {  
  12.             // Release the drag.  
  13.             if (DEBUG) Log.v(TAG, "Intercept done!");  
  14.             mIsBeingDragged = false;  
  15.             mIsUnableToDrag = false;  
  16.             mActivePointerId = INVALID_POINTER;  
  17.             if (mVelocityTracker != null) {  
  18.                 mVelocityTracker.recycle();  
  19.                 mVelocityTracker = null;  
  20.             }  
  21.             return false;  
  22.         }  
  23.   
  24.         // Nothing more to do here if we have decided whether or not we  
  25.         // are dragging.  
  26.         if (action != MotionEvent.ACTION_DOWN) {  
  27.             if (mIsBeingDragged) {  
  28.                 if (DEBUG) Log.v(TAG, "Intercept returning true!");  
  29.                 return true;  
  30.             }  
  31.             if (mIsUnableToDrag) {  
  32.                 if (DEBUG) Log.v(TAG, "Intercept returning false!");  
  33.                 return false;  
  34.             }  
  35.         }  
  36.   
  37.         switch (action) {  
  38.             case MotionEvent.ACTION_MOVE: {  
  39.                 /* 
  40.                  * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check 
  41.                  * whether the user has moved far enough from his original down touch. 
  42.                  */  
  43.   
  44.                 /* 
  45.                 * Locally do absolute value. mLastMotionY is set to the y value 
  46.                 * of the down event. 
  47.                 */  
  48.                 final int activePointerId = mActivePointerId;  
  49.                 if (activePointerId == INVALID_POINTER) {  
  50.                     // If we don't have a valid id, the touch down wasn't on content.  
  51.                     break;  
  52.                 }  
  53.   
  54.                 final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);  
  55.                 final float x = MotionEventCompat.getX(ev, pointerIndex);  
  56.                 final float dx = x - mLastMotionX;  
  57.                 final float xDiff = Math.abs(dx);  
  58.                 final float y = MotionEventCompat.getY(ev, pointerIndex);  
  59.                 final float yDiff = Math.abs(y - mInitialMotionY);  
  60.                 if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);  
  61.   
  62.                 if (dx != 0 && !isGutterDrag(mLastMotionX, dx) &&  
  63.                         canScroll(thisfalse, (int) dx, (int) x, (int) y)) {  
  64.                     // Nested view has scrollable area under this point. Let it be handled there.  
  65.                     mLastMotionX = x;  
  66.                     mLastMotionY = y;  
  67.                     mIsUnableToDrag = true;  
  68.                     return false;  
  69.                 }  
  70.                 if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {  
  71.                     if (DEBUG) Log.v(TAG, "Starting drag!");  
  72.                     mIsBeingDragged = true;  
  73.                     requestParentDisallowInterceptTouchEvent(true);  
  74.                     setScrollState(SCROLL_STATE_DRAGGING);  
  75.                     mLastMotionX = dx > 0 ? mInitialMotionX + mTouchSlop :  
  76.                             mInitialMotionX - mTouchSlop;  
  77.                     mLastMotionY = y;  
  78.                     setScrollingCacheEnabled(true);  
  79.                 } else if (yDiff > mTouchSlop) {  
  80.                     // The finger has moved enough in the vertical  
  81.                     // direction to be counted as a drag...  abort  
  82.                     // any attempt to drag horizontally, to work correctly  
  83.                     // with children that have scrolling containers.  
  84.                     if (DEBUG) Log.v(TAG, "Starting unable to drag!");  
  85.                     mIsUnableToDrag = true;  
  86.                 }  
  87.                 if (mIsBeingDragged) {  
  88.                     // Scroll to follow the motion event  
  89.                     if (performDrag(x)) {  
  90.                         ViewCompat.postInvalidateOnAnimation(this);  
  91.                     }  
  92.                 }  
  93.                 break;  
  94.             }  
  95.   
  96.             case MotionEvent.ACTION_DOWN: {  
  97.                 /* 
  98.                  * Remember location of down touch. 
  99.                  * ACTION_DOWN always refers to pointer index 0. 
  100.                  */  
  101.                 mLastMotionX = mInitialMotionX = ev.getX();  
  102.                 mLastMotionY = mInitialMotionY = ev.getY();  
  103.                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);  
  104.                 mIsUnableToDrag = false;  
  105.   
  106.                 mScroller.computeScrollOffset();  
  107.                 if (mScrollState == SCROLL_STATE_SETTLING &&  
  108.                         Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {  
  109.                     // Let the user 'catch' the pager as it animates.  
  110.                     mScroller.abortAnimation();  
  111.                     mPopulatePending = false;  
  112.                     populate();  
  113.                     mIsBeingDragged = true;  
  114.                     requestParentDisallowInterceptTouchEvent(true);  
  115.                     setScrollState(SCROLL_STATE_DRAGGING);  
  116.                 } else {  
  117.                     completeScroll(false);  
  118.                     mIsBeingDragged = false;  
  119.                 }  
  120.   
  121.                 if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY  
  122.                         + " mIsBeingDragged=" + mIsBeingDragged  
  123.                         + "mIsUnableToDrag=" + mIsUnableToDrag);  
  124.                 break;  
  125.             }  
  126.   
  127.             case MotionEventCompat.ACTION_POINTER_UP:  
  128.                 onSecondaryPointerUp(ev);  
  129.                 break;  
  130.         }  
  131.   
  132.         if (mVelocityTracker == null) {  
  133.             mVelocityTracker = VelocityTracker.obtain();  
  134.         }  
  135.         mVelocityTracker.addMovement(ev);  
  136.   
  137.         /* 
  138.          * The only time we want to intercept motion events is if we are in the 
  139.          * drag mode. 
  140.          */  
  141.         return mIsBeingDragged;  
  142.     }  
代码B:boolean android.support.v4.view.ViewPager.onTouchEvent(MotionEvent ev)

  1. public boolean onTouchEvent(MotionEvent ev) {  
  2.         if (mFakeDragging) {  
  3.             // A fake drag is in progress already, ignore this real one  
  4.             // but still eat the touch events.  
  5.             // (It is likely that the user is multi-touching the screen.)  
  6.             return true;  
  7.         }  
  8.   
  9.         if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {  
  10.             // Don't handle edge touches immediately -- they may actually belong to one of our  
  11.             // descendants.  
  12.             return false;  
  13.         }  
  14.   
  15.         if (mAdapter == null || mAdapter.getCount() == 0) {  
  16.             // Nothing to present or scroll; nothing to touch.  
  17.             return false;  
  18.         }  
  19.   
  20.         if (mVelocityTracker == null) {  
  21.             mVelocityTracker = VelocityTracker.obtain();  
  22.         }  
  23.         mVelocityTracker.addMovement(ev);  
  24.   
  25.         final int action = ev.getAction();  
  26.         boolean needsInvalidate = false;  
  27.   
  28.         switch (action & MotionEventCompat.ACTION_MASK) {  
  29.             case MotionEvent.ACTION_DOWN: {  
  30.                 mScroller.abortAnimation();  
  31.                 mPopulatePending = false;  
  32.                 populate();  
  33.   
  34.                 // Remember where the motion event started  
  35.                 mLastMotionX = mInitialMotionX = ev.getX();  
  36.                 mLastMotionY = mInitialMotionY = ev.getY();  
  37.                 mActivePointerId = MotionEventCompat.getPointerId(ev, 0);  
  38.                 break;  
  39.             }  
  40.             case MotionEvent.ACTION_MOVE:  
  41.                 if (!mIsBeingDragged) {  
  42.                     final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);  
  43.                     final float x = MotionEventCompat.getX(ev, pointerIndex);  
  44.                     final float xDiff = Math.abs(x - mLastMotionX);  
  45.                     final float y = MotionEventCompat.getY(ev, pointerIndex);  
  46.                     final float yDiff = Math.abs(y - mLastMotionY);  
  47.                     if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);  
  48.                     if (xDiff > mTouchSlop && xDiff > yDiff) {  
  49.                         if (DEBUG) Log.v(TAG, "Starting drag!");  
  50.                         mIsBeingDragged = true;  
  51.                         requestParentDisallowInterceptTouchEvent(true);  
  52.                         mLastMotionX = x - mInitialMotionX > 0 ? mInitialMotionX + mTouchSlop :  
  53.                                 mInitialMotionX - mTouchSlop;  
  54.                         mLastMotionY = y;  
  55.                         setScrollState(SCROLL_STATE_DRAGGING);  
  56.                         setScrollingCacheEnabled(true);  
  57.   
  58.                         // Disallow Parent Intercept, just in case  
  59.                         ViewParent parent = getParent();  
  60.                         if (parent != null) {  
  61.                             parent.requestDisallowInterceptTouchEvent(true);  
  62.                         }  
  63.                     }  
  64.                 }  
  65.                 // Not else! Note that mIsBeingDragged can be set above.  
  66.                 if (mIsBeingDragged) {  
  67.                     // Scroll to follow the motion event  
  68.                     final int activePointerIndex = MotionEventCompat.findPointerIndex(  
  69.                             ev, mActivePointerId);  
  70.                     final float x = MotionEventCompat.getX(ev, activePointerIndex);  
  71.                     needsInvalidate |= performDrag(x);  
  72.                 }  
  73.                 break;  
  74.             case MotionEvent.ACTION_UP:  
  75.                 if (mIsBeingDragged) {  
  76.                     final VelocityTracker velocityTracker = mVelocityTracker;  
  77.                     velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);  
  78.                     int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(  
  79.                             velocityTracker, mActivePointerId);  
  80.                     mPopulatePending = true;  
  81.                     final int width = getClientWidth();  
  82.                     final int scrollX = getScrollX();  
  83.                     final ItemInfo ii = infoForCurrentScrollPosition();  
  84.                     final int currentPage = ii.position;  
  85.                     final float pageOffset = (((float) scrollX / width) - ii.offset) / ii.widthFactor;  
  86.                     final int activePointerIndex =  
  87.                             MotionEventCompat.findPointerIndex(ev, mActivePointerId);  
  88.                     final float x = MotionEventCompat.getX(ev, activePointerIndex);  
  89.                     final int totalDelta = (int) (x - mInitialMotionX);  
  90.                     int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,  
  91.                             totalDelta);  
  92.                     setCurrentItemInternal(nextPage, truetrue, initialVelocity);  
  93.   
  94.                     mActivePointerId = INVALID_POINTER;  
  95.                     endDrag();  
  96.                     needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();  
  97.                 }  
  98.                 break;  
  99.             case MotionEvent.ACTION_CANCEL:  
  100.                 if (mIsBeingDragged) {  
  101.                     scrollToItem(mCurItem, true0false);  
  102.                     mActivePointerId = INVALID_POINTER;  
  103.                     endDrag();  
  104.                     needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();  
  105.                 }  
  106.                 break;  
  107.             case MotionEventCompat.ACTION_POINTER_DOWN: {  
  108.                 final int index = MotionEventCompat.getActionIndex(ev);  
  109.                 final float x = MotionEventCompat.getX(ev, index);  
  110.                 mLastMotionX = x;  
  111.                 mActivePointerId = MotionEventCompat.getPointerId(ev, index);  
  112.                 break;  
  113.             }  
  114.             case MotionEventCompat.ACTION_POINTER_UP:  
  115.                 onSecondaryPointerUp(ev);  
  116.                 mLastMotionX = MotionEventCompat.getX(ev,  
  117.                         MotionEventCompat.findPointerIndex(ev, mActivePointerId));  
  118.                 break;  
  119.         }  
  120.         if (needsInvalidate) {  
  121.             ViewCompat.postInvalidateOnAnimation(this);  
  122.         }  
  123.         return true;  
  124.     }  
代码C:void android.support.v4.view.ViewPager.onSecondaryPointerUp(MotionEvent ev)

  1. private void onSecondaryPointerUp(MotionEvent ev) {  
  2.         final int pointerIndex = MotionEventCompat.getActionIndex(ev);  
  3.         final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);  
  4.         if (pointerId == mActivePointerId) {  
  5.             // This was our active pointer going up. Choose a new  
  6.             // active pointer and adjust accordingly.  
  7.             final int newPointerIndex = pointerIndex == 0 ? 1 : 0;  
  8.             mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);  
  9.             mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);  
  10.             if (mVelocityTracker != null) {  
  11.                 mVelocityTracker.clear();  
  12.             }  
  13.         }  
  14.     }  

总结

在这里就不想上篇文章那样分各种情况去讨论了。那样有些无聊。这里就重要点的说明一下我的理解,最后附一个我写的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

版权声明:本文博主原创文章。转载请注明出处。

原文地址:https://www.cnblogs.com/mengfanrong/p/4758349.html