One, understandActivityThe composition of the

An Activity contains a Window object, which is implemented by PhoneWindow. PhoneWindow uses a DecorView as the root View of the entire application window, which in turn divides the screen into two regions: One is the TitleView and the other is the ContentView, and what we usually write is displayed in the ContentView. The following diagram shows the structure of an Activity.

2. Types of touch events

Touch events correspond to the MotionEvent class, and there are three main types of events:

  • ACTION_DOWN
  • ACTION_MOVE(Moves beyond a certain threshold are judged asACTION_MOVEOperation)
  • ACTION_UP

Three, the three stages of event transmission

  • Distribution (dispatchTouchEvent) : The method returns the valuetrueIndicates that the event is consumed by the current view. Return tosuper.dispatchTouchEventIndicates that the event continues to be distributed.
  • Intercept (onInterceptTouchEvent) : The method returns the valuetrueIntercepting the event and turning it over to itselfonTouchEventMethods to consume; returnfalseIndicates no interception and needs to continue to be passed to the subview.

    ifreturn super.onInterceptTouchEvent(ev)Event interception can be divided into two cases:
1. If the View(ViewGroup) has a child View and the child View is clicked, it is not intercepted and continues to be distributed to the child View for processing, equivalent to return false. 2. If the View(ViewGroup) has no child View or has child View but does not click on the subview (in this case, the ViewGroup is equivalent to a normal View), the onTouchEvent of the View will respond, which is equivalent to return true. Viewgroups such as LinearLayout, RelativeLayout, and FrameLayout are not blocked by default, whereas viewgroups such as ScrollView and ListView are blocked by default.Copy the code
  • Consumption (onTouchEvent) : The method returns the valuetrueIndicates that the current view can process corresponding events. The return value isfalseIndicates that the current view does not handle the event, which will be passed to the parent viewonTouchEventMethod to process. ifreturn super.onTouchEvent(ev), event processing can be divided into two situations:
1. If the View is clickable or longClickable, return true, indicating that the event was consumed. 2. If the View is not clickable or longClickable, false will be returned, indicating that the event is not consumed and will be passed up, just as false is returned.Copy the code

Note: There are three classes with event-passing capabilities in Android.

  • ActivityHave both distribution and consumption methods.
  • ViewGroup: Has distribution, blocking, and consumption methods.
  • View: Have distribution and consumption methods.

Four,ActivityDistribution of click events

When we operate on the touch screen, Linux receives the corresponding hardware interrupts, which are then processed into raw input events and written to the corresponding device nodes. And what our Android input system does is basically monitor these device nodes. When a device node has data to read, it reads the data and performs a series of translation processes, and then it finds the appropriate event receiver in all the Windows and sends it. When the click event is generated, the event is passed to the current Activity, which is done by the PhoneWindow in the Activity. The PhoneWindow passes the event processing to the DecorView, which passes the event processing to the ViewGroup. The source code flow is as follows:

1.Activity#dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); If (getWindow().superdispatchTouchEvent (ev)) {return true; if (getWindow().superdispatchTouchEvent (ev)); Return onTouchEvent(ev);} return onTouchEvent(ev); return onTouchEvent(ev); }Copy the code
2. Abstract Window#superDispatchTouchEvent
public abstract boolean superDispatchTouchEvent(MotionEvent event);
Copy the code
3. Unique implementation of class Phonewindows #superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
Copy the code

Five,ViewThe event distribution mechanism of

Events are dispatched to the ViewGroup’s dispatchTouchEvent method and handled by itself if its onInterceptTouchEvent returns true. If its mOnTouchListener is set, onTouch will be called. Otherwise onTouchEvent will be called. In onTouchEvent, onClick is called if mOnCLickListener is set. If its onInterceptTouchEvent returns false, it will be handled by a child View on the click event chain, and so on, completing the distribution. ViewGroup dispatchTouchEvent key source code is as follows:

