Android touch event delivery mechanism

Time:2022-1-2

1、 UnderstandActivityConstitutive

OneActivityContains aWindowObject, which is composed ofPhoneWindowTo achieve.PhoneWindowtakeDecorViewAs the root of the entire application windowView, and thisDecorViewThe screen is divided into two areas: one isTitleView, the other isContentViewAnd what we usually write is to showContentViewIn, shown in the figure belowActivityComposition of.

Android touch event delivery mechanism

2、 Type of touch event

The touch event corresponds toMotionEventClass. There are three main types of events:

  • ACTION_DOWN
  • ACTION_MOVE(if the moving distance exceeds a certain threshold, it will be judged asACTION_MOVEOperation)
  • ACTION_UP

3、 Three stages of event delivery

  • Distribute(dispatchTouchEvent)The return value of the method is:trueIndicates that the event is consumed by the current view; Return assuper.dispatchTouchEventIndicates that the event will continue to be distributed.
  • Intercept(onInterceptTouchEvent)The return value of the method is:trueMeans to intercept this event and hand it over to its ownonTouchEventMethods of consumption; returnfalseIndicates that it is not intercepted and needs to continue to be passed to the child view.
    Ifreturn super.onInterceptTouchEvent(ev)There are two types of event interception:
1. If a child view exists in the view (ViewGroup) and the child view is clicked, it will not be intercepted and distribution will continue
To process the child view is equivalent to return false.
2. If the view (ViewGroup) does not have a child view or has a child view but does not click a child view (ViewGroup)
Equivalent to a normal view), the ontouchevent response of the view is equivalent to return true. 
Note: General viewgroups such as LinearLayout, relativelayout and FrameLayout do not intercept by default, but
Scrollview, listview and other viewgroups may be blocked, depending on the specific situation.
  • Consumption(onTouchEvent)The return value of the method is:trueIndicates that the current view can handle the corresponding events; The return value isfalseIndicates that the current view does not handle this event, and it will be passed to the parent viewonTouchEventMethod. Ifreturn super.onTouchEvent(ev), event handling can be divided into two situations:
1. If the view is clickable or longclickable, it will return true, indicating consumption
The event is returned, which is the same as returning true;
2. If the view is not clickable or longclickable, false will be returned, indicating No
Consuming this event will be passed up, just like returning false

Note: inAndroidIn the system, there are three classes with the ability of event transmission and processing.

  • Activity: it has two methods: distribution and consumption.
  • ViewGroup: it has three methods: distribution, interception and consumption.
  • View: it has two methods: distribution and consumption.

IVActivityDistribution process of click events

When we operate the touch screen,LinuxThe corresponding hardware interrupt will be received, and then the interrupt will be processed into the original input event and written to the corresponding device node. And ourAndroidTo sum up, what the input system does is to monitor these device nodes. When a device node has data readable, it reads out the data and performs a series of translation processing, and then finds the appropriate event receiver in all windows and sends it.
When the click event is generated, the event will be passed to the current userActivity, byActivityMediumPhoneWindowDone,PhoneWindowThen hand over the event handling work toDecorView, and thenDecorViewDelegate event handling toViewGroup。 The source code flow is as follows:

1.Activity#dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    //Distributed by the window attached to the activity, returns true, and the event cycle ends
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    //Returning false means that the event is not handled and all ontouchevents of the view are ignored
    //If false is returned, the ontouchevent of the activity will be called
    return onTouchEvent(ev);
}
2. Abstract class window#superdispatchtouchevent
public abstract boolean superDispatchTouchEvent(MotionEvent event);

3. The only implementation class is phonewindow #superdispatchtouchevent
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

VViewEvent distribution mechanism

Event distribution toViewGroupofdispatchTouchEventMethod, if it’sonInterceptTouchEventreturntrue, it is handled by itself. At this time, if itsmOnTouchListenerIs set, thenonTouchWill be called, otherwiseonTouchEventWill be called. stayonTouchEventIf setmOnCLickListener, thenonClickWill be called. If it’sonInterceptTouchEventreturnfalse, it will be handed over to the child on the click event chainViewProcessing, such a cycle, complete the distribution.ViewGroupindispatchTouchEventThe key source code is as follows:

