Book attached to the text: event distribution analysis – copy jingdong home page two linkage

structure

The event

An event is a MotionEvent object.

ACTION_DOWN: Event starts, mainly handles the event distribution, when the distribution is finished, the mFirstTouchTarget is assigned (handles the event’s child View list).

ACTION_MOVE: The move event will be triggered multiple times. For a View, if there is no down, there must be no move. But the ViewGroup, if it’s not down, it’s going to have a second move event. Because it is triggered multiple times, the source code must be viewed multiple times

ACTION_UP: The event is over

ACTION_CANCEL: When an event is moved, it is assigned as cancel. The second Move can be intercepted by the upper layer

View and ViewGroup structure

1. The ViewGroup extends the View. So methods that are not implemented in the ViewGroup call the View method. ViewGroup does not implement event handling, so it calls the View’s event handling dispatchTouchEvent

2. From the layout perspective, the father of View must be ViewGroup. So the ViewGroup sends events layer by layer to the View. A ViewGroup might send events to its own subviews, or call its own (extends View) dispatchTouchEvent to handle them.

Event handling: View.java –> dispatchTouchEvent Event distribution: viewGroup. Java –> dispatchTouchEvent

The incident

There is only one event, but more than one person wants to handle it. If the object being handled is not the one we want to give, then there is an event conflict.

Resolution: There are two ways to resolve event conflicts: internal interception and external interception. Child View to handle, called internal interception. His father ViewGroup to handle, call external interception.

The whole process

Event handling – View

First of all, events are handled on the View onClick and onTouch. OnTouch is executed first and onClick is executed later. When onTouch returns false, onClick executes. When onTouch returns true, onClick does not execute. Indicates that the event onTouch was consumed. The reason that onClick does not return a value is that the event is already consumed when onClick is executed.

The source code

Java dispatchTouchEvent ()

To look down

The core code

So much for the core code of event handling

1. The && processing logic in Java is like this,if(a && b && c)If a=false, b is not executed. If b=false, do not execute c

ListenerInfo li = mListenerInfo; What is this?



Point into the getListenerInfo ()

So mListenerInfo in the first couple of conditions is the new OnTouchListener in the setOnTouchListener, so as long as setOnTouchListener is implemented then the first couple of conditions are true, So li will be executed. MOnTouchListener. The onTouch (this event))

Point into li. MOnTouchListener. The onTouch (this event)) found a interface,

So it’s going to do a custom onTouch

The return value true and false in the custom onTouch will affect the return value of view.DispatchTouchEvent (). So onTouch returns true to indicate that the event was consumed, and false= no consumption

So the return value in the custom onTouch is true, result = true; if (! Result && onTouchEvent(event)) if the first condition is not true, then onTouchEvent(event) will not be executed, so onClick() will not be executed in the onTouchEvent(Event) ACTION_UP method

OnTouchEvent (event) onTouchEvent() has down, up, and so on. Where onClick is in the Up event.

There’s a performClick in the up event of onTouchEvent

Point in

PerformClick () method

1. Handle button sound

2. Handle the onClick

3.result = true;

That would be the end of the matter

Event distribution – ViewGroup

Event distribution is dispatchTouchEvent () in the ViewGroup, where the source code is the focus.

To streamline the source code

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        / /... omit
        if (onFilterTouchEventForSecurity(ev)) {
            / /... omit
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                /** Clear the last event cache */
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            /** Intercept judgment */
            final boolean intercepted;
            if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! =null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! =0;
                if(! disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action);// restore action in case it was changed
                } else {
                    intercepted = false; }}else {
                intercepted = true;
            }

            / /... omit

            ** Event distribution */
            if(! canceled && ! intercepted) {if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                    / /... omit

                    // Number of subviews
                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null&& childrenCount ! =0) {
                        /** Traverse the child View*/
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {

                            / /... omit

                            /** Determine whether the subview needs to distribute */
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

                                / /... omit

                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                        }
                    }
                }
            }

            /** Specific execution distribution */
            // Whether to intercept
            if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {

                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while(target ! =null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            target = next;
                            continue; } } predecessor = target; target = next; }}}return handled;
    }
Copy the code

Event distribution structure – Down event!

1. Clear the cache

Skip some accessibility and other code

2. Intercept judgment

1. If it is ACTION_DOWN or mFirstTouchTarget! = null (it must be null at the beginning, before traversing the subview) 2. Child View is not set an intercept (shots requestDisallowInterceptTouchEvent () 3. This is the onInterceptTouchEvent () method

You need to override onInterceptTouchEvent () to return true for intercepting

3. Interception judgment – Interception (OnInterceptTouchEvent () returns true)

Skip the middle section of code. Go straight to the final distribution execution process

DispatchTransformedTouchEvent ()This method is the handling method of event distribution, important!! When child is null

When child is empty, call “Handled” = Super.DispatchTouchEvent (transformedEvent); That is, the dispatchTouchEvent in the call’s own (Viewgroup) extends (inherited) View

The return value: Handled is whether the thing was handled

4. Intercept judgment – When not intercepting (OnInterceptTouchEvent () returns false)

  1. When you don’t intercept, you goif (! canceled && ! intercepted)

  1. Down Indicates the normal flow of the event

3. If there is a child View, insert it into an ArrayListArrayList (in XML order, textView/ImagView… List(0) =ImagView, List(1) = textView

  1. For traverses the child View to determine whether to distribute

Important!! If (dispatchTransformedTouchEvent (ev, false, child, idBitsToAssign)) the method to judge whether son consumption, without spending for the next, To consume, addTouchTarget to mFirstTouchTarget and break out of the loop.

3.1 points into dispatchTransformedTouchEvent method

Points into dispatchTransformedTouchEvent method, when the child is not null, the call will be handled = child. DispatchTouchEvent (transformedEvent); When the child is a View, it will call the View’s dispatchTouchEvent to handle the event, and when the child is a ViewGroup, it will call the ViewGroup’s dispatchTouchEvent to (recursively) distribute the event, and then iterate over the son’s son… One layer at a time.

  1. Determine the distribution of

Next = null mFirstTouchTarget = target; ! =null alreadyDispatchedToNewTouchTarget = true

  1. If a for iterates through, there is no subclass that handles the event, which is all of the subviews

Before dispatchTransformedTouchEvent () is false, then go to intercept code

  1. If you have a child View that you want to handle, addTouchTarget and then break; Out of the for loop, go mFirstTouchTarget! = NULL to distribute the event

  1. In the distribution casewhile (target ! = null)This while loop usually only goes once (single touch, multi-touch multiple times).

Step 5: While the for loop is looking for the child View to handle, the event has already been sent to the View, so the tag “Handled” =true returns

Event distribution — Move events!

The Down event determines which View consumes the event, but the Move event still starts with the top ViewGroup

1. Do not clear the cache

  1. Because mFirstTouchTarget! = null so it still goes through the intercept or not process

  1. If there is no interception, then if (! canceled && ! intercepted) here will still go. When the subview is down, the distribution event is finished, so the subview will not distribute the event

  1. After skipping the distribution event code, it’s the last step, because mFirstTouchTarget! =null, so else

  1. DispatchTransformedTouchEvent () distribute events, distributed to save the child before the View

Event conflict resolution

Event conflicts can only be handled when moving!!

The resolution of event conflicts is divided into internal interception (child View handling) and external interception (parent container handling).

Internal intercept

Internal interception, child View of dispatchTouchEvent through getParent () requestDisallowInterceptTouchEvent whether to set up the father shall have the right to intercept

getParent().requestDisallowInterceptTouchEvent(); This method is the sword of the above code. A child View can set the parent container to prevent it from intercepting.

But that’s not enough, because ViewGroup DOWN clears the cache, so there’s no way to set the child View, so you have to deal with the parent DOWN, right

External intercept

External intercepting is controlled by setting onInterceptTouchEvent of the parent container, whether the parent container intercepts or not