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 }
原理还不太清楚,只能先贴上代码,望各位大神指教,谢谢!