Touch事件分发源码解析

Posted wlrhnh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Touch事件分发源码解析相关的知识,希望对你有一定的参考价值。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
以下源码基于Gingerbread 2.3.7
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1、先看ViewGroup的dispatchOnTouchEvent(MotionEvent e)的源码

1.1 主要是获取一些坐标值,留备后用

 1     @Override
 2     public boolean dispatchTouchEvent(MotionEvent ev) {
 3         if (!onFilterTouchEventForSecurity(ev)) {
 4             return false;
 5         }
 6 
 7         final int action = ev.getAction();
 8         final float xf = ev.getX();
 9         final float yf = ev.getY();
10         final float scrolledXFloat = xf + mScrollX;
11         final float scrolledYFloat = yf + mScrollY;
12         final Rect frame = mTempRect;
 

1.2 先处理DOWN事件

 1         //TODO 1、判断是不是子View不允许老子拦截Touch事件
 2         boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
 3 
 4         if (action == MotionEvent.ACTION_DOWN) {
 5             if (mMotionTarget != null) {
 6                 // this is weird, we got a pen down, but we thought it was
 7                 // already down!
 8                 // XXX: We should probably send an ACTION_UP to the current
 9                 // target.
10                 mMotionTarget = null;
11             }
12             // If we‘re disallowing intercept or if we‘re allowing and we didn‘t
13             // intercept
14             //TODO 2、如果子View不允许,或者老子自己不想拦截,则进入这里
15             if (disallowIntercept || !onInterceptTouchEvent(ev)) {
16                 // reset this event‘s action (just to protect ourselves)
17                 ev.setAction(MotionEvent.ACTION_DOWN);
18                 // We know we want to dispatch the event down, find a child
19                 // who can handle it, start with the front-most child.
20                 final int scrolledXInt = (int) scrolledXFloat;
21                 final int scrolledYInt = (int) scrolledYFloat;
22                 final View[] children = mChildren;
23                 final int count = mChildrenCount;
24                 //TODO 3、根据用户落指的坐标,找到应该响应该Touch事件的子View或者子ViewGroup
25                 for (int i = count - 1; i >= 0; i--) {
26                     final View child = children[i];
27                     if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
28                             || child.getAnimation() != null) {
29                         child.getHitRect(frame);
30                         //TODO 4、很明显,根据范围来判断的
31                         if (frame.contains(scrolledXInt, scrolledYInt)) {
32                             // offset the event to the view‘s coordinate system
33                             final float xc = scrolledXFloat - child.mLeft;
34                             final float yc = scrolledYFloat - child.mTop;
35                             ev.setLocation(xc, yc);
36                             child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
37                             //TODO 5、找到子View,将事件交给它,如果它愿意消费DOWN事件,则记录下来这个龟儿子,并返回
39                             if (child.dispatchTouchEvent(ev))  {
40                                 // Event handled, we have a target now.
41                                 mMotionTarget = child;
42                                 return true;
43                             }
44                             // The event didn‘t get handled, try the next view.
45                             // Don‘t reset the event‘s location, it‘s not
46                             // necessary here.
47                         }
48                     }
49                 }
50             }
51         }

看见上面5条注释了吧,够用了

1.3 没找到子View、子View不愿消费(注释5)、或者不是DOWN事件,如下

 1         boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
 2                 (action == MotionEvent.ACTION_CANCEL);
 3 
 4         if (isUpOrCancel) {
 5             // Note, we‘ve already copied the previous state to our local
 6             // variable, so this takes effect on the next event
 7             mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
 8         }
 9 
10         // The event wasn‘t an ACTION_DOWN, dispatch it to our target if
11         // we have one.
12         // TODO 1、如果是没有子View,不管是DOWN还是其他事件,都交给自己来处理,包括后续的MOVE、UP事件。
13         //      因为这个方法有点类似递归调用,所以如果自己也不想处理,那么父类会分发给自己
14         final View target = mMotionTarget;
15         if (target == null) {
16             // We don‘t have a target, this means we‘re handling the
17             // event as a regular view.
18             ev.setLocation(xf, yf);
19             if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
20                 ev.setAction(MotionEvent.ACTION_CANCEL);
21                 mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
22             }
23             // TODO 2、调用View的dispatchTouchEvent,会调用到自己的onTouchEvent()方法
24             return super.dispatchTouchEvent(ev);
25         }

这段代码就一个意思,不管啥事件,没有子View,老子亲自处理~

1.4 找到子View,继续往下走

 1         // TODO 1、找到子View,子View让老子拦截,或者老子自己想拦截,给龟孙一个CANCEL,
 2         //      并告诉父ViewGroup,老子要了,而且将mMotionTarget置为null,这样后续事件直接走上面的判断,直接找老子处理
 3         // if have a target, see if we‘re allowed to and want to intercept its
 4         // events
 5         if (!disallowIntercept && onInterceptTouchEvent(ev)) {
 6             final float xc = scrolledXFloat - (float) target.mLeft;
 7             final float yc = scrolledYFloat - (float) target.mTop;
 8             mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
 9             ev.setAction(MotionEvent.ACTION_CANCEL);
10             ev.setLocation(xc, yc);
11             if (!target.dispatchTouchEvent(ev)) {
12                 // target didn‘t handle ACTION_CANCEL. not much we can do
13                 // but they should have.
14             }
15             // clear the target
16             mMotionTarget = null;
17             // Don‘t dispatch this event to our own view, because we already
18             // saw it when intercepting; we just want to give the following
19             // event to the normal onTouchEvent().
20             return true;
21         }
22 
23         // TODO 2、如果是UP、CANCEL事件,则清空
24         if (isUpOrCancel) {
25             mMotionTarget = null;
26         }

