2018-05-31 17:23:46
Note: 这里的源码来自Android 2.3.6,这个版本的代码比较简单,适合理解Touch事件的传递原理。后续版本源码复杂了很多,但是原理都是类似的。
2个方法源码较多,在这里记录下。
View.java
1 /** 2 * Implement this method to handle touch screen motion events. 3 * 4 * @param event The motion event. 5 * @return True if the event was handled, false otherwise. 6 */ 7 public boolean onTouchEvent(MotionEvent event) { 8 final int viewFlags = mViewFlags; 9 10 if ((viewFlags & ENABLED_MASK) == DISABLED) { 11 // A disabled view that is clickable still consumes the touch 12 // events, it just doesn't respond to them. 13 return (((viewFlags & CLICKABLE) == CLICKABLE || 14 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); 15 } 16 17 if (mTouchDelegate != null) { 18 if (mTouchDelegate.onTouchEvent(event)) { 19 return true; 20 } 21 } 22 23 if (((viewFlags & CLICKABLE) == CLICKABLE || 24 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { 25 switch (event.getAction()) { 26 case MotionEvent.ACTION_UP: 27 boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; 28 if ((mPrivateFlags & PRESSED) != 0 || prepressed) { 29 // take focus if we don't have it already and we should in 30 // touch mode. 31 boolean focusTaken = false; 32 if (isFocusable() && isFocusableInTouchMode() && !isFocused()) { 33 focusTaken = requestFocus(); 34 } 35 36 if (!mHasPerformedLongPress) { 37 // This is a tap, so remove the longpress check 38 removeLongPressCallback(); 39 40 // Only perform take click actions if we were in the pressed state 41 if (!focusTaken) { 42 // Use a Runnable and post this rather than calling 43 // performClick directly. This lets other visual state 44 // of the view update before click actions start. 45 if (mPerformClick == null) { 46 mPerformClick = new PerformClick(); 47 } 48 if (!post(mPerformClick)) { 49 performClick(); 50 } 51 } 52 } 53 54 if (mUnsetPressedState == null) { 55 mUnsetPressedState = new UnsetPressedState(); 56 } 57 58 if (prepressed) { 59 mPrivateFlags |= PRESSED; 60 refreshDrawableState(); 61 postDelayed(mUnsetPressedState, 62 ViewConfiguration.getPressedStateDuration()); 63 } else if (!post(mUnsetPressedState)) { 64 // If the post failed, unpress right now 65 mUnsetPressedState.run(); 66 } 67 removeTapCallback(); 68 } 69 break; 70 71 case MotionEvent.ACTION_DOWN: 72 if (mPendingCheckForTap == null) { 73 mPendingCheckForTap = new CheckForTap(); 74 } 75 mPrivateFlags |= PREPRESSED; 76 mHasPerformedLongPress = false; 77 postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); 78 break; 79 80 case MotionEvent.ACTION_CANCEL: 81 mPrivateFlags &= ~PRESSED; 82 refreshDrawableState(); 83 removeTapCallback(); 84 break; 85 86 case MotionEvent.ACTION_MOVE: 87 final int x = (int) event.getX(); 88 final int y = (int) event.getY(); 89 90 // Be lenient about moving outside of buttons 91 int slop = mTouchSlop; 92 if ((x < 0 - slop) || (x >= getWidth() + slop) || 93 (y < 0 - slop) || (y >= getHeight() + slop)) { 94 // Outside button 95 removeTapCallback(); 96 if ((mPrivateFlags & PRESSED) != 0) { 97 // Remove any future long press/tap checks 98 removeLongPressCallback(); 99 100 // Need to switch from pressed to not pressed 101 mPrivateFlags &= ~PRESSED; 102 refreshDrawableState(); 103 } 104 } 105 break; 106 } 107 return true; 108 } 109 110 return false; 111 }
ViewGroup.java
1 /** 2 * {@inheritDoc} 3 */ 4 @Override 5 public boolean dispatchTouchEvent(MotionEvent ev) { 6 if (!onFilterTouchEventForSecurity(ev)) { 7 return false; 8 } 9 10 final int action = ev.getAction(); 11 final float xf = ev.getX(); 12 final float yf = ev.getY(); 13 final float scrolledXFloat = xf + mScrollX; 14 final float scrolledYFloat = yf + mScrollY; 15 final Rect frame = mTempRect; 16 17 boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; 18 19 if (action == MotionEvent.ACTION_DOWN) { 20 if (mMotionTarget != null) { 21 // this is weird, we got a pen down, but we thought it was 22 // already down! 23 // XXX: We should probably send an ACTION_UP to the current 24 // target. 25 mMotionTarget = null; 26 } 27 // If we're disallowing intercept or if we're allowing and we didn't 28 // intercept 29 if (disallowIntercept || !onInterceptTouchEvent(ev)) { 30 // reset this event's action (just to protect ourselves) 31 ev.setAction(MotionEvent.ACTION_DOWN); 32 // We know we want to dispatch the event down, find a child 33 // who can handle it, start with the front-most child. 34 final int scrolledXInt = (int) scrolledXFloat; 35 final int scrolledYInt = (int) scrolledYFloat; 36 final View[] children = mChildren; 37 final int count = mChildrenCount; 38 39 for (int i = count - 1; i >= 0; i--) { 40 final View child = children[i]; 41 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE 42 || child.getAnimation() != null) { 43 child.getHitRect(frame); 44 if (frame.contains(scrolledXInt, scrolledYInt)) { 45 // offset the event to the view's coordinate system 46 final float xc = scrolledXFloat - child.mLeft; 47 final float yc = scrolledYFloat - child.mTop; 48 ev.setLocation(xc, yc); 49 child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 50 if (child.dispatchTouchEvent(ev)) { 51 // Event handled, we have a target now. 52 mMotionTarget = child; 53 return true; 54 } 55 // The event didn't get handled, try the next view. 56 // Don't reset the event's location, it's not 57 // necessary here. 58 } 59 } 60 } 61 } 62 } 63 64 boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || 65 (action == MotionEvent.ACTION_CANCEL); 66 67 if (isUpOrCancel) { 68 // Note, we've already copied the previous state to our local 69 // variable, so this takes effect on the next event 70 mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; 71 } 72 73 // The event wasn't an ACTION_DOWN, dispatch it to our target if 74 // we have one. 75 final View target = mMotionTarget; 76 if (target == null) { 77 // We don't have a target, this means we're handling the 78 // event as a regular view. 79 ev.setLocation(xf, yf); 80 if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { 81 ev.setAction(MotionEvent.ACTION_CANCEL); 82 mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 83 } 84 return super.dispatchTouchEvent(ev); 85 } 86 87 // if have a target, see if we're allowed to and want to intercept its 88 // events 89 if (!disallowIntercept && onInterceptTouchEvent(ev)) { 90 final float xc = scrolledXFloat - (float) target.mLeft; 91 final float yc = scrolledYFloat - (float) target.mTop; 92 mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 93 ev.setAction(MotionEvent.ACTION_CANCEL); 94 ev.setLocation(xc, yc); 95 if (!target.dispatchTouchEvent(ev)) { 96 // target didn't handle ACTION_CANCEL. not much we can do 97 // but they should have. 98 } 99 // clear the target 100 mMotionTarget = null; 101 // Don't dispatch this event to our own view, because we already 102 // saw it when intercepting; we just want to give the following 103 // event to the normal onTouchEvent(). 104 return true; 105 } 106 107 if (isUpOrCancel) { 108 mMotionTarget = null; 109 } 110 111 // finally offset the event to the target's coordinate system and 112 // dispatch the event. 113 final float xc = scrolledXFloat - (float) target.mLeft; 114 final float yc = scrolledYFloat - (float) target.mTop; 115 ev.setLocation(xc, yc); 116 117 if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { 118 ev.setAction(MotionEvent.ACTION_CANCEL); 119 target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; 120 mMotionTarget = null; 121 } 122 123 return target.dispatchTouchEvent(ev); 124 }