TouchEvent touch event mechanism in Android
When our Fingers click or slide on the Android screen, the touch event TouchEvent is triggered. In the App, ViewGroup and View have multi-level nesting. In the outermost layer, there are Activity and inner View, some viewgroups between Activity and View. To simplify the discussion, we assume that only one ViewGroup exists in an Activity and only one View exists in this ViewGroup. When we touch the View UI with our fingers, the touch event TouchEvent will be generated, as shown in the general process:
First, the outermost Activity receives the event and triggers the execution of the dispatchTouchEvent of the Activity. In this method, the Activity calls the execution of the dispatchTouchEvent method of the internal ViewGroup, the dispatchTouchEvent method of ViewGroup calls the execution of the dispatchTouchEvent method of the innermost View. The onTouchEvent method of View may be executed in the dispatchTouchEvent method of View, then, ViewGroup may also execute the onTouchEvent method of ViewGroup, and the Activity may also execute the onTouchEvent method of Activity.
Is a streamlined main flow chart, a total of two main lines:
The first main line is to call the dispatchTouchEvent method from Activity-> ViewGroup-> View in sequence from the external and the Android system will pass the MotionEvent parameter to the method in sequence. DispatchTouchEvent is used to transmit touch events,The main line shows the process of transferring touch events from the external to the internal layer., DispatchTouchEvent is the entry for each touch event to be passed.
The second main line is to call the onTouchEvent method in sequence from View-> ViewGroup-> Activity, and Android will pass the MotionEvent parameter to this method in sequence. OnTouchEvent is used to process touch events,This main line shows the process of processing touch events from the inside out.
Both dispatchTouchEvent and onTouchEvent receive a MotionEvent type parameter. MotionEvent encapsulates the data information of the touch event, including the touch event type and coordinate position. For details, see MotionEvent in Android. Both dispatchTouchEvent and onTouchEvent have a boolean type return value. If true is returned, the current object has processed the touch event. If false is returned, the current object has not processed the touch event.
The following describes how to dispatch and process events of Activity, ViewGroup, and View.
Activity
DispatchTouchEvent
All touch events generated by touch operations on the UI will first trigger the execution of the dispatchTouchEvent method in the Activity. The source code is as follows:
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev);}
The key to the above method is that the Activity first obtains the current window object through the getWindow () method, and then calls the superDispatchTouchEvent method of the window. In fact, getWindow () returns a PhoneWindow instance, in this way, the superDispatchTouchEvent method of PhoneWindow will be called. The source code is as follows:
@Overridepublic boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event);}
MDecor is a DecorView variable in PhoneWindow. DecorView represents the top View of the current Window and can be viewed as the root View. The code above shows that the superDispatchTouchEvent method of DecorView will be executed later. The source code is as follows:
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event);}
In fact, DectorView inherits from FrameLayout, so DectorView indirectly inherits from ViewGroup, so DectorView executes the dispatchTouchEvent method corresponding to its parent ViewGroup class. In this method, DectorView finds the child node it is touching. In fact, its child node is also a ViewGroup, and then executes the dispatchTouchEvent method of the ViewGroup, in this way, the touch event parameter MotionEvent is transferred from the Activity to the sub-ViewGroup of DecorView. We will discuss the execution logic in the dispatchTouchEvent method in ViewGroup later, so we will not introduce it too much here.
This section describes how to transfer a touch event from Activity to ViewGroup using the superDispatchTouchEvent and dispatchTouchEvent methods. Both methods return a boolean parameter. If true is returned, indicates that the touch event is processed, and vice versa, it indicates that the touch event is not processed. Let's take a look at the source code of the dispatchTouchEvent in the above Activity, and we will find that if the superDispatchTouchEvent of PhoneWindow returns true, then the dispatchTouchEvent method of the Activity will directly return true, this indicates that the touch event is processed by the Window, so the onTouchEvent method of the subsequent Activity will not be executed. The onTouchEvent method is called by the Activity only when the Window does not process the touch event.
OnTouchEvent
The source code of onTouchEvent is as follows:
public boolean onTouchEvent(MotionEvent event) { if (mWindow.shouldCloseOnTouch(this, event)) { finish(); return true; } return false;}
Only when the touch event is not processed by any View or ViewGroup, the Activity will execute its own onTouchEvent to process the touch event. A typical scenario is that the current touch point is out of the Window range, so that all views in the Window will not receive or process the touch event, in this case, we can rewrite this method to implement some logic processing. If processing is completed, true is returned; otherwise, false is returned. The default implementation basically always returns false.
ViewGroup
DispatchTouchEvent
After the Activity receives a touch event, it will call the dispatchTouchEvent method of ViewGroup through DectorView. Because the source code of this method is too long, no source code is posted here. Click here to view its source code. The main logic of this method is described here. The dispatchTouchEvent method is the portal for ViewGroup to process touch events.
The ViewGroup defines a member variable of the TouchTarget type, mFirstTouchTarget, which is used to save the child views in the current ViewGroup that process touch events.
First, the dispatchTouchEvent method calls its own onInterceptTouchEvent method. onInterceptTouchEvent is used to intercept ViewGroup to pass the touch event to its subview. If this method returns true, ViewGroup should intercept the touch event; if false is returned, ViewGroup should not intercept the touch event and pass the touch event to the subview. A boolean handled variable is also defined in the dispathTouchEvent method to save the return value of the dispathTouchEvent method. If it is true, the touch event is processed by the current ViewGroup, otherwise, the request is not processed.
Then, only when onInterceptTouchEvent returns false will the ViewGroup traverse its child views in sequence. It will call the isTransformedTouchPointInView method to determine whether the coordinates of the touch events carried by the MotionEvent falls within the subview range, if the coordinates of the touch event fall within the range of the Child View, the Child View in the current ViewGroup is touched, in this way, the ViewGroup will pass the coordinates of the touch event and the Child View to the dispatchTransformedTouchEvent method. In this way, the dispatchTouchEvent method of the child View will be called, the returned value indicates whether the self-View has processed the touch event. If dispatchTransformedTouchEvent returns true, the sub-View has processed the touch event. In this way, the ViewGroup calls the addTouchTarget method to call the mFirstTouch The Target is bound to the Child View, and the variable alreadyDispatchedToNewTouchTarget is also set to true, indicating that the child View has processed the touch event. Once a child View processes a touch event, the ViewGroup jumps out of the for loop through break and does not traverse other child views.
After the for loop of the Child View, if no child View processes the touch event, mFirstTouchTarget is null, in this case, ViewGroup will pass null as the child parameter to the dispatchTransformedTouchEvent method. This method will call super. the dispatchTouchEvent method is equivalent to executing the dispatchTouchEvent method in the View class because ViewGroup inherits from the View. In this way, it is very likely that the onTouchEvent method inherited by ViewGroup from the View is executed. The returned value of dispatchTransformedTouchEvent is used as the value of the local variable handled. The dispatchTouchEvent method in the View class is described in detail below.
After the for loop of the Child View, if a child View is found to process the touch event, alreadyDispatchedToNewTouchTarget is true, and the local variable handled is set to true, that is, as long as a child View processes a touch event, the current ViewGroup also processes the touch event. In this case, the ViewGroup does not call the dispatchTouchEvent method inherited from the View, therefore, the onTouchEvent method of ViewGroup is not triggered.
OnInterceptTouchEvent
Previously, we mentioned that onInterceptTouchEvent is used to intercept a ViewGroup to send a touch event to a sub-View. By default, the ViewGroup always returns false, indicating that the event is not intercepted. We can rewrite this method to implement our own touch event interception logic.
DispatchTransformedTouchEvent
Click here to view the source code. The main logic code is as follows:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; final MotionEvent transformedEvent; ...... // Perform any necessary transformations and dispatch. if (child == null) { handled = super.dispatchTouchEvent(transformedEvent); } else { final float offsetX = mScrollX - child.mLeft; final float offsetY = mScrollY - child.mTop; transformedEvent.offsetLocation(offsetX, offsetY); if (! child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } handled = child.dispatchTouchEvent(transformedEvent); } // Done. transformedEvent.recycle(); return handled;}
The main purpose of this method is to convert the x and y coordinates in the MotionEvent to the coordinates in the View coordinate system specified by the passed child variable, transformedEvent indicates the MotionEvent that has completed the transformation of the specified coordinate system. If the input child parameter is null, it indicates that the current ViewGroup is passed in, And then super is called directly. dispatchTouchEvent (transformedEvent) allows ViewGroup to call the dispatchTouchEvent method in the parent class View. If the input child parameter is not null, it indicates a subview of the current ViewGroup, then child is called. dispatchTouchEvent (transformedEvent) to pass the touch event from the ViewGroup to the sub-View. The following describes the implementation logic of View's dispatchTouchEvent.
OnTouchEvent
The onTouchEvent method of ViewGroup inherits from the onTouchEvent method of View. ViewGroup is not rewritten. The implementation logic of onTouchEvent method of View is described below.
View
DispatchTouchEvent
Click here to view the source code. The main logic of the source code is as follows:
Public boolean dispatchTouchEvent (MotionEvent event ){...... boolean result = false ;...... if (onFilterTouchEventForSecurity (event) {// noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; // if OnTouchListener is set, the onTouch method of OnTouchListener is executed here if (li! = Null & li. mOnTouchListener! = Null & (mViewFlags & ENABLED_MASK) = ENABLED & li. mOnTouchListener. onTouch (this, event) {// If the onTouch method of OnTouchListener returns true, it indicates that the touch event is processed, and the result is set to true result = true ;} // if the touch event is not processed by OnTouchListener, The onTouchEvent method if (! Result & onTouchEvent (event) {// If onTouchEvent returns true, the touch event is processed by the View, result is set to true result = true ;}}...... return result ;}
DispatchTouchEvent is the entry for View to process touch events. In this method, View first checks whether it has set OnTouchListener. If it is set, the onTouch method of OnTouchListener is called. If it returns true, the touch event is processed, result is set to true. If the touch event is not processed by OnTouchListener, The onTouchEvent method of the View is executed. If onTouchEvent returns true, the touch event is processed by the View and the result is set to true.
It can be seen from the above that in the dispatchTouchEvent method, the onTouch method of OnTouchListener is executed first. Once it returns true, the onTouchEvent method of View itself will not be called, only when OnTouchListener does not process a touch event will the onTouchEvent method of the View be executed later.
OnTouchEvent
Click here to View the source code, View. in the onTouchEvent () method, if the View registers an event listener such as CLICK or LONG_CLICK, the registered event listener will process the touch event, so that the onTouchEvent returns true. Different processes are executed based on different actions. For example, if ACTION_UP is used, the receivmclick () method is executed. This method triggers OnClickListener. onClick () execution.
If View does not register any event listeners such as CLICK or LONG_CLICK, onTouchEvent returns false, indicating that onTouchEvent does not process the MotionEvent.
Summary
Through the processing of touch events at the Activity, ViewGroup, and View levels above, we can find that the processing of touch events at each level in Android starts from the dispatchTouchEvent method, first, call the dispatchTouchEvent method at the next level to pass the touch event to the next level. If the next level processes the touch event, you can think that this level also processes touch events, so this level does not only need to do other special processing for touch events; if the next level does not process touch events, that is, if the dispatchTouchEvent method at the next level returns false, the onTouchEvent method at the current level will be called to process the touch event.
For more blog posts, see my Android blog summary. I hope this article will help you understand the touch event mechanism in Android!