PS: If you think you are trying hard, then tell me what the result of your effort is!
The previous article here described the general process of Android event distribution. Below, we will introduce the relevant methods of events from three aspects: Activity, ViewGroup, and View. The sections are as follows:
- Activity
- ViewGroup
- View
Activity#
There are mainly two methods related to event transmission in Activity: dispatchTouchEvent() and onTouchEvent(). The event transmission starts with the dispatchTouchEvent() method of Activity.
Event Distribution#
The event distribution method in Activity is dispatchTouchEvent(), and its source code is as follows:
// Event distribution
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// Empty method
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
// onTouchEvent() method defaults to returning false
return onTouchEvent(ev);
}
In the code above, only the ACTION_DOWN event is clearly handled, indicating that only the ACTION_DOWN event will trigger event distribution. It then calls the superDispatchTouchEvent(ev) method of the Window class, which is an abstract method. When this method is called, it will invoke the method in the specific subclass, which is PhoneWindow in this case. The specific implementation of the superDispatchTouchEvent(ev) method in the Window class is as follows:
// Abstract method in Window class
public abstract boolean superDispatchTouchEvent(MotionEvent event);
// Specific implementation of superDispatchTouchEvent() method in PhoneWindow subclass
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
Clearly, mDecor.superDispatchTouchEvent(event) is called here, where mDecor is a PhoneWindow.DecorView object, which is also the top-level view of the window. It is the actual root view of the Activity, inheriting from FrameLayout. By calling super.dispatchTouchEvent, it will dispatch the touch event to the various child views of the activity, which are the views set in the Activity.onCreate method using setContentView. The code reference is as follows:
// DecorView class declaration
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
...
public boolean superDispatchTouchEvent(MotionEvent event) {
// This calls the dispatchTouchEvent method in FrameLayout
return super.dispatchTouchEvent(event);
}
...
}
Since FrameLayout does not override the dispatchTouchEvent(event) method, we need to look at the implementation of this method in its parent class, which is ViewGroup, for the specific event distribution process that needs further study.
Event Handling#
The event handling method in Activity is onTouchEvent(), and its source code is as follows:
// Event handling, defaults to returning false
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
// Defaults to returning false
return false;
}
For the onTouchEvent() method, the event transmission is passed to the parent control. Even if it returns false, the event is still considered consumed.
Note: When Activity performs event distribution, the event continues to be passed down only when return super.dispatchTouchEvent(ev) is executed. If it returns true or false, the event is consumed, thus terminating the event propagation.
ViewGroup#
There are mainly three methods related to event transmission in ViewGroup: dispatchTouchEvent(), onInterceptTouchEvent(), and onTouchEvent(), as detailed below:
Event Distribution#
The event distribution method in ViewGroup is dispatchTouchEvent(), and its source code is as follows:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
// dispatchTouchEvent() method defaults to returning false
boolean handled = false;
// The method decides whether to intercept event distribution
onInterceptTouchEvent(ev);
...
// By default, canceled and intercepted are false
if (!canceled && !intercepted) {
...
// This method passes the event to child views
dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
}
return handled;
}
This method's purpose is to traverse the child views in ViewGroup and pass the event (ACTION_DOWN) to the child views for processing. It mainly calls the onInterceptTouchEvent() and dispatchTransformedTouchEvent() methods, where onInterceptTouchEvent() defaults to returning false. Below is the main source code for the dispatchTransformedTouchEvent() method:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits){
...
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
// Perform event distribution to child views
handled = child.dispatchTouchEvent(event);
}
...
return handled;
}
Clearly, the dispatchTransformedTouchEvent() method mainly handles event distribution to child views. If there are no child views, it calls the parent view's dispatchTouchEvent(event) method.
Event Interception#
The event distribution method onInterceptTouchEvent() in ViewGroup has the following source code:
public boolean onInterceptTouchEvent(MotionEvent ev) {
...
// Defaults to returning false
return false;
}
This method defaults to returning false, indicating that it does not intercept event distribution to child views. This method is called in the dispatchTouchEvent() method of ViewGroup.
Event Handling#
ViewGroup does not have its own onTouchEvent() event handling method. ViewGroup inherits from View, and its event handling method is the event handling method of View, which is as follows:
public boolean onTouchEvent(MotionEvent event) {
...
// Defaults to returning false
return false;
}
This method handles relevant events. If it returns true, it indicates that the event has been handled, and specific usage will be recorded in the following text.
View#
There are mainly two methods related to event distribution in View: dispatchTouchEvent() and onTouchEvent(), as detailed below:
Event Distribution#
The event distribution method in View is dispatchTouchEvent(), and the main code is as follows:
public boolean dispatchTouchEvent(MotionEvent event) {
...
// Default return value is false
boolean result = false;
...
if (onFilterTouchEventForSecurity(event)) {
...
// Calls the onTouchEvent method
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
In the code above, the default return value of the dispatchTouchEvent() method is false, indicating that the event continues to be distributed. In fact, the return value of the dispatchTouchEvent method is related to the return value of the onTouchEvent method. If onTouchEvent returns true, the result of dispatchTouchEvent will be true, indicating that the event has been consumed. Of course, it can also be understood that the value of onTouchEvent being true indicates that the event has been consumed. Below are the conditions for executing the onTouchEvent method:
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
// The window is obscured, discard the touch event (rarely executed)
return false;
}
return true;
}
Clearly, the above method will generally return true, so it can definitely execute the onTouchEvent method. Calling the dispatchTouchEvent method of View can actually be simplified to the following code:
public boolean dispatchTouchEvent(MotionEvent event) {
...
// Default return value is false
boolean result = false;
...
return onTouchEvent(event)
}
Event Handling#
The event handling method in View is onTouchEvent(), and the main code is as follows:
public boolean onTouchEvent(MotionEvent event) {
...
// The condition for returning true here is that TouchDelegate is empty by default, which is mainly about the touch area of the View
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
// This condition will only return true if a click event is set, meaning the event is consumed
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
// Handling of various events
...
}
return true;
}
// Defaults to returning false, indicating that the event is not consumed
return false;
}
When the event is passed to the child view, there are two possible outcomes: either the current view consumes the event or it does not consume the event and passes it back up. If it is not intercepted until the Activity, and no processing is done, the event will ultimately be discarded.
This article is the second part of the Android event distribution mechanism, mainly recording the methods related to events in Activity, ViewGroup, and View, namely dispatchTouchEvent(), onTouchEvent(), and onInterceptTouchEvent(), and understanding the event relationships in Android from the perspective of code. The specific event distribution process will be discussed in the next article.