preface

series

1, Android input event: DecorView (1) 2, Android input event: DecorView (2


1, View/ViewGroup/ViewTree confusion 2, ViewGroup event distribution 3, View event distribution 4, ViewTree event distribution 5, event distribution series summary

A little metaphor

If you are confused about the code call process, let’s make a simple analogy: a school has 3 grades, each grade has 1 grade director, 3 classes, each class has a teacher, there are several students, one of them is called Xiaoming

One day, the principal received a task: there is a Japanese female student: Ishihara in the United States to come to the school to do exchanges. The principal assigned this task to go down, first go to the first grade director and ask your grade to accept this student? Grade director thought there are still several classes under hand, first asked 1 class director you can receive, 1 class teacher want so many students I ask who can take this Ishihara in the United States, first asked Xiao Ming. This procedure is called dispatchTouchEvent. Xiao Ming received the task, found no one under the hand (he is View, class teacher, grade director, principal is ViewGroup), can only take a look at the ishihara in the United States information, at this time he has two choices: Accept/reject Xiaoming looked at the information found ishihara in the United States is a little beauty, so happy to choose to accept, then promised to the teacher in charge, the teacher will tell the results of the grade director, grade director told the principal, the principal sighed, finally will throw off the burden. The process is dispatchTouchEvent returns the result. One day Rimi Ishihara’s classmate: Matsui heard that Rimi Ishihara was having a good time in China, so he wanted to come. The principal could not help but agree. Principal know ishihara limi exchanges in grade 1, students consider matsui and beauty is in the ishihara, better communication together, then directly handed the task 1 grade, director of class 1, grade 1 director know ishihara beauty in, then directly to the class 1, 1 class teacher know xiao Ming received ishihara in the United States, let xiao Ming with matsui (ishihara beauty is in the Down, Matsui is the follow-up to the Move, Up events), Xiao Ming mind ten thousand grass mud horse, who called their impulse (their own XXX, kneeling also to complete). The process is that once a layout handles a Down event, it is notified of subsequent events.

Back to the past time, xiao Ming is a student for integrity, said he studies very busy, the book has its own gold room, has its own beautiful woman in the book, don’t have time to accompany the ishihara in the United States, he told the teacher in charge he didn’t pick up the task at this moment, the teacher in charge, nobody would like to take this work in the class, fortunately there is a plan B, we can first take a look at yourself not to complete the task. If you can not complete, or to the leadership to do it, to the grade director, grade director secretly glad that they have a plan B, found that their plan B can be completed, so they took Ishihara In person. The principal was happy to know that the grade director had received the assignment, and his plan B was finally off the table. This plan B is onTouchEvent, of course, Matsui finally gave the grade director belt. In the middle of an episode, the teacher looked at ishihara Rimei’s information and had a bold idea: his son and her similar grade, more communication to improve the son’s foreign language level, how good ah. So when he received the assignment from the grade director, he stopped it and told the grade director he could handle it himself. This process is onInterceptTouchEvent. Of course, Ming is not without opportunities to accompany Rimi Ishihara. The school has made rules before: Anyone can prohibit the superior not to send the task to oneself (the leadership can not privately intercept the task, at least have to inform the subordinate), Xiaoming used this provision, the teacher in charge of the bold idea broke a ground. Of course, this rule does not discriminate, including grade directors, principals have to abide by. This process is requestDisallowInterceptTouchEvent

Confusion in View/ViewGroup/ViewTree

Parent/subclass, parent/child layout

Some articles on the Internet do not distinguish between the two when drawing the diagram, which is easy to confuse people who are new to this content. View group (ViewGroup) :

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {}

public abstract class ViewGroup extends View implements ViewParent, ViewManager {}
Copy the code

ViewGroup is a subclass of View, and View is the parent class of ViewGroup. The parent/subclass relationship is a language category relationship:

super.doMethod(xx)
Copy the code

Take a look at the contents of a common layout file:

<? The XML version = "1.0" encoding = "utf-8"? > <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"  android:background="@color/colorGreen" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:context=".MainActivity"> <View android:background="@color/colorAccent" android:layout_width="100dp" android:layout_height="100dp"> </View> </FrameLayout>Copy the code

FrameLayout is a subclass of ViewGroup. In this layout file, FrameLayout is the parent layout and View is the child layout. As can be seen from the name of ViewGroup, ViewGroup is a collection of views, of course, ViewGroup can also be a collection of viewgroups (nested). Parent/child layout relationships are defined in ViewGroup/View. Child layout finds parent layout

    public final ViewParent getParent() {
        return mParent;
    }
Copy the code
  • ViewParent is an interface implemented by ViewGroup and View View PL
  • After obtaining the mParent, it needs to be forcibly converted to the corresponding type
  • Each time a child layout is added to the parent layout, the parent layout is assigned to mParent (assignParent(xx)).
  • RootView(e.g. DecorView) mParent points to ViewRootImpl (specified in setView(xx))

The parent layout adds the child layout to the one-dimensional array using the addView(xx) method, so finding the child layout by the parent is the process of accessing the group number:

    public View getChildAt(int index) {
        if (index < 0 || index >= mChildrenCount) {
            return null;
        }
        //private View[] mChildren;
        return mChildren[index];
    }
Copy the code

ViewTree build

The simplest ViewTree structure is to know the parent layout, the child layout relationship, and add the child layout to the parent layout. Add the parent layout as a child of another parent layout, and the ViewTree adds another layer. The resulting ViewTree is as follows:

Note: ViewTree is not a class, but is named for the convenience of describing the layout hierarchy of views/Viewgroups.

It follows that:

  • View is the parent class of ViewGroup
  • A View can only be a child layout, not a parent layout
  • ViewGroup can do both parent and child layouts

Having clarified the above concepts, we will proceed to the main topic.

ViewGroup event distribution

Review the previous article:

2. The DecorView calls the parent’s dispatchTouchEvent(xx) method to continue distributing the event

As you can see, the focus here is on the parent’s dispatchTouchEvent(xx) method. DecorView inherits from FrameLayout, looks for the method in FrameLayout, and finds that FrameLayout does not override the method. FrameLayout inherits from the ViewGroup and finds dispatchTouchEvent(xx) in the ViewGroup. So the DecorView ends up calling the dispatchTouchEvent(xx) method of the ViewGroup.

A little example

addView(View1)
addView(View2)
addView(View3)
addView(View4)
Copy the code

View1, View2, View3 intersects at “1” View3, View4 intersects at “2” when “1” and “2” are clicked, how does the event pass? Conclusion first: When clicking the “1” position:

1, ViewGroup first receive events, and determine whether 1 position on some layout within 2 and four ViewGroup layout, reverse traversal search, namely View4 – > find 3, first determine the View1 order View4, “1” not in View4, If View3 does not want to process the event, then the event is not passed to View1 or View2. If not, continue to judge View2; 4, View2 is not processed, continue to judge View1. 5. When View3, View2, and View1 do not handle events, only their parent layout, ViewGroup, can be given. 6. When a ViewGroup does not want to process itself, it returns to its parent layout, which operates in the same way that a ViewGroup distributes events.

When clicking on the “2” position, it is similar to clicking on the “1” position and will not be repeated. The ViewGroup dispatchTouchEvent(xx) method does the following:

ViewGroup dispatchTouchEvent(MotionEvent ev)

ViewGroup.java public boolean dispatchTouchEvent(MotionEvent ev) { ... // flag whether the event has been handled Boolean handled = false; / / if there is no obscured the View, you can receive events if (onFilterTouchEventForSecurity (ev)) {final int action = ev. The getAction (); final int actionMasked = action & MotionEvent.ACTION_MASK; ActionMasked == motionEvent.action_down) {if (actionMasked == motionEvent.action_down) { That is the beginning of a sequence of events / / empty before / / empty touchTarget list cancelAndClearTouchTargets (ev); resetTouchState(); } // Flag whether to intercept the event final Boolean intercepted; MFirstTouchTarget! MFirstTouchTarget! = null indicates that a child layout handled the Down event, Is the layout in receipt of the Down event return true / / mFirstTouchTarget - > to head the if (actionMasked = = MotionEvent. ACTION_DOWN | | mFirstTouchTarget! = null) { DisallowIntercept =true disallowIntercept ------(1) Final Boolean disallowIntercept= (mGroupFlags &) FLAG_DISALLOW_INTERCEPT) ! = 0; if (! DisallowIntercept) {// Can intercept the event, Call ViewGroup onInterceptTouchEvent ------ (2) // The return value indicates whether the event has been handled intercepted = onInterceptTouchEvent(ev); Ev.setaction (action); } else {// Interception is not allowed, then intercepted = false; }} else {// If there is no Down event and no sub-layout has handled the Down event, the ViewGroup has intercepted the event intercepted = true; }... TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; // The event was not cancelled and was not intercepted. canceled && ! intercepted) { ... if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int actionIndex = ev.getActionIndex(); ViewGroup Final int childrenCount = mChildrenCount; If (newTouchTarget == null && childrenCount! = 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); / / receive events generated child layout according to the drawing order list, is generally ignored final ArrayList < View > preorderedList = buildTouchDispatchChildList (); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; AddView (xx1) addView(xx2) for (int I = childrenCount - 1; i >= 0; I -) {/ / make sure the child of the final layout to be detected int childIndex = getAndVerifyPreorderedIndex (childrenCount, I, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); . / / child. CanReceivePointerEvents () - > can receive events, Is the Visible and invisible Visible child layout if we can't receive events / / isTransformedTouchPointInView () - > check whether the points of the current click falls within the child layout, The effect of the padding/scroll/matrix on the position of the sublayout is considered when detecting if (! child.canReceivePointerEvents() || ! isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); // If the conditions are not met, skip the sublayout and continue looking for another layout. } newTouchTarget = getTouchTarget(child); if (newTouchTarget ! = null) { newTouchTarget.pointerIdBits |= idBitsToAssign; // Break out of the loop without having to find the next sublayout; } / / the child layout has not been processed distributed to the child Down / / events layout - > child -- -- -- -- -- - (3) if (dispatchTransformedTouchEvent (ev, false, child, IdBitsToAssign)) {// Return true-> The Down event has been processed by the sub-layout mLastTouchDownTime = ev.getdowntime (); . mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); //mFirstTouchTarget points to the current list header (mFirstTouchTarget has a value). newTouchTarget = addTouchTarget(child, idBitsToAssign); / / mark Down event has found the handler alreadyDispatchedToNewTouchTarget = true; break; }... } if (preorderedList ! = null) preorderedList.clear(); }... If (mFirstTouchTarget == null) {if (mFirstTouchTarget == null) {if (mFirstTouchTarget == null) { null ------(4) handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else {// Find the TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target ! = null) { final TouchTarget next = target.next; If (alreadyDispatchedToNewTouchTarget && target = = newTouchTarget) {/ / Down event, before already processed, here direct labeling has handled li = true; } else {/ / Down outside of other events, such as Move Up final Boolean cancelChild = resetCancelNextUpFlag (child) target. | | intercepted. //target.child is the target child layout, Distributed to it -- -- -- -- -- - (5) if (dispatchTransformedTouchEvent (ev, cancelChild, target. The child, target pointerIdBits)) {/ / return true, Mark handled handled = true; } if (cancelChild) {// If it has been cancelled, then skip the current and continue looking for the next node}} // Continue looking for the next node that needs to be dealt with generally, the linked list usually has only one node predecessor = target; target = next; }}... }... // Return dispatchTouchEvent(xx) this method handles the event result //true-> handled false-> unhandled ------(6) return handled; }Copy the code

Note that, for ease of understanding, we use a sub-layout instead of a sub-view, which can be a View or a ViewGroup. (1) Disable blocking flags:

ViewGroup.java public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! = 0)) {return = 0; } / / tag set the if (disallowIntercept) {mGroupFlags | = FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } if (mParent ! = null) {/ / recursive calls the superclass until ViewRootImpl mParent. RequestDisallowInterceptTouchEvent (disallowIntercept); }}Copy the code

