The essence of the View event distribution mechanism is the distribution process of the MotionEvent event, that is, how the MotionEvent is passed and processed between the Views after it is generated.
First, let’s introduce what MotionEvent is. The so-called MotionEvent is a series of touch events generated when a user touches the screen of a mobile phone with his finger. Typical touch events are:
- ACTION_DOWN: The moment your finger touches the screen.
- ACTION_MOVE: Slide your fingers on the screen.
- ACTION_UP: The moment when your finger leaves the screen.
- ACTION_CANCLE: The current event sequence terminates.
An event sequence usually starts with a DOWN event, a UP event terminates, and several MOVE events are interspersed in the middle.
The order of events passed: Activity(Window) → ViewGroup → View, that is, events are passed down from Activity.
There are three main methods involved in the distribution of events:
- dispatchTouchEvent: Pass events from top to bottom. Its return value is affected by the dispatchTouchEvent method of the child View and the onTouchEvent method of the current View.
- onInterceptTouchEvent: Intercepts events. This method is unique to ViewGroup. Once an event in the event sequence is intercepted, the remaining events in the sequence will be handed over to the intercepted ViewGroup for processing, and this method will not be called again.
- onTouchEvent: consumes an event, that is, handles a certain event.
Next, the event distribution mechanisms of Activity, ViewGroup, and View will be explained separately.
Activity's event distribution mechanism
When a click event occurs, the event is first passed to the Activity dispatchTouchEvent() method for processing.
Activity will call getWindow().superDispatchTouchEvent() method in the dispatchTouchEvent() method to pass the event to the Window's mDecor(DecorView) for processing, and mDecor will pass the event to the ViewGroup for processing by calling the superDispatchTouchEvent method.
/** * Source code analysis: () */ public boolean dispatchTouchEvent(MotionEvent ev) { if (() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; // If getWindow().superDispatchTouchEvent(ev) returns true // Then () returns true, and the method ends. That is: the click event stops passing down and the event delivery process ends // Otherwise: Continue to call down } return onTouchEvent(ev); } /** * getWindow().superDispatchTouchEvent(ev) * illustrate: * a. getWindow() = Get the object of the Window class * b. The Window class is an abstract class, and its only implementation class = PhoneWindow class; that is, the Window class object here = PhoneWindow class object * c. SuperDispatchTouchEvent() of Window class = 1 abstract method implemented by subclass PhoneWindow class */ @Override public boolean superDispatchTouchEvent(MotionEvent event) { return (event); // mDecor = instance object of top view (DecorView) } /** * (event) * Definition: belongs to the top view (DecorView) * illustrate: * a. The DecorView class is an internal class of the PhoneWindow class * b. DecorView inherits from FrameLayout and is the parent class of all interfaces. * c. FrameLayout is a subclass of ViewGroup, so the indirect parent class of DecorView = ViewGroup */ public boolean superDispatchTouchEvent(MotionEvent event) { return (event); // Call the parent class method = ViewGroup's dispatchTouchEvent() // That is, pass events to ViewGroup for processing. For details, please see the event distribution mechanism of ViewGroup } /** * () * Definition: belongs to the top view (DecorView) * illustrate: * a. The DecorView class is an internal class of the PhoneWindow class * b. DecorView inherits from FrameLayout and is the parent class of all interfaces. * c. FrameLayout is a subclass of ViewGroup, so the indirect parent class of DecorView = ViewGroup */ public boolean onTouchEvent(MotionEvent event) { // When a click event is not received/processed by any View under Activity // Application scenario: Handle touch events that occur outside the Window boundary if ((this, event)) { finish(); return true; } return false; // That is, true will be returned only if the click event is outside the Window boundary, and false will be returned in general. }
ViewGroup event distribution mechanism
When the event is passed from the Activity to the dispatchTouchEvent() of the ViewGroup, the ViewGroup will first call the onInterceptTouchEvent() method to determine whether to intercept the event (the default is not intercepted, and the user needs to rewrite it if it is intercepted). If the event is not intercepted, the ViewGroup will traverse all its subViews through a for loop, find the View where the current event occurs, and then call the dispatchTouchEvent() method of the subView to distribute the event to the subView for processing.
If the event is intercepted by ViewGroup or the View where the event occurs is not found (the event occurs in a blank space), ViewGroup will call its onTouchEvent() method to handle the event.
/** * Source code analysis: () */ public boolean dispatchTouchEvent(MotionEvent ev) { ... // Only key codes are posted // Every time ViewGroup is distributed, it needs to call onInterceptTouchEvent() to ask whether to intercept the event if (disallowIntercept || !onInterceptTouchEvent(ev)) { // Determine value 1: disallowIntercept = Whether to disable event interception function (default is false), it can be modified by calling requestDisallowInterceptTouchEvent() // Determine value 2: !onInterceptTouchEvent(ev) = Inverse the return value of onInterceptTouchEvent() // a. If false is returned in onInterceptTouchEvent() (that is, no event is intercepted), the second value will be true, thus entering the internal conditional judgment // b. If true is returned in onInterceptTouchEvent() (i.e., intercept event), the second value will be false, thus breaking out of this conditional judgment // c. About onInterceptTouchEvent() ->>Analysis 1 (MotionEvent.ACTION_DOWN); final int scrolledXInt = (int) scrolledXFloat; final int scrolledYInt = (int) scrolledYFloat; final View[] children = mChildren; final int count = mChildrenCount; // Through a for loop, iterates through all subViews under the current ViewGroup for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if (( & VISIBILITY_MASK) == VISIBLE || () != null) { (frame); // Determine whether the currently traversed View is the view being clicked, so as to find the currently clicked View // If so, enter the conditional judgment internally if ((scrolledXInt, scrolledYInt)) { final float xc = scrolledXFloat - ; final float yc = scrolledYFloat - ; (xc, yc); &= ~CANCEL_NEXT_UP_EVENT; // The internal call of the dispatchTouchEvent() of the View is called within the conditional judgment // That is, the click event is passed from ViewGroup to subView (for details, please see the View event distribution mechanism below) if ((ev)) { mMotionTarget = child; return true; // There is a return value after calling the dispatchTouchEvent of the child View // If the control is clickable, then when clicked, the return value of dispatchTouchEvent must be true, which will cause the conditional judgment to be true. // So I directly return true to the dispatchTouchEvent() of ViewGroup, that is, I will jump out directly // That is, block the ViewGroup click event } } } } } } boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } final View target = mMotionTarget; // If you click on a blank (that is, no View reception event) / Intercept event (manually copy onInterceptTouchEvent() to return true) if (target == null) { (xf, yf); if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { (MotionEvent.ACTION_CANCEL); mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; } return (ev); // Call the dispatchTouchEvent() of the ViewGroup parent class, that is, () // Therefore, the ViewGroup's onTouch() ->> onTouchEvent() ->> performClick() ->> onClick() will be executed, that is, the event will not be passed down (for details, please refer to the () in the distribution mechanism of the View event) // Here we need to be different from the above: dispatchTouchEvent() of the subview } ... } /** * () * Function: Whether to intercept events * illustrate: * a. Return true = Intercept, that is, the event stops being passed down (it needs to be manually set, that is, rewrite onInterceptTouchEvent() to return true) * b. Return false = No intercept (default) */ public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }
View event distribution mechanism
When the event is passed from the ViewGroup to the View dispatchTouchEvent(), the first thing to execute is the View's onTouch() method. The onTouch() method is a method defined in the View's OnTouchListener interface. If the user registers listening for the View, this method will be triggered when the user touches the screen. This method returns false by default and needs to be rewrited by the user.
Only when the onTouch() method returns false will the View's onTouchEvent() method be executed. Then the performClick() method will be called according to the situation, and the performClick() method will then call the onClick() method.
/** * Source code analysis: () */ public boolean dispatchTouchEvent(MotionEvent event) { if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && (this, event)) { return true; } return onTouchEvent(event); } // Note: dispatchTouchEvent() will return true only if the following 3 conditions are true; otherwise, execute onTouchEvent() // 1. mOnTouchListener != null // 2. (mViewFlags & ENABLED_MASK) == ENABLED // 3. (this, event) // The following is analyses of these 3 conditions one by one /** * Condition 1: mOnTouchListener != null * Description: mOnTouchListener variable is assigned in the() method */ public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; // That is, as long as we register the Touch event for the control, the mOnTouchListener will be assigned (not empty) } /** * Condition 2: (mViewFlags & ENABLED_MASK) == ENABLED * illustrate: * a. This condition is to determine whether the currently clicked control is enabled * b. Since many Views are enabled by default, this condition is constant to true */ /** * Condition 3: (this, event) * Description: That is, onTouch() when the callback control registers the Touch event; the settings need to be manually repeated, as follows (taking the button Button as an example) */ (new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); // If onTouch() returns true, all the above three conditions will be valid, so that () directly returns true and event distribution ends // If it isonTouch()returnfalse,This will make all three conditions valid,Thus,()Jump outIf,implementonTouchEvent(event)
If the View's onTouchEvent() returns true, that is, the event is consumed, then the distribution of the event ends here. If false is returned, the onTouchEvent() methods of ViewGroup and Activity will be called from bottom up to process the event. It is worth mentioning that the onTouchEvent() method of Activity must handle events.
At this point, the distribution of the event is completed.
The above is a detailed content of the event distribution mechanism in Android. For more information about the Android event distribution mechanism, please follow my other related articles!