preface

Android event distribution is a necessary skill in our development, but the knowledge about event distribution is also a little complicated. If we start from scratch to read the source code, often a myriad of threads, miss the point. We can start from the following questions, outline contract territory to master this knowledge point. In the end, readers can judge whether they have really mastered this knowledge by judging whether their own problems have been solved

First of all, let’s think about what happens from the time we touch the screen to the time the App responds to events. What are the parts? We can break down the whole Touch event into the following parts

  • 1.TouchHow do events get from the screen to usApp
  • 2.TouchEvent arrivesAppHow to pass to the corresponding page
  • 3.TouchHow are events distributed internally after they arrive at the corresponding page

The one that is most relevant to upper-layer software development is Article 3, which we are most concerned about, and which can also be broken down into the following questions

  • ViewGroupWhether to intercept the event, and how to deal with the interception and non-interception respectively?
  • The childViewWhether to intercept the event, and how to deal with the interception and non-interception respectively?
  • ViewGroupAnd the sonViewDo not intercept, how to handle the final event?
  • How are event conflicts handled?

The above problems can be summarized as mind mapping as follows:



Let’s do a detailed analysis

1.TouchHow do events get from the screen to usApp

1.1 Hardware and kernel

When we touch the screen or press buttons, the first thing that’s triggered is the hardware driver that receives the event, writes the event to the input device node, and that generates the most primitive kernel event when the screen is touched, The Linux kernel wraps hardware-generated touch events as events and stores them in the /dev/input_event [x] directory

The goal is to encapsulate the input Event as a generic Event for subsequent processing

1.2 SystemServerPart of the

As we know, when the system starts, the SystemServer process starts a series of system services, such as AMS,WMS, and so on. One of them is the InputManagerService that we use to manage event inputs

This service is responsible for communicating with the hardware and receiving on-screen input events. Inside it, a reader thread, called InputReader, is started, which takes tasks from the system (/dev/inpu/) and dispatches them to the InputDispatcher thread, and then uniformly dispatches events.

1.3 Cross-process communication passed toApp

Now that the system process has the input event, but it needs to pass it to the App process, This is where cross-process communication comes in. The Window in our App communicates with the InputManagerService using an InputChannel, which is a pipe and actually communicates through sockets. We know that viewrootimpl.setView () is called when the Activity is started and that InputChannel is also registered during viewrootimpl.setView () :

public final class ViewRootImpl {

  public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
      requestLayout();
      // ...
      / / create InputChannel
      mInputChannel = new InputChannel();
      Binder registers InputChannel with SystemServermWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); }}Copy the code

There’s something aboutWindowManagerServiceandBinderCommunicating across processes, the reader does not need to get bogged down in the fine details

Just understand the ultimate inSystemServerIn the process,WindowManagerServiceAccording to the currentWindowTo create theSocketPairUsed for cross-process communication and simultaneous communicationAppIt was sent in the processInputChannelRegistered

After that,ViewRootImplIn theInputChannelI’m pointing to the right oneInputChannelAs aClientEnd,fdwithSystemServerIn the process ofServerthefdcompositionSocketPairAnd they can communicate in both directions.

Then our App process’s main thread will listen socket client, when after receiving the message (input), the callback NativeInputEventReceiver. HandleEvent () method, Will eventually go InputEventReceiver dispachInputEvent method.

After the above operations, the App finally gets the input event, and the next step is to transfer it to the corresponding page

1.4 summary

The section on how the kernel handles Input events and cross-process communication is generally not the most important part for application developers, nor is it the focus of this article, so it has only been outlined for those who want more details: Input System – Event processing

2.TouchEvent arrivesAppHow to pass to the corresponding page

Now that we’ve got the input event in the App process, let’s see how the event is distributed to the page and let’s go through the source code, okay

2.1 Event Sending toViewRootImpl

//InputEventReceiver.java
private void dispatchInputEvent(int seq, InputEvent event) {
    mSeqMap.put(event.getSequenceNumber(), seq);
    onInputEvent(event); 
}

//ViewRootImpl.java ::WindowInputEventReceiver
final class WindowInputEventReceiver extends InputEventReceiver {
    public void onInputEvent(InputEvent event) {
       enqueueInputEvent(event, this.0.true); }}//ViewRootImpl.java
void enqueueInputEvent(InputEvent event,
        InputEventReceiver receiver, int flags, boolean processImmediately) {
    adjustInputEventForCompatibility(event);
    QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);

    QueuedInputEvent last = mPendingInputEventTail;
    if (last == null) {
        mPendingInputEventHead = q;
        mPendingInputEventTail = q;
    } else {
        last.mNext = q;
        mPendingInputEventTail = q;
    }
    mPendingInputEventCount += 1;

    if (processImmediately) {
        doProcessInputEvents(); 
    } else{ scheduleProcessInputEvents(); }}Copy the code

You can see that the event is back in ViewRootImpl, which is responsible not only for drawing the interface, but also for delivering the event

2.2 First distribution of responsibility chain

Next, go to doProcessInputEvents, which involves the first chain of responsibility distribution in event distribution

   void doProcessInputEvents(a) {...// Deliver all pending input events in the queue.
        while(mPendingInputEventHead ! =null) { QueuedInputEvent q = mPendingInputEventHead; mPendingInputEventHead = q.mNext; deliverInputEvent(q); }... }private void deliverInputEvent(QueuedInputEvent q) { InputStage stage; .//stage assignment.if(stage ! =null) {
            stage.deliver(q);
        } else{ finishInputEvent(q); }}abstract class InputStage {
        private final InputStage mNext;

        public InputStage(InputStage next) {
            mNext = next;
        }

        public final void deliver(QueuedInputEvent q) {
            if((q.mFlags & QueuedInputEvent.FLAG_FINISHED) ! =0) {
                forward(q);
            } else if (shouldDropInputEvent(q)) {
                finish(q, false);
            } else {
                traceEvent(q, Trace.TRACE_TAG_VIEW);
                final int result;
                try {
                    result = onProcess(q);
                } finally{ Trace.traceEnd(Trace.TRACE_TAG_VIEW); } apply(q, result); }}}Copy the code

QueuedInputEvent is an input event that is passed to InputStage 2.InputStage is the responsibility chain that processes the input, and event 3 is passed through the responsibility chain when a call to Deliver is called. After the event is distributed, finishInputEvent is called to inform the InputDispatcher thread of the SystemServer process, and the event is removed to complete the distribution and consumption of the event.

So the question is, when is the responsibility chain of InputStage assembled?

2.3 Assembling the responsibility chain

We have to go back to the view file pl.setView method

	public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {...// Set up the input pipeline.
	        mSyntheticInputStage = new SyntheticInputStage();
	        InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
	        InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
	                 "aq:native-post-ime:" + counterSuffix);
	        InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
	        InputStage imeStage = new ImeInputStage(earlyPostImeStage,
	                "aq:ime:" + counterSuffix);
	        InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
	        InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
	                        "aq:native-pre-ime:"+ counterSuffix); mFirstInputStage = nativePreImeStage; mFirstPostImeInputStage = earlyPostImeStage; . }}Copy the code

As you can see, in the setView method, the chain of responsibility for handling the input event has been concatenated. The different subclasses of InputStages have been concatenated by the constructor. So what do these InputStages do?

  • SyntheticInputStage. Integrated event processing phases, such as handling navigation panels, joysticks, and so on.
  • ViewPostImeInputStage. View input processing phases, such as buttons, finger touches, and other motion events, are well knownviewEvent distribution occurs at this stage.
  • NativePostImeInputStage. Local method processing phase, mainly build deferred queue.
  • EarlyPostImeInputStage. Input method early processing stage.
  • ImeInputStage. Input method event processing phase, processing input method characters.
  • ViewPreImeInputStage. View preprocessing input method event phase, call the viewviewthedispatchKeyEventPreImeMethods.
  • NativePreImeInputStage. The local method preprocesses the input method event stage.

To recap, the main thread to the application handles the event through a series of Inputstages via ViewRootImpl. This stage is actually a simple classification of events, such as view input events, input method events, navigation panel events, and so on. Our View touch event happens in the ViewPostImeInputStage

    final class ViewPostImeInputStage extends InputStage {
        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if((source & InputDevice.SOURCE_CLASS_POINTER) ! =0) {
                    returnprocessPointerEvent(q); }}}private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            boolean handled = mView.dispatchPointerEvent(event)
            return handled ? FINISH_HANDLED : FORWARD;
        }

//View.java
    public final boolean dispatchPointerEvent(MotionEvent event) {
            if (event.isTouchEvent()) {
                return dispatchTouchEvent(event);
            } else {
                returndispatchGenericMotionEvent(event); }}Copy the code

1. Through layer upon layer the callback will be called to mView. DispatchPointerEvent 2. We know that the mView in ViewRootImpl is a DecorView

Events have now passed to the DecorView, also is our roots in the interface layout Followed by events in the Activity, Window, DecorView in the relay

2.4 events inActivity.Window.DecorViewIn the pass

//DecorView.java
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        //cb is the corresponding Activity/Dialog
        final Window.Callback cb = mWindow.getCallback();
        returncb ! =null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }


//Activity.java
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

//PhoneWindow.java
    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

//DecorView.java
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }    
Copy the code

DecorView -> Activity -> PhoneWindow -> DecorView looks like a strange event flow. The event starts from the DecorView and ends up back in the DecorView.

Why did against 2.4.1ViewRootImplDon’t just hand it overActivity?

Mainly to understand lotus root ViewRootImpl does not know there is such a thing as Activity! It just holds the DecorView. So, can not directly send touch events to the Activity. The dispatchTouchEvent ()

2.4.2 toAcitivityAfter that, why not just hand it inDecorViewStart distributing events?

Because the Activity doesn’t know there is a DecorView! However, the Activity holds a PhoneWindow, which of course knows what’s in its window and can send events to the DecorView. In Android, an Activity doesn’t know what’s in its Window, so coupling is low. It doesn’t need to know what’s in the Window, right

2.5 summary

After the above process, the event finally arrived at the familiarViewGroup.dispatchTouchEvent

The flow chart is as follows:

3.TouchHow are events distributed internally once they arrive on the page

The following is our most common and common event distribution section

3.1 ViewGroupWhether to intercept events

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        final boolean intercepted;
        // Only if ActionDown or mFirstTouchTarget is empty
        if(actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget ! =null) {
            final booleandisallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! =0;
            if (!disallowIntercept) {
                intercepted = onInterceptTouchEvent(ev);
            } 
        } 

        
        if(! canceled && ! intercepted) {// The event is passed to the child view.if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
            	...
            	// If the subview is consumed, assign the mFirstTouchTarget valuenewTouchTarget = addTouchTarget(child, idBitsToAssign); . }}/ / mFirstTouchTarget will call dispatchTransformendTouchEvent isn't empty
        if (mFirstTouchTarget == null) {
            handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); }}private boolean dispatchTransformedTouchEvent(View child) {
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else{ handled = child.dispatchTouchEvent(event); }}Copy the code

Action_Down or mFirstTouchTarget is not null. MFirstTouchTarget is a linked list structure, indicating that a child View has consumed an event. Null indicates that no child View has consumed event 3. There isa disallowIntercept field before deciding whether to intercept or not, which will be used in case of event conflicts with internal interception. Then the onInterceptTouchEvent, ViewGroup whether to intercept events is controlled by this method

3.1.2 ViewGroupWhat happens after interception?

1. After interception, The event will no longer be sent to the sub-view 2. If the next mFirstTouchTarget is null, will call to dispatchTransformedTouchEvent, then call to super. The dispatchTouchEvent, eventually to ViewGroup. The onTouchEvent 3. ViewGroup: mFirstTouchTarget==null ViewGroup: mFirstTouchTarget==null The second is that the child View does not handle the event, and both cases end up calling back to viewGroup.onTouchEvent

Through the above analysis, we can get the pseudo code intercepted by ViewGroup:

public boolean dispatchTouchEvent(MotionEvent event) {
    boolean isConsume = false;
    if (isViewGroup) {
        if (onInterceptTouchEvent(event)) {
            isConsume = super.dispatchTouchEvent(event); }}return isConsume;
}
Copy the code

If it is a ViewGroup, the onInterceptTouchEvent method is used to check whether it is intercepted. If it is, the dispatchTouchEvent method of the parent View class is executed.

3.1.3 ViewGroupWhat happens if you don’t intercept?

If the ViewGroup does not intercept, it will be passed to the child View

    if(! canceled && ! intercepted) {if (actionMasked == MotionEvent.ACTION_DOWN
                || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            final int childrenCount = mChildrenCount;
        	// Iterate over the subview
            if (newTouchTarget == null&& childrenCount ! =0) {
                for (int i = childrenCount - 1; i >= 0; i--) {
                    final int childIndex = getAndVerifyPreorderedIndex(
                            childrenCount, i, customOrder);
                    final View child = getAndVerifyPreorderedView(
                            preorderedList, children, childIndex);

                    //2. Determine the event coordinates
                    if(! child.canReceivePointerEvents() || ! isTransformedTouchPointInView(x, y, child,null)) {
                        ev.setTargetAccessibilityFocus(false);
                        continue;
                    }

                    //3. Pass events
                    if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                        newTouchTarget = addTouchTarget(child, idBitsToAssign);
                        alreadyDispatchedToNewTouchTarget = true;
                        break;
                    }
                }
            }
        }
    }

    private boolean dispatchTransformedTouchEvent(View child) {
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else{ handled = child.dispatchTouchEvent(event); }}Copy the code

If no interception is performed, the ViewGroup mainly does the following: 1. Traverses all subviews of the current ViewGroup. Determine whether the current View is within the coordinate range of the current sub-view. If it is not within the range, events cannot be received. Skip 3 directly. Using dispatchTransformedTouchEvent, if return true, through addTouchTarget mFirstTouchTarget assignment 4. The main dispatchTransformedTouchEvent do is two things, If the child is not null, the event distribution to the child, or call the super. DispatchTouchEvent, and eventually return result 5. MFirstTouchTarget is singly linked list structure, record consumption chain, but at the time of the single point touch this feature is not used, It’s just a regular TouchTarget object

3.2 the sonViewWhether to intercept

public boolean dispatchTouchEvent(MotionEvent event) {
        
        if (onFilterTouchEventForSecurity(event)) {
            
            ListenerInfo li = mListenerInfo;
            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

The child View’s diapatchTouchEvent logic is simpler 1. If setOnTouchListener is set and true is returned, onTouchEvent will no longer execute 2. Otherwise, execute onTouchEvent, which is where OnClickListenr is triggered

So by default, onTouchEvent is executed directly, and if we set setOnClickListener or setLongClickListener, it will fire normally

3.2.1 if the childViewWhat about consumption events?

As I said, if the child View consumes the event, the dispatchTouchEvent method returns true that means I handled the event, So the event ends, the dispatchTouchEvent of the ViewGroup returns true and then the dispatchTouchEvent of the Activity returns true

//Activity.java
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
Copy the code

Summary: If the child View consumes the event, the event ends

3.2.2 if the childViewWhat about not consuming events?

Son View does not intercept events, then mFirstTouchTarget null, retired from circulation, call the dispatchTransformedTouchEvent method.

    if (mFirstTouchTarget == null) {
        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
    }
Copy the code

To summarize: 1. The View is not intercept events, callbacks to dispatchTransformedTouchEvent 2. Then we go to super.DispatchTouchEvent 3. Then the ViewGroup will behave like a child View, by default onTouchEvent, or onTouch if setOnTouchLister is set

3.3 ifViewGroupAnd the sonViewWhat happens if they don’t intercept

If neither ViewGroup nor child View intercepts (mFirstTouchTarget == NULL),dispatchTouchEvent returns false

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
Copy the code

The answer is obvious: it executes the Activity’s onTouchEvent method

3.4 How do I Distribute subsequent Events?

The handler for the event distribution has been found, and it looks like the task is complete. However, event distribution is actually a series of events including ACTION_DOWN, ACTION_MOVE, ACTION_UP and ACTION_CANCEL. What we have analyzed above is the process of ACTION_DOWN. How to deal with subsequent events?

	public boolean dispatchTouchEvent(MotionEvent ev) {
		    if(! canceled && ! intercepted) {if (actionMasked == MotionEvent.ACTION_DOWN
                             || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                             || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {   
                             ...
                         	//1. Traverse the subview
                         	//2. Check whether it is in the coordinate range
                            //3. Assign the event to mFirstTouchTarget
                            The distribution / / 4. If successful, alreadyDispatchedToNewTouchTarget assignment to true. }if (mFirstTouchTarget == null) {
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                TouchTarget target = mFirstTouchTarget;
                while(target ! =null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                               handled = true; } } predecessor = target; target = next; }}}Copy the code

We can see from the above that 1. The subsequent events will not loop through the sub-view, because the target View has been found, and 2 will be distributed directly via mFirstTouchTarget. If a View starts processing interception events, the subsequent sequence of events can only be handled by it

3.5 summary

  • The essence of event distribution is a recursive method, passed down, calleddispatchTouchEventMethod, find the handler of the event, which is the chain of responsibility pattern common in projects.
  • During the distribution process,ViewGroupthroughonInterceptTouchEventDetermines whether to intercept the event
  • During the distribution process,ViewDefault passonTouchEventHandle events
  • If the underlyingViewWithout consumption, the parent element is executed step up by defaultonTouchEventMethods.
  • If all theViewtheonTouchEventMethods all returnfalseIs executed toActivitytheonTouchEventMethod, event distribution ends.

4 Slide conflict resolution

We often encounter sliding conflict in development, for example, a page has horizontal and vertical sliding at the same time. In this case, we need to judge and intercept the event in Action_MOVE according to the situation. There are two common solutions to sliding conflict: 1. External interception 2. Internal interception

4.1 External interception method

The onInterceptTouchEvent template is provided as follows:

    // External interception: parent view.java
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean intercepted = false;
        // The parent view intercepts the condition
        boolean parentCanIntercept;

        switch (ev.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                intercepted = false;
                break;
            case MotionEvent.ACTION_MOVE:
                if (parentCanIntercept) {
                    intercepted = true;
                } else {
                    intercepted = false;
                }
                break;
            case MotionEvent.ACTION_UP:
                intercepted = false;
                break;
        }
        return intercepted;

    }
Copy the code

There is a problem with this approach. If ACTION_DOWN is handed to the child View, subsequent events should be sent directly to the child View. Why is this blocked by the parent View? Let’s look at the dispatchTouchEvent method again

    public boolean dispatchTouchEvent(MotionEvent ev) {
        final boolean intercepted;
        if(actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget ! =null) {
        	//1. Judge interception
            intercepted = onInterceptTouchEvent(ev);
        } 

        // Dispatch to touch targets.
        if (mFirstTouchTarget == null) {
        	//4. Subsequent events are directly assigned to the ViewGroup
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
        } else {
            while(target ! =null) {
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                	/ / 2. CancelChild are true
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                        target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    if (cancelChild) {
                       if (predecessor == null) {
                       	   //3.mFirstTouchTarget is set to null
                           mFirstTouchTarget = next;
                       } else {
                           predecessor.next = next;
                       }
                       target.recycle();
                       target = next;
                       continue;
                    }
                }
            }
        }
    }

    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            returnhandled; }}Copy the code

As shown above: 2. If intercepted is true, cancelChild is also true. DispatchTransformedTouchEvent method pass Action_CANCEL to child View 3. After cancelChild mFirstTouchTarget set is empty 4. MFirstTouchTarget is empty, Subsequent events are handled by the ViewGroup

So that’s why external interception works

4.2 Internal interception method

Next, take a look at the template code for internal interception

    / / the parent view. Java
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
            return false;
        } else {
            return true; }}/ / child view. Java
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        // The parent view intercepts the condition
        boolean parentCanIntercept;

        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                break;
            caseMotionEvent.ACTION_MOVE: getParent().requestDisallowInterceptTouchEvent(! parentCanIntercept);break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return super.dispatchTouchEvent(event);
    }
Copy the code

Internal intercept method is the initiative to the View, if the child View need to direct consumption, events or to the parent container handling internal intercept method mainly through requestDisallowInterceptTouchEvent control

Let’s see why we can do internal interception by calling this method

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        final boolean intercepted;
        // Only if ActionDown or mFirstTouchTarget is empty
        if(actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget ! =null) {
            final booleandisallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! =0;
            if(! disallowIntercept) { intercepted = onInterceptTouchEvent(ev); }}}Copy the code

As shown above, the principle is simple 1. Child View through requestDisallowInterceptTouchEvent control mGroupFlags values, If disallowIntercept is true, the onInterceptTouchEvent will not go to the onInterceptTouchEvent, and external intercepts will not be available. When external processing is needed, Set disallowIntercept to false

conclusion

This article summarizes the detailed process of event distribution mechanism from screen to View in detail. Here are a few questions for readers to check whether they have really mastered this knowledge

  • 1. Briefly describe how events are transferred from the screen toViewthe
  • 2. How many times is the chain of responsibility distributed in the event distribution process?
  • 3. Why are events distributed fromDecorView -> Activity -> PhoneWindow -> DecorView
  • 4. How many ways can slide conflicts be resolved? Introduce them separately
  • 5. If only inonInterceptTouchEventtheACTION_MOVETo intercept events fromViewGrouptoViewEach of theActionHow is it transmitted
  • 6. Click on theViewGroupOne of theViewAnd then the finger moves somewhere else and then lifts. How are the events distributed
  • 7.ViewtheOnTouchandOnTouchEventWhat does it matter?OnTouchandOnClickThe event?
  • 8. Write the pseudo-code for the long press event by hand

The resources

Thumb reporters into Android company, find out events the secret behind the distribution mechanism Daily asking | event is first to DecorView or to the Window? Reflection | Android events distribution mechanism, the design and implementation of a view event distribution, the interviewer asked six straight touches the soul, I pieces of abuse