This method is uniquely ViewGroup, if some layouts, didn’t want his father to intercept the sequence of events, so call getParent (). RequestDisallowInterceptTouchEvent (true). This method sets the parent layout all the way up, meaning that the parent layout at all levels above the child layout does not intercept events

(2) Only ViewGroup has onInterceptTouchEvent(xx); View does not. This method is used to intercept events before they are distributed to child layouts.

ViewGroup.java public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.isFromSource(InputDevice.SOURCE_MOUSE) && ev.getAction() == MotionEvent.ACTION_DOWN && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY) && IsOnScrollbarThumb (ev.getx (), ev.gety ())) {return true; } return false; }Copy the code

If you do not override the onInterceptTouchEvent(xx) method, events are not intercepted by default. Note when overriding this method for interception:

OnInterceptTouchEvent (xx) is intercepted from a Down event. If no sub-layout processes a Down event, the subsequent Move event (xx) and Up event (xx) are not received. ViewGroup dispatchTouchEvent(xx) is rarely overwritten. OnInterceptTouchEvent (XX) + onTouchEvent(xx) is used to handle events

(3) Distribute events to sublayouts

ViewGroup.java private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, Int desiredPointerIdBits) {// Child refers to the sublayout to receive the event final Boolean handled; . If (child == null) {// If no child layout receives Down events, So the Down/Move/Up event calls the parent class of the ViewGroup, which is the View, so it calls the View's dispatchTouchEvent(xx) method handled = super.dispatchTouchEvent(transformedEvent); } else {final float offsetX = mscrollx-child-mleft;} else {final float offsetX = mscrollx-child-mleft; final float offsetY = mScrollY - child.mTop; / / the Event location deviation, make it fall within the child layout transformedEvent. OffsetLocation (offsetX, offsetY); // consider the effect of matrix on position if (! child.hasIdentityMatrix()) { transformedEvent.transform(child.getInverseMatrix()); } // The Event location is offset from the upper left corner of the current child layout. // The Event location is distributed to the child layout. The child may be a View or a ViewGroup. Until it returns handled = child.dispatchTouchEvent(transformedEvent); } // Return handled; }Copy the code

2. MotionEvent modiates AXIS_X and AXIS_Y values, which are the values obtained by event.getx () and event.gety (). This is why these two values are located at the top left corner of the current View

(4) The method here

handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); Null is passed in, so View dispatchTouchEvent(xx) is handled