这段代码,龟孙之前说的好好的, 要自己消费事件,突然又不想了。或者老子突然想拦截了,那么老子自己拦截,自己处理

1.5 没有幺蛾子了,交给龟孙自己处理

 1         // finally offset the event to the target‘s coordinate system and
 2         // dispatch the event.
 3         final float xc = scrolledXFloat - (float) target.mLeft;
 4         final float yc = scrolledYFloat - (float) target.mTop;
 5         ev.setLocation(xc, yc);
 6 
 7         if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
 8             ev.setAction(MotionEvent.ACTION_CANCEL);
 9             target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
10             mMotionTarget = null;
11         }
12 
13         // TODO 1、不管前面这几行了,交给龟孙去处理
14         return target.dispatchTouchEvent(ev);

ViewGroup没有重写View的onTouchEvent()方法,onInterceptTouchEvent()返回false,默认不拦截

2、 View的相关方法

2.1 dispatchTouchEvent()方法

就一点点,调用自己的onTouchEvent()方法,并以onTouchEvent的返回值为返回值

 1     /**
 2      * Pass the touch screen motion event down to the target view, or this
 3      * view if it is the target.
 4      *
 5      * @param event The motion event to be dispatched.
 6      * @return True if the event was handled by the view, false otherwise.
 7      */
 8     public boolean dispatchTouchEvent(MotionEvent event) {
 9         if (!onFilterTouchEventForSecurity(event)) {
10             return false;
11         }
12 
13         if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
14                 mOnTouchListener.onTouch(this, event)) {
15             return true;
16         }
17         return onTouchEvent(event);
18     }

2.2 onTouchEvent()方法

这个方法比较复杂,不可点击的时候返回false,说明自己不处理。可点击的时候,根据不同的事件类型进行处理

  1     public boolean onTouchEvent(MotionEvent event) {
  2         final int viewFlags = mViewFlags;
  3 
  4         if ((viewFlags & ENABLED_MASK) == DISABLED) {
  5             // A disabled view that is clickable still consumes the touch
  6             // events, it just doesn‘t respond to them.
  7             return (((viewFlags & CLICKABLE) == CLICKABLE ||
  8                     (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
  9         }
 10 
 11         if (mTouchDelegate != null) {
 12             if (mTouchDelegate.onTouchEvent(event)) {
 13                 return true;
 14             }
 15         }
 16 
 17         if (((viewFlags & CLICKABLE) == CLICKABLE ||
 18                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
 19             switch (event.getAction()) {
 20                 case MotionEvent.ACTION_UP:
 21                     boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
 22                     if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
 23                         // take focus if we don‘t have it already and we should in
 24                         // touch mode.
 25                         boolean focusTaken = false;
 26                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
 27                             focusTaken = requestFocus();
 28                         }
 29 
 30                         if (!mHasPerformedLongPress) {
 31                             // This is a tap, so remove the longpress check
 32                             removeLongPressCallback();
 33 
 34                             // Only perform take click actions if we were in the pressed state
 35                             if (!focusTaken) {
 36                                 // Use a Runnable and post this rather than calling
 37                                 // performClick directly. This lets other visual state
 38                                 // of the view update before click actions start.
 39                                 if (mPerformClick == null) {
 40                                     mPerformClick = new PerformClick();
 41                                 }
 42                                 if (!post(mPerformClick)) {
 43                                     performClick();
 44                                 }
 45                             }
 46                         }
 47 
 48                         if (mUnsetPressedState == null) {
 49                             mUnsetPressedState = new UnsetPressedState();
 50                         }
 51 
 52                         if (prepressed) {
 53                             mPrivateFlags |= PRESSED;
 54                             refreshDrawableState();
 55                             postDelayed(mUnsetPressedState,
 56                                     ViewConfiguration.getPressedStateDuration());
 57                         } else if (!post(mUnsetPressedState)) {
 58                             // If the post failed, unpress right now
 59                             mUnsetPressedState.run();
 60                         }
 61                         removeTapCallback();
 62                     }
 63                     break;
 64 
 65                 case MotionEvent.ACTION_DOWN:
 66                     if (mPendingCheckForTap == null) {
 67                         mPendingCheckForTap = new CheckForTap();
 68                     }
 69                     mPrivateFlags |= PREPRESSED;
 70                     mHasPerformedLongPress = false;
 71                     postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
 72                     break;
 73 
 74                 case MotionEvent.ACTION_CANCEL:
 75                     mPrivateFlags &= ~PRESSED;
 76                     refreshDrawableState();
 77                     removeTapCallback();
 78                     break;
 79 
 80                 case MotionEvent.ACTION_MOVE:
 81                     final int x = (int) event.getX();
 82                     final int y = (int) event.getY();
 83 
 84                     // Be lenient about moving outside of buttons
 85                     int slop = mTouchSlop;
 86                     if ((x < 0 - slop) || (x >= getWidth() + slop) ||
 87                             (y < 0 - slop) || (y >= getHeight() + slop)) {
 88                         // Outside button
 89                         removeTapCallback();
 90                         if ((mPrivateFlags & PRESSED) != 0) {
 91                             // Remove any future long press/tap checks
 92                             removeLongPressCallback();
 93 
 94                             // Need to switch from pressed to not pressed
 95                             mPrivateFlags &= ~PRESSED;
 96                             refreshDrawableState();
 97                         }
 98                     }
 99                     break;
100             }
101             return true;
102         }
103 
104         return false;
105     }

 

 
  

以上是关于Touch事件分发源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Android Touch事件分发(源码分析)

Android Touch事件分发(源码分析)

事件分发机制之 源码解析

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

源码阅读分析 - View的Touch事件分发

View事件分发机制(源码分析篇)