zoukankan      html  css  js  c++  java
  • 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     }

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

    我的GitHub:https://github.com/lelelongwang
  • 相关阅读:
    九月腾讯,创新工场,淘宝等公司最新面试三十题
    java静态变量和实例变量的区别
    海量数据处理:十道面试题与十个海量数据处理方法总结
    持有对象(看think in java)
    在myeclipse9.0中安装插件SVN(掌握通用安装插件的方法)
    java的垃圾回收机制(think in java学习总结):
    CSS控制文本自动换行
    jquery获得select option的值 和对select option的操作
    JS操作table!js table行数
    jquery ui datepicker 只能选今天以后的日期
  • 原文地址:https://www.cnblogs.com/longjunhao/p/4219529.html
Copyright © 2011-2022 走看看