1.ViewGroupWill beACTION_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 is reset in this method_ DISALLOW_ INTERCEPT
    resetTouchState();
}
2. Process currentViewBlock click events
final boolean interception;
//Mfirsttouchtarget is assigned when the event is successfully processed by the child element of viewgorup
//And point to the child element. On the contrary, when intercepted by ViewGroup, mfirsttouchtarget is null.
if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
    //In the subview, set it through the requestdisallowintercepttouchevent method
    // FLAG_ DISALLOW_ At this time, the ViewGroup will not be able to intercept the action_ Events other than down 
    final boolean disallowIntercept = (mGroupFlags & 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 intercept touches(ACTION_MOVE、ACTION_UP.eg).
        intercepted = true;
    }
}
3.dispatchTouchEvent()Method the rest of the source code
public boolean dispatchTouchEvent(MotionEvent ev) {
    ...
    final View[] children = mChildren;
    //Traverse the child elements of the ViewGroup. If the child element can accept the click event, it will be handed over 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;
            }
            //There is one item to judge whether the position of the touch point is within the range of the sub view or whether the sub view is playing animation
            //If not, 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);
        }
    ...
}
4. IndispathcTransformedTouchEventMethod
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);
        //If there is a child view, call the dispatchtouchevent (event) method of the child view. If there is no child view,
        //Call super Dispatchtouchevent (event) method.
        if (child == null) {
            handled == super..dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
    ...
}
5. Event delivery toViewofdispatchTouchEvent()
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;
}
6. Event delivery toViewofonTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
    ...
    final int action = event.getAction();
    //As long as the clickable and long of view_ Clickable has one that is true, and ontouchevent() will
    //Return true to consume this event.
    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;
}
7. InACTION_UPCalled in the eventperformCLick()method
public boolean performClick() {
    final boolean result;
    final Listenerinfo li = mListenerInfo;
    //If the view has a click event set, the onclick method will execute.
    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;
}

From the above source code analysis can be obtainedViewThe complete click event delivery process is shown in the figure below.

Android touch event delivery mechanism

6、 Summary: delivery rules for click event distribution

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

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

Some important conclusions:

1. Event delivery priority:onTouchListener.onTouch > onTouchEvent> onClickListener.onClick

2. Under normal circumstances, a time series can only be intercepted and consumed by one view. Once an element intercepts this event, all events in the same event sequence will be directly handled by it (that is, the intercepting method of the view will not be called to ask whether it wants to intercept, but the remaining events will be interceptedACTION_MOVEACTION_DOWNAnd so on). Special case: by rewritingViewofonTouchEventreturnfalseEvents can be forcibly transferred to otherViewhandle.

3. IfViewNo consumption exceptACTION_DOWNFor other events, the click event will disappear, and the parent element’sonTouchEventWill not be called, and the currentViewSubsequent events can be received continuously, and finally these disappeared click events will be passed to the userActivityhandle.

4.ViewGroupBy default, no event is intercepted (returned)false)。

5.ViewofonTouchEventAll events are consumed by default (return)true), unless it is not clickable(clickableandlongClickableAt the same timefalse)。ViewoflongClickableAll properties are by defaultfalseclickableAttributes should be divided into situations, such asButtonofclickableProperty defaults totrue, andTextViewofclickableDefault tofalse

6.ViewofenableProperties do not affectonTouchEventThe default return value of.

7. AdoptionrequestDisallowInterceptTouchEventMethod can interfere with the event distribution process of the parent element in the child element, but ` action_ Except for the down event.

The final complete event distribution flow chart is as follows:

Android touch event delivery mechanism

Recommended Today

Vue2 technology finishing 3 – Advanced chapter – update completed

3. Advanced chapter preface Links to basic chapters:https://www.cnblogs.com/xiegongzi/p/15782921.html Link to component development:https://www.cnblogs.com/xiegongzi/p/15823605.html 3.1. Custom events of components 3.1.1. Binding custom events There are two implementation methods here: one is to use v-on with vuecomponent$ Emit implementation [PS: this method is a little similar to passing from child to parent]; The other is to use ref […]