public boolean onTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null ) { mInputEventConsistencyVerifier.onTouchEvent(ev, 0 ); //View中都会调用的方法,用来处理污染,防止多次报告相同的问题,也就是先做过滤处理。 } final int action = ev.getAction(); if (mCurrentMotionEvent != null ) { mCurrentMotionEvent.recycle(); //回收上次事件 } mCurrentMotionEvent = MotionEvent.obtain(ev); //保存本次事件 if (mVelocityTracker == null ) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); // 以下是计算手势的算法,用于判断手势行为 final boolean pointerUp = (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP; final int skipIndex = pointerUp ? ev.getActionIndex() : - 1 ; final boolean isGeneratedGesture = (ev.getFlags() & MotionEvent.FLAG_IS_GENERATED_GESTURE) != 0 ; // Determine focal point float sumX = 0 , sumY = 0 ; final int count = ev.getPointerCount(); for ( int i = 0 ; i < count; i++) { if (skipIndex == i) continue ; sumX += ev.getX(i); sumY += ev.getY(i); } final int div = pointerUp ? count - 1 : count; final float focusX = sumX / div; final float focusY = sumY / div; boolean handled = false ; // 此处进行事件处理 switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_DOWN: //单手Down mDownFocusX = mLastFocusX = focusX; // 记录初始位置 mDownFocusY = mLastFocusY = focusY; // Cancel long press and taps cancelTaps(); //移除手势Handler中之前保存的状态 break ; case MotionEvent.ACTION_POINTER_UP: // 单手Up mDownFocusX = mLastFocusX = focusX; mDownFocusY = mLastFocusY = focusY; // Check the dot product of current velocities. // If the pointer that left was opposing another velocity vector, clear. mVelocityTracker.computeCurrentVelocity( 1000 , mMaximumFlingVelocity); final int upIndex = ev.getActionIndex(); final int id1 = ev.getPointerId(upIndex); final float x1 = mVelocityTracker.getXVelocity(id1); final float y1 = mVelocityTracker.getYVelocity(id1); for ( int i = 0 ; i < count; i++) { if (i == upIndex) continue ; final int id2 = ev.getPointerId(i); final float x = x1 * mVelocityTracker.getXVelocity(id2); final float y = y1 * mVelocityTracker.getYVelocity(id2); final float dot = x + y; if (dot < 0 ) { mVelocityTracker.clear(); break ; } } break ; case MotionEvent.ACTION_DOWN: if (mDoubleTapListener != null ) { boolean hadTapMessage = mHandler.hasMessages(TAP); // 在300ms之前是否已经发送过TAP消息,此处用来判断是否产生双击事件 if (hadTapMessage) mHandler.removeMessages(TAP); //表示之前已经Down过,然后移除发送的TAP,也就是不在Handler中进行单机事件回调 if ((mCurrentDownEvent != null ) && (mPreviousUpEvent != null ) && hadTapMessage && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // This is a second tap mIsDoubleTapping = true ; recordGestureClassification( TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); // Give a callback with the first tap of the double-tap handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); // 回调双击事件 // Give a callback with down event of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); // 回调双击事件 } else { // This is a first tap mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); // 单击,发送给Handler TAP记录此次事件,消息延迟为300ms } } mDownFocusX = mLastFocusX = focusX; mDownFocusY = mLastFocusY = focusY; if (mCurrentDownEvent != null ) { mCurrentDownEvent.recycle(); //回收事件, } mCurrentDownEvent = MotionEvent.obtain(ev); //创建一个新的 MotionEvent,从现有的复制,native实现 mAlwaysInTapRegion = true ; mAlwaysInBiggerTapRegion = true ; mStillDown = true ; mInLongPress = false ; mDeferConfirmSingleTap = false ; mHasRecordedClassification = false ; if (mIsLongpressEnabled) { //判断是否长按Enable mHandler.removeMessages(LONG_PRESS); mHandler.sendMessageAtTime( // 长按一定时间才发送 长按消息 mHandler.obtainMessage( LONG_PRESS, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS, 0 /* arg2 */ ), mCurrentDownEvent.getDownTime() + ViewConfiguration.getLongPressTimeout()); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); handled |= mListener.onDown(ev); // Down事件最后消耗 break ; case MotionEvent.ACTION_MOVE: if (mInLongPress || mInContextClick) { break ; } // 这个case用来处理滚动事件 final int motionClassification = ev.getClassification(); final boolean hasPendingLongPress = mHandler.hasMessages(LONG_PRESS); final float scrollX = mLastFocusX - focusX; final float scrollY = mLastFocusY - focusY; if (mIsDoubleTapping) { // Give the move events of the double-tap recordGestureClassification( TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mAlwaysInTapRegion) { final int deltaX = ( int ) (focusX - mDownFocusX); final int deltaY = ( int ) (focusY - mDownFocusY); int distance = (deltaX * deltaX) + (deltaY * deltaY); int slopSquare = isGeneratedGesture ? 0 : mTouchSlopSquare; final boolean ambiguousGesture = motionClassification == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE; final boolean shouldInhibitDefaultAction = hasPendingLongPress && ambiguousGesture; if (shouldInhibitDefaultAction) { // Inhibit default long press if (distance > slopSquare) { // The default action here is to remove long press. But if the touch // slop below gets increased, and we never exceed the modified touch // slop while still receiving AMBIGUOUS_GESTURE, we risk that *nothing* // will happen in response to user input. To prevent this, // reschedule long press with a modified timeout. mHandler.removeMessages(LONG_PRESS); final long longPressTimeout = ViewConfiguration.getLongPressTimeout(); mHandler.sendMessageAtTime( mHandler.obtainMessage( LONG_PRESS, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS, 0 /* arg2 */ ), ev.getDownTime() + ( long ) (longPressTimeout * mAmbiguousGestureMultiplier)); } // Inhibit default scroll. If a gesture is ambiguous, we prevent scroll // until the gesture is resolved. // However, for safety, simply increase the touch slop in case the // classification is erroneous. Since the value is squared, multiply twice. slopSquare *= mAmbiguousGestureMultiplier * mAmbiguousGestureMultiplier; } if (distance > slopSquare) { recordGestureClassification( TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL); handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; mAlwaysInTapRegion = false ; mHandler.removeMessages(TAP); mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); } int doubleTapSlopSquare = isGeneratedGesture ? 0 : mDoubleTapTouchSlopSquare; if (distance > doubleTapSlopSquare) { mAlwaysInBiggerTapRegion = false ; } } else if ((Math.abs(scrollX) >= 1 ) || (Math.abs(scrollY) >= 1 )) { recordGestureClassification(TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SCROLL); handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; } final boolean deepPress = motionClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS; if (deepPress && hasPendingLongPress) { mHandler.removeMessages(LONG_PRESS); mHandler.sendMessage( mHandler.obtainMessage( LONG_PRESS, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS, 0 /* arg2 */ )); } break ; case MotionEvent.ACTION_UP: mStillDown = false ; MotionEvent currentUpEvent = MotionEvent.obtain(ev); if (mIsDoubleTapping) { // Finally, give the up event of the double-tap recordGestureClassification( TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DOUBLE_TAP); handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mInLongPress) { mHandler.removeMessages(TAP); mInLongPress = false ; } else if (mAlwaysInTapRegion && !mIgnoreNextUpEvent) { recordGestureClassification( TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP); handled = mListener.onSingleTapUp(ev); // 回调单击 down->up if (mDeferConfirmSingleTap && mDoubleTapListener != null ) { mDoubleTapListener.onSingleTapConfirmed(ev); } } else if (!mIgnoreNextUpEvent) { // A fling must travel the minimum tap distance final VelocityTracker velocityTracker = mVelocityTracker; final int pointerId = ev.getPointerId( 0 ); velocityTracker.computeCurrentVelocity( 1000 , mMaximumFlingVelocity); final float velocityY = velocityTracker.getYVelocity(pointerId); final float velocityX = velocityTracker.getXVelocity(pointerId); if ((Math.abs(velocityY) > mMinimumFlingVelocity) || (Math.abs(velocityX) > mMinimumFlingVelocity)) { handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY); } } if (mPreviousUpEvent != null ) { mPreviousUpEvent.recycle(); } // Hold the event we obtained above - listeners may have changed the original. mPreviousUpEvent = currentUpEvent; if (mVelocityTracker != null ) { // This may have been cleared when we called out to the // application above. mVelocityTracker.recycle(); mVelocityTracker = null ; } mIsDoubleTapping = false ; mDeferConfirmSingleTap = false ; mIgnoreNextUpEvent = false ; mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); break ; case MotionEvent.ACTION_CANCEL: cancel(); break ; } if (!handled && mInputEventConsistencyVerifier != null ) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0 ); } return handled; } |