(5) When a sub-layout processes the Down event, subsequent events such as Move and Up will be sent directly to the sub-layout that originally processed the Down event. It can be seen that:

A Down event is the start of a sequence of events (Down->Move->Up). If a layout does not handle a Down event, subsequent events will not be received. The sub-layout can still receive the complete sequence of events

(6) The final event processing result is reflected in a Boolean value. True -> Indicates that the event is handled false -> Indicates that the event is not handled Note:

Returns true to tell the caller that the event was handled, regardless of whether the event was “truly handled”. Return false to tell the caller that the event was not processed, even if “a lot of processing” was done to the event.

The above analysis of a potentially confusing bunch of ViewGroup distribution events represented by a graph:

ViewGroup Event distribution method

View Event Distribution

From the ViewGroup distribution logic, we can see that the process of ViewGroup event distribution is to recursively find the sub-layout and distribute. So what are the conditions for the recursion to end?

2. If there is no sub-layout to handle the event, you can only receive it (not processing it), then call super.dispatchTouchEvent(xx). That is the dispatchTouchEvent (xx)

ViewGroup dispatchTouchEvent(xx)

View dispatchTouchEvent(MotionEvent ev)

public boolean dispatchTouchEvent(MotionEvent event) { ... If (actionMasked == motionevent.action_down) {// stop sliding stopNestedScroll(); } if (onFilterTouchEventForSecurity(event)) { if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) { result = true; } // If the onTouch callback is registered, the onTouch method is executed // If the method returns true, the event is considered handled. if (li ! = null && li.mOnTouchListener ! = null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } // The event is not handled, then onTouchEvent is called to handle if (! result && onTouchEvent(event)) { result = true; }}... if (actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && ! result)) { stopNestedScroll(); } return result; }Copy the code

OnTouch is registered by:

    public void setOnTouchListener(OnTouchListener l) {
        getListenerInfo().mOnTouchListener = l;
    }
Copy the code

If onTouch(xx) handles the event, onTouchEvent(xx) will not be called

onTouchEvent(MotionEvent event)

Public Boolean onTouchEvent(MotionEvent event) {final float x = event.getx (); final float y = event.getY(); Final int viewFlags = mViewFlags; final int viewFlags = mViewFlags; Final int action = event.getAction(); // If the View is set to: You can click, can grow in the press and the right mouse button pop-up (rarely) a / / so that the View can click -- -- -- -- -- -- -- (1) the final Boolean clickable = ((viewFlags & clickable) = = clickable | | (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE; If ((viewFlags & ENABLED_MASK) == DISABLED) {// The View defaults to ENABLED, if DISABLED, return clickable; } if (mTouchDelegate ! < span style = "box-sizing: border-box; color: RGB (51, 51, 51); line-height: 25px; font-size: 14px! Important; word-break: normal;" Through this method to determine whether a click on the coordinates in the layout of "expanding area" / / is the son is the event to the layout processing -- -- -- -- -- -- -- -- - (2) if (mTouchDelegate. OnTouchEvent (event)) {return true; }} / / the View can be displayed or click mouse suspension (rarely) if (clickable | | (viewFlags & TOOLTIP) = = TOOLTIP) {switch (action) {case MotionEvent.ACTION_UP: ... boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) ! = 0; If ((mPrivateFlags & PFLAG_PRESSED)! = 0 || prepressed) { ... Boolean focusTaken = false; Boolean focusTaken = false; if (isFocusable() && isFocusableInTouchMode() && ! isFocused()) { focusTaken = requestFocus(); } //mHasPerformedLongPress indicates whether the long press event has already handled the event // If it has, clicking on the event will not execute if (! mHasPerformedLongPress && ! RemoveLongPressCallback (); mIgnoreNextUpEvent) {removeLongPressCallback(); // If the previous step was to request focus and succeeded, no subsequent events are processed this time if (! FocusTaken) {if (mPerformClick == null) {// Implement the Runnable interface class, used to call mPerformClick = new PerformClick(); } // In order to give the View enough time to update the other state, we send the Handler to the main thread to execute ------(3) if (! Post (mPerformClick)) {// If unsuccessful, call performClickInternal() directly; }}}... } mIgnoreNextUpEvent = false; break; case MotionEvent.ACTION_DOWN: ... boolean isInScrollingContainer = isInScrollingContainer(); if (isInScrollingContainer) { ... / / in a scrollable container, for the sake of fault tolerance, delay click postDelayed (mPendingCheckForTap ViewConfiguration. GetTapTimeout ()); } else {// Set the pressed state, used for background/foreground etc. SetPressed (true, x, y); / / open a long press delay events, when delay events to execute the events (long press events) / / ViewConfiguration. GetLongPressTimeout long () is the threshold value of time. It may be different on different systems // I have 400MS on my phone, The default value is 500 ms -- -- -- -- -- -- -- -- -- -- (4) checkForLongClick (ViewConfiguration. GetLongPressTimeout (), x, y, TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__LONG_PRESS); } break; case MotionEvent.ACTION_CANCEL: ... break; case MotionEvent.ACTION_MOVE: ... break; } // If clickable=true, the event is handled. } return false; }Copy the code

(1) CLICKABLE/LONG_CLICKABLE related assignments:

View.java
    public void setClickable(boolean clickable) {
        setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
    }
    public void setLongClickable(boolean longClickable) {
        setFlags(longClickable ? LONG_CLICKABLE : 0, LONG_CLICKABLE);
    }
Copy the code

Attributes can currently be specified in XML as well. OnTouchEvent (xx) can still be executed without setting the above properties. There are two ways to do this:

The View CLICKABLE/LONG_CLICKABLE attribute is not set by default. For example, TextView does not have CLICKABLE set, but Button has CLICKABLE set by default. Just call the setOnClickListener (xx)/the setOnLongClickListener, The view.setClickable (xx)/ view.setLongClickable (xx) methods are called internally

(2) Generally speaking, the content area remains the same. There are two ways to expand the click area of a View:

Set the padding 2. Set the TouchDelegate

A quick word about TouchDelegate, as the name suggests, the Touch event delegate. The usage method is as follows:

Post (new Runnable() {@override public void run() {Rect areaRect = new Rect(); // Get the original region view.getHitRect(areaRect); Arearect. left -= 100; areaRect.top -= 100; areaRect.right += 100; areaRect.bottom += 100; View parentView = (View)view.getParent(); TouchDelegate = new TouchDelegate(areaRect, view); // Set the proxy for the parent layout, event flow: OnTouchEvent for the child layout -> onTouchEvent for the parent layout -> Fall within the expanded area -> change the coordinate value -> dispatchTouchEvent for the child layout -> onTouchEvent for the child layout parentView.setTouchDelegate(touchDelegate); }});Copy the code

(3) PerformClick is finally called

public boolean performClick() { ... final boolean result; final ListenerInfo li = mListenerInfo; if (li ! = null && li.mOnClickListener ! = null) {/ / sound feedback playSoundEffect (SoundEffectConstants. CLICK); / / as the onClick li. MOnClickListener. OnClick (this); // As long as onClick is executed, the event is considered handled result = true; } else { result = false; }... return result; }Copy the code

We usually set the click event for the View:

public void setOnClickListener(@Nullable OnClickListener l) { if (! isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }Copy the code

That’s when it’s called back. (4) Let’s see if the long-press event finally calls the Run method in CheckForLongPress

public void run() { if ((mOriginalPressedState == isPressed()) && (mParent ! = null) && mOriginalWindowAttachCount == mWindowAttachCount) { recordGestureClassification(mClassification); If (performLongClick(mX, mY)) {// mHasPerformedLongPress = true; } } } private boolean performLongClickInternal(float x, float y) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED); boolean handled = false; final ListenerInfo li = mListenerInfo; if (li ! = null && li.mOnLongClickListener ! = null) {/ / execution setOnLongClickListener (xx) registered callback handled. = li mOnLongClickListener. OnLongClick (View. This); }... if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } return handled; }Copy the code

The difference between a long press and a short press

  • Overwrite onLongClick(View v), whose return value determines whether to execute onClick(View v). If true, the long press event has been handled
  • Overwrite onClick(View v), this method has no return value, execute this method will not execute onLongClick(View v)

Graph the View event distribution flow:

View event distribution common methods

It is worth noting that onTouch(XX) callback and short press operation difference:

The short press is triggered when an Up event is received, whereas onTouch(xx) is triggered whenever an event is received

ViewTree event distribution

The above analyses the event distribution process of ViewGroup and View respectively, and many viewgroups and views form the ViewTree structure. Associate the dispatchTouchEvent and onTouchEvent of the parent and child layouts as shown:

  • If the parent dispatchTouchEvent handled the event, the child’s dispatchTouchEvent will not receive the event
  • If the onTouchEvent of the child layout handles the event, the onTouchEvent of the parent layout will not receive the event

The difficulties of ViewGroup/View event distribution are as follows:

Figure out the child/parent layout, View/ViewGroup inheritance

Event distribution series summary

The Android event distribution series is divided into three articles to describe the Android input event to the bottom of the source. (1) Analyze the App layer receives events from the bottom of the viewrotimpl.java processing

(2) Analyze how a DecorView handles an event

(3) The previous event was not processed, and the mission is completed after being transferred to this point. That is why this article is called “The reason for the View to receive the disk”.

Many articles on the web describe DecorView in conjunction with View/ViewGroup event handling, without specifying the difference between the two. From this series of articles, we have learned that event handling by DecorView is not required, except when a DecorView is used as a RootView (such as an Activity, Dialog, etc.). Put the three in series:

Generally speaking, we often come across the second part, the third part, especially the third part. It is suggested to combine the corresponding articles of these three parts, partial —-> whole —-> partial, so as to have a clearer understanding of the whole event distribution. This article is based on Android 10.0 source code

If you like, please like, your encouragement is my motivation to move forward.