Android L(5.0)源码之手势识别onTouchEvent

onTouchEvent同样也是在view中定义的一个方法。处理传递到view 的手势事件。通过MotionEvent的getAction()方法来获取Touch事件的类型,类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件。其中ACTION_DOWN是指按下触摸屏,ACTION_MOVE是指按下触摸屏后移动受力点,ACTION_UP则是指松 开触摸屏,ACTION_CANCEL不会由用户直接触发。

贴上View.onTouchEvent的android 5.0源代码

  1     public boolean onTouchEvent(MotionEvent event) {
  2         final float x = event.getX();
  3         final float y = event.getY();
  4         final int viewFlags = mViewFlags;
  5 
  6         if ((viewFlags & ENABLED_MASK) == DISABLED) {
  7             if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
  8                 setPressed(false);
  9             }
 10             // A disabled view that is clickable still consumes the touch
 11             // events, it just doesn't respond to them.
 12             return (((viewFlags & CLICKABLE) == CLICKABLE ||
 13                     (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
 14         }
 15 
 16         if (mTouchDelegate != null) {
 17             if (mTouchDelegate.onTouchEvent(event)) {
 18                 return true;
 19             }
 20         }
 21 
 22         if (((viewFlags & CLICKABLE) == CLICKABLE ||
 23                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
 24             switch (event.getAction()) {
 25                 case MotionEvent.ACTION_UP:
 26                     boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
 27                     if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
 28                         // take focus if we don't have it already and we should in
 29                         // touch mode.
 30                         boolean focusTaken = false;
 31                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
 32                             focusTaken = requestFocus();
 33                         }
 34 
 35                         if (prepressed) {
 36                             // The button is being released before we actually
 37                             // showed it as pressed.  Make it show the pressed
 38                             // state now (before scheduling the click) to ensure
 39                             // the user sees it.
 40                             setPressed(true, x, y);
 41                        }
 42 
 43                         if (!mHasPerformedLongPress) {
 44                             // This is a tap, so remove the longpress check
 45                             removeLongPressCallback();
 46 
 47                             // Only perform take click actions if we were in the pressed state
 48                             if (!focusTaken) {
 49                                 // Use a Runnable and post this rather than calling
 50                                 // performClick directly. This lets other visual state
 51                                 // of the view update before click actions start.
 52                                 if (mPerformClick == null) {
 53                                     mPerformClick = new PerformClick();
 54                                 }
 55                                 if (!post(mPerformClick)) {
 56                                     performClick();
 57                                 }
 58                             }
 59                         }
 60 
 61                         if (mUnsetPressedState == null) {
 62                             mUnsetPressedState = new UnsetPressedState();
 63                         }
 64 
 65                         if (prepressed) {
 66                             postDelayed(mUnsetPressedState,
 67                                     ViewConfiguration.getPressedStateDuration());
 68                         } else if (!post(mUnsetPressedState)) {
 69                             // If the post failed, unpress right now
 70                             mUnsetPressedState.run();
 71                         }
 72 
 73                         removeTapCallback();
 74                     }
 75                     break;
 76 
 77                 case MotionEvent.ACTION_DOWN:
 78                     mHasPerformedLongPress = false;
 79 
 80                     if (performButtonActionOnTouchDown(event)) {
 81                         break;
 82                     }
 83 
 84                     // Walk up the hierarchy to determine if we're inside a scrolling container.
 85                     boolean isInScrollingContainer = isInScrollingContainer();
 86 
 87                     // For views inside a scrolling container, delay the pressed feedback for
 88                     // a short period in case this is a scroll.
 89                     if (isInScrollingContainer) {
 90                         mPrivateFlags |= PFLAG_PREPRESSED;
 91                         if (mPendingCheckForTap == null) {
 92                             mPendingCheckForTap = new CheckForTap();
 93                         }
 94                         mPendingCheckForTap.x = event.getX();
 95                         mPendingCheckForTap.y = event.getY();
 96                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
 97                     } else {
 98                         // Not inside a scrolling container, so show the feedback right away
 99                         setPressed(true, x, y);
100                         checkForLongClick(0);
101                     }
102                     break;
103 
104                 case MotionEvent.ACTION_CANCEL:
105                     setPressed(false);
106                     removeTapCallback();
107                     removeLongPressCallback();
108                     break;
109 
110                 case MotionEvent.ACTION_MOVE:
111                     drawableHotspotChanged(x, y);
112 
113                     // Be lenient about moving outside of buttons
114                     if (!pointInView(x, y, mTouchSlop)) {
115                         // Outside button
116                         removeTapCallback();
117                         if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
118                             // Remove any future long press/tap checks
119                             removeLongPressCallback();
120 
121                             setPressed(false);
122                         }
123                     }
124                     break;
125             }
126 
127             return true;
128         }
129 
130         return false;
131     }

 随后,在onTouch()方法中,我们调用GestureDetector的onTouchEvent()方法,将捕捉到的MotionEvent交给 GestureDetector 来分析是否有合适的callback函数来处理用户的手势。

贴上GestureDetector.onTouchEvent的android 5.0的源码:

  1     public boolean onTouchEvent(MotionEvent ev) {
  2         if (mInputEventConsistencyVerifier != null) {
  3             mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
  4         }
  5 
  6         final int action = ev.getAction();
  7 
  8         if (mVelocityTracker == null) {
  9             mVelocityTracker = VelocityTracker.obtain();
 10         }
 11         mVelocityTracker.addMovement(ev);
 12 
 13         final boolean pointerUp =
 14                 (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP;
 15         final int skipIndex = pointerUp ? ev.getActionIndex() : -1;
 16 
 17         // Determine focal point
 18         float sumX = 0, sumY = 0;
 19         final int count = ev.getPointerCount();
 20         for (int i = 0; i < count; i++) {
 21             if (skipIndex == i) continue;
 22             sumX += ev.getX(i);
 23             sumY += ev.getY(i);
 24         }
 25         final int div = pointerUp ? count - 1 : count;
 26         final float focusX = sumX / div;
 27         final float focusY = sumY / div;
 28 
 29         boolean handled = false;
 30 
 31         switch (action & MotionEvent.ACTION_MASK) {
 32         case MotionEvent.ACTION_POINTER_DOWN:
 33             mDownFocusX = mLastFocusX = focusX;
 34             mDownFocusY = mLastFocusY = focusY;
 35             // Cancel long press and taps
 36             cancelTaps();
 37             break;
 38 
 39         case MotionEvent.ACTION_POINTER_UP:
 40             mDownFocusX = mLastFocusX = focusX;
 41             mDownFocusY = mLastFocusY = focusY;
 42 
 43             // Check the dot product of current velocities.
 44             // If the pointer that left was opposing another velocity vector, clear.
 45             mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
 46             final int upIndex = ev.getActionIndex();
 47             final int id1 = ev.getPointerId(upIndex);
 48             final float x1 = mVelocityTracker.getXVelocity(id1);
 49             final float y1 = mVelocityTracker.getYVelocity(id1);
 50             for (int i = 0; i < count; i++) {
 51                 if (i == upIndex) continue;
 52 
 53                 final int id2 = ev.getPointerId(i);
 54                 final float x = x1 * mVelocityTracker.getXVelocity(id2);
 55                 final float y = y1 * mVelocityTracker.getYVelocity(id2);
 56 
 57                 final float dot = x + y;
 58                 if (dot < 0) {
 59                     mVelocityTracker.clear();
 60                     break;
 61                 }
 62             }
 63             break;
 64 
 65         case MotionEvent.ACTION_DOWN:
 66             if (mDoubleTapListener != null) {
 67                 boolean hadTapMessage = mHandler.hasMessages(TAP);
 68                 if (hadTapMessage) mHandler.removeMessages(TAP);
 69                 if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
 70                         isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
 71                     // This is a second tap
 72                     mIsDoubleTapping = true;
 73                     // Give a callback with the first tap of the double-tap
 74                     handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
 75                     // Give a callback with down event of the double-tap
 76                     handled |= mDoubleTapListener.onDoubleTapEvent(ev);
 77                 } else {
 78                     // This is a first tap
 79                     mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
 80                 }
 81             }
 82 
 83             mDownFocusX = mLastFocusX = focusX;
 84             mDownFocusY = mLastFocusY = focusY;
 85             if (mCurrentDownEvent != null) {
 86                 mCurrentDownEvent.recycle();
 87             }
 88             mCurrentDownEvent = MotionEvent.obtain(ev);
 89             mAlwaysInTapRegion = true;
 90             mAlwaysInBiggerTapRegion = true;
 91             mStillDown = true;
 92             mInLongPress = false;
 93             mDeferConfirmSingleTap = false;
 94             
 95             if (mIsLongpressEnabled) {
 96                 mHandler.removeMessages(LONG_PRESS);
 97                 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
 98                         + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
 99             }
100             mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
101             handled |= mListener.onDown(ev);
102             break;
103 
104         case MotionEvent.ACTION_MOVE:
105             if (mInLongPress) {
106                 break;
107             }
108             final float scrollX = mLastFocusX - focusX;
109             final float scrollY = mLastFocusY - focusY;
110             if (mIsDoubleTapping) {
111                 // Give the move events of the double-tap
112                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
113             } else if (mAlwaysInTapRegion) {
114                 final int deltaX = (int) (focusX - mDownFocusX);
115                 final int deltaY = (int) (focusY - mDownFocusY);
116                 int distance = (deltaX * deltaX) + (deltaY * deltaY);
117                 if (distance > mTouchSlopSquare) {
118                     handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
119                     mLastFocusX = focusX;
120                     mLastFocusY = focusY;
121                     mAlwaysInTapRegion = false;
122                     mHandler.removeMessages(TAP);
123                     mHandler.removeMessages(SHOW_PRESS);
124                     mHandler.removeMessages(LONG_PRESS);
125                 }
126                 if (distance > mDoubleTapTouchSlopSquare) {
127                     mAlwaysInBiggerTapRegion = false;
128                 }
129             } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
130                 handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
131                 mLastFocusX = focusX;
132                 mLastFocusY = focusY;
133             }
134             break;
135 
136         case MotionEvent.ACTION_UP:
137             mStillDown = false;
138             MotionEvent currentUpEvent = MotionEvent.obtain(ev);
139             if (mIsDoubleTapping) {
140                 // Finally, give the up event of the double-tap
141                 handled |= mDoubleTapListener.onDoubleTapEvent(ev);
142             } else if (mInLongPress) {
143                 mHandler.removeMessages(TAP);
144                 mInLongPress = false;
145             } else if (mAlwaysInTapRegion) {
146                 handled = mListener.onSingleTapUp(ev);
147                 if (mDeferConfirmSingleTap && mDoubleTapListener != null) {
148                     mDoubleTapListener.onSingleTapConfirmed(ev);
149                 }
150             } else {
151 
152                 // A fling must travel the minimum tap distance
153                 final VelocityTracker velocityTracker = mVelocityTracker;
154                 final int pointerId = ev.getPointerId(0);
155                 velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
156                 final float velocityY = velocityTracker.getYVelocity(pointerId);
157                 final float velocityX = velocityTracker.getXVelocity(pointerId);
158 
159                 if ((Math.abs(velocityY) > mMinimumFlingVelocity)
160                         || (Math.abs(velocityX) > mMinimumFlingVelocity)){
161                     handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY);
162                 }
163             }
164             if (mPreviousUpEvent != null) {
165                 mPreviousUpEvent.recycle();
166             }
167             // Hold the event we obtained above - listeners may have changed the original.
168             mPreviousUpEvent = currentUpEvent;
169             if (mVelocityTracker != null) {
170                 // This may have been cleared when we called out to the
171                 // application above.
172                 mVelocityTracker.recycle();
173                 mVelocityTracker = null;
174             }
175             mIsDoubleTapping = false;
176             mDeferConfirmSingleTap = false;
177             mHandler.removeMessages(SHOW_PRESS);
178             mHandler.removeMessages(LONG_PRESS);
179             break;
180 
181         case MotionEvent.ACTION_CANCEL:
182             cancel();
183             break;
184         }
185 
186         if (!handled && mInputEventConsistencyVerifier != null) {
187             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0);
188         }
189         return handled;
190     }

原理还不太清楚,只能先贴上代码,望各位大神指教,谢谢!

posted @ 2015-01-12 19:33  lelelong  阅读(445)  评论(0编辑  收藏  举报