1.ViewGroupWill be inACTION_DOWNReset the status when the event arrives
// Handle an initial down. if (actionMasked === MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the // previous gesture due to an app switch, ANR, or some other stae change. cancelAndClearTouchTarget(ev); // FLAG_DISALLOW_INTERCEPT resetTouchState() is reset in this method; }Copy the code
2. Process the currentViewWhether to block click events
Final Boolean interception. // When the event is successfully handled by a child element of ViewGorup, mFirstTouchTarget is assigned and refers to the child element; otherwise, mFirstTouchTarget is null when the event is intercepted by a ViewGroup. if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! = null) {/ / in the sub View through requestDisallowInterceptTouchEvent methods to set / / FLAG_DISALLOW_INTERCEPT: ViewGroup will not be able to intercept events other than ACTION_DOWN FLAG_DISALLOW_INTERCEPT) ! = 0; if (! disallowintercept) { intercepted = onInterceptTouchEvent(ev); //re store action in case it was changed ev.setAction(action); } else { intercepted = false; } else { // There are no touch targets and this action is not an initial down so this // view group continues to Touches (ACTION_MOVE, action_up.eg). intercepted = true; }}Copy the code
3.dispatchTouchEvent()Method the rest of the source
public boolean dispatchTouchEvent(MotionEvent ev) { ... final View[] children = mChildren; // Iterates through the child elements of the ViewGroup, and if the child element can receive the click event, passes it to the child element for processing. for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = (preorderedList == null) ? children[childIndex] : preorderedList.get(childIndex); if (childWithAccessibilityFocus ! = null) { if (childWithAccessibilityFocus ! = child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } // Determine whether the touch point is in the range of the child View or whether the child View is playing animation, if one // does not match, start traversing the next child View. if (! canViewReceivePointerEvents(child) || ! isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } newTouchTarget == getTouchTarget(child); if (newTouchTarget ! = null) { newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { mLastTouchDownTime = ev.getDownTime(); if (preorderedList ! = null) { for (int j = 0; j < childrenCOunt; j++) { if (children[childIndex] == mChildren[j]) { mLastTouchDownIndex = j; break; } } } else { mLastTouchDownIndex = childIndex; } mLastTouchDownX == ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget == true; break } ev.setTargetAccessibilityFocus(false); }... }Copy the code
In 4.dispathcTransformedTouchEventMethod to perform the real distribution logic
private boolean dispatchTransformedTouchEvent(MotionEvent event,boolean cancel,View child,int desiredPointerIdBits) { final int oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); // Call the dispatchTouchEvent(event) method of the child View if there is no child View, or call the super.dispatchTouchEvent(event) method if there is no child View. if (child == null) { handled == super.. dispatchTouchEvent(event); } else { handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; }... }Copy the code
5. Event transfer toViewthedispatchTouchEvent()
public boolean dispatchTouchEvent(MotionEvent event) { ... boolean result = false; if (onFilterTouchEventForSecurity(event)) { ListenerInfo li = mListenerInfo; // The onTouch method takes precedence over the onTouchEvent(event) method if (li! = null && li.mOnTouchListener ! = null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (! result && onTouchEvent(event)) { result == true; }}... return result; }Copy the code
6. The event is passed toViewtheonTouchEvent()
public boolean onTouchEvent(MotionEvent event) { ... final int action = event.getAction(); OnTouchEvent () returns true whenever either View's CLICKABLE or LONG_CLICKABLE is true. If ((viewFlags & CLICKABLE) = = CLICKABLE | | (viewFlags & LONG_CLICKABLE) = = LONG_CLICKABLE) | | (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch(action) { case MotionEvent.ACTION_UP: boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) ! = 0; if ((mPrivatFlags & PFLAG_PRESSED) ! = 0 || prepressed) { boolean focusTaken = false; if (! mHasPerformedLongPress && ! mIgnoreNextUpEvent) { removeLongPressCallback(); if (! focusTaken) { if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (! post(mPerformClick)) { performClick(); }}}}... } return true; } return true; }Copy the code
In 7.ACTION_UPEvent is calledperformCLick()methods
public boolean performClick() { final boolean result; final Listenerinfo li = mListenerInfo; // If the View sets the click event, the onClick method is executed. if (li ! = null && li.mOnClickListener ! == null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); result = true; } else { result = false; } sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); return result; }Copy the code

According to the above source code analysis, the complete click event transfer process of View can be obtained as shown in the figure below.

Summary: the delivery rules of click event distribution

According to the source code analysis of event distribution, the relationship between the three important methods of click event distribution can be expressed in pseudocode as follows:

public boolean diapatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) {
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}
Copy the code

Some important conclusions:

1. Event delivery priority: onTouchListener. OnTouch > onTouchEvent> onClickListener.

2. Normally, a time series can only be intercepted and consumed by one View. Because once an element intercepts the event, all events in the same sequence are handed to it directly (that is, the View’s interception method is no longer called to ask it whether to intercept, but the remaining ACTION_MOVE, ACTION_DOWN, etc.). Exception: You can force the event to be handled by another View by returning false to the onTouchEvent that overrides the View.

3. If the View consumes no events other than ACTION_DOWN, the click event will disappear, the parent element’s onTouchEvent will not be called, and the current View will continue to receive subsequent events, which will eventually be passed to the Activity.

4.ViewGroup does not intercept any events by default (return false).

5. The View’s onTouchEvent will consume the event by default (returning true), unless it is unclickable (both Clickable and longClickable are false). The longClickable property of a View is false by default. The Clickable property of a Button is true by default and that of a TextView is false by default.

6. The Enable property of View does not affect the default return value of onTouchEvent.

7. Through requestDisallowInterceptTouchEvent method can intervene in the parent element in child elements distribution of events, but ` ACTION_DOWN except events.

The final complete event distribution flow chart is as follows: