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.
Touch
How do events get from the screen to usApp
- 2.
Touch
Event arrivesApp
How to pass to the corresponding page - 3.
Touch
How 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
ViewGroup
Whether to intercept the event, and how to deal with the interception and non-interception respectively?- The child
View
Whether to intercept the event, and how to deal with the interception and non-interception respectively? ViewGroup
And the sonView
Do 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.Touch
How 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 SystemServer
Part 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 aboutWindowManagerService
andBinder
Communicating across processes, the reader does not need to get bogged down in the fine details
Just understand the ultimate inSystemServer
In the process,WindowManagerService
According to the currentWindow
To create theSocketPair
Used for cross-process communication and simultaneous communicationApp
It was sent in the processInputChannel
Registered
After that,ViewRootImpl
In theInputChannel
I’m pointing to the right oneInputChannel
As aClient
End,fd
withSystemServer
In the process ofServer
thefd
compositionSocketPair
And 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.Touch
Event arrivesApp
How 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 knownview
Event 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 viewview
thedispatchKeyEventPreIme
Methods.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
.DecorView
In 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.1ViewRootImpl
Don’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 toAcitivity
After that, why not just hand it inDecorView
Start 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.Touch
How are events distributed internally once they arrive on the page
The following is our most common and common event distribution section
3.1 ViewGroup
Whether 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 ViewGroup
What 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 ViewGroup
What 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 sonView
Whether 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 childView
What 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 childView
What 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 ifViewGroup
And the sonView
What 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, called
dispatchTouchEvent
Method, find the handler of the event, which is the chain of responsibility pattern common in projects. - During the distribution process,
ViewGroup
throughonInterceptTouchEvent
Determines whether to intercept the event - During the distribution process,
View
Default passonTouchEvent
Handle events - If the underlying
View
Without consumption, the parent element is executed step up by defaultonTouchEvent
Methods. - If all the
View
theonTouchEvent
Methods all returnfalse
Is executed toActivity
theonTouchEvent
Method, 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 to
View
the - 2. How many times is the chain of responsibility distributed in the event distribution process?
- 3. Why are events distributed from
DecorView
->Activity
->PhoneWindow
->DecorView
- 4. How many ways can slide conflicts be resolved? Introduce them separately
- 5. If only in
onInterceptTouchEvent
theACTION_MOVE
To intercept events fromViewGroup
toView
Each of theAction
How is it transmitted - 6. Click on the
ViewGroup
One of theView
And then the finger moves somewhere else and then lifts. How are the events distributed - 7.
View
theOnTouch
andOnTouchEvent
What does it matter?OnTouch
andOnClick
The 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