☞ Reading text

  1. Situation (Situation)
  2. Conflict (Complication)
  3. Question (Question)
  4. The Answer (the Answer)
    1. Analyze the
      1. The argument
        1. Took three chapters
        2. point
      2. arguments
        1. The human-computer interaction
        2. The View tree
        3. The class diagram
        4. annotation
        5. DecorView
        6. WindowCallbackWrapper
        7. Activity
        8. PhoneWindow
        9. ViewGroup
        10. View
      3. Flow of events
    2. argument
  5. A picture
  6. standard
  7. Common mistakes
  8. Best practices
  9. fishing
  10. methodology
  11. tool
  12. the
  13. The advanced
  14. reference
  15. A long song

Situation (Situation)

1. Focusing on the mobile Internet for several years, as a high P, I [applause] have a vague understanding of the event distribution mechanism. Unprofessional. I can’t stand it. 2. View tree recursive nesting logic so that the majority of the first line of peer clouds in the fog, at a loss.

Conflict (Complication)

1. There are a lot of blogs on the Internet with a lot of description information points (but ACTION_CANCEL description is very few), so I feel confused after reading them. 2. Event distribution is mainly used to solve the conflict problem caused by custom cool controls and sliding nesting (the program silly silly can not distinguish is horizontal slide or vertical slide), found that peers have all kinds of writing, thunder is everywhere [people sit at home, pot from heaven].





Question (Question)

1. Is there a systematic analysis routine? 2. Point out common mistakes and give best practices? 3. Give a picture clearly for easy reference. 4. Can “fish” and “fishing” be both?

The Answer (the Answer)

Analyze the

The argument

Took three chapters

1. At the individual level, this article only contains single touch events (ACTION_DOWN, ACTION_MOVE, ACTION_UP, ACTION_CANCEL). I don’t think it has anything to do with event distribution, so I’ll skip it. 3. A family’s opinion is presumptuous, and a family listens in vain.

point

1. Consistency Guarantees: Press to begin, and it may end with movement, release, or cancellation. ACTION_DOWN -> ACTION_MOVE(*) -> ACTION_UP/ACTION_CANCEL. 2. The dispatchTouchEvent method of the View class completes the event consumption processing, and the dispatchTouchEvent method of the ViewGroup completes the event distribution processing. Normally, it is not recommended to override this method to change the system event distribution mechanism. 3. The onInterceptTouchEvent method of the ViewGroup class completes the event interception. The ViewGroup on the event distribution path must call the onInterceptTouchEvent method when ACTION_DOWN or when the event is not directly consumed by itself. 4. View class onTouchEvent method to complete the specific processing event consumption, that is, trigger click listener (OnClickListener) and long time click listener (OnLongClickListener) and key state, focus related processing. 1. If OnTouchListener is set, OnTouchListener will be called first. If onTouch returns true, onTouchEvent will not be called and consumption will be directly returned. 2. If the TouchDelegate is set, the TouchDelegate will be called first in onTouchEvent. If the onTouchEvent class returns true, the TouchDelegate will be returned directly. 3. If the View is clickable, execute onTouchEvent and return true; 1. ACTION_DOWN: set the button flag bit as pressed state, and trigger the delay (500ms) executive click event. 2. ACTION_MOVE: If the coordinate of the button is beyond the control area, set the button flag bit to non-pressed state, and remove the delayed pressing and clicking event triggered by ACTION_DOWN. 3. ACTION_UP: If the button flag bit is in pressed state and the long-press and click event triggered by ACTION_DOWN has not been executed, remove the long-press and click event and execute the click event. 4. ACTION_CANCEL: Set the button flag bit to non-pressed state, remove the delayed press and click event triggered by ACTION_DOWN. 4. Otherwise unclickable, return false;

arguments

Based on Android 8.0 (API Level 28) source code parsing

The human-computer interaction

appreciation

User keypress behavior -> phone sensor ->ViewRootImpl->DecorView->WindowCallbackWrapper->Activity->PhoneWindow->DecorView->ViewGroup*->View-> programmer’s Code logic -> hardware (display, speaker, etc.) response output -> user awareness

The View tree

appreciation

1. Views are organized in a tree structure with nodes as ViewGroup or View. A ViewGroup can have multiple child nodes, whereas a View has no child nodes. 2. In Android, the root node of the View tree is DecorView (parent View is FrameLayout and belongs to ViewGroup). 3. On Android, the id of the View sub-root node is Android: ID/Content. {:.info}

The class diagram

appreciation

1. ViewRootImpl is the logical starting point for the Android layer and is used to receive event messages from the underlying system. View management class, not View itself. (BTW: Measure, layout and draw of View drawing process are also triggered by this class.) 2. DecorView is the root node of the Android View tree that holds the Window object. The actual event distribution capability of the DecorView (inheriting the event distribution capability of the parent ViewGroup and View classes) can be implemented directly, but the event distribution directly calls the Window, passing it indirectly to the Activity’s event distribution, and then the Activity calls back to the DecorView’s actual event distribution capability. Corresponds to the ring dependence in the diagram. 3. The Activity is a page in Android, and the actual event distribution is triggered by this class’s dispatchTouchEvent. (Easter Eggs: If you want to make your interface inoperable, making the View transparent is a bit low, just override this method.) 4. ViewGroup is responsible for event distribution and interception. Pressed events are handled differently from subsequent events (move, release, or cancel). 1. Press the event button to determine whether to intercept the event. 1. If not intercepted, the distribution event to find the target consumption sub-view (reverse order traversal sub-view, recursive call sub-view event distribution, determine whether there is a sub-view consumption. MFirstTouchTarget stores the target consumer child View object. 1. If there is a subview consumption, then the target subview consumption event. 2. Otherwise try consuming events yourself. 2. Otherwise, try consuming events yourself. 1. If the target consumer sub-view is found by pressing the event, it will judge whether to intercept. Otherwise, it will not be blocked. 2. If there is a target consumer sub-view, then according to whether to intercept. 1. If no interception occurs, subsequent events are normally transmitted. 2. If there is an interception, the current event is converted to a cancel event and sent to the target consumer sub-view, and the target consumer sub-view is reset to null, and subsequent events directly attempt to consume the event themselves (regardless of whether the consumption, subsequent events will receive & attempt to process event distribution); 3. Otherwise try consuming events yourself. (Will not call whether to intercept, actually intercept or not to intercept, are their own consumption events.) 5. View is responsible for event consumption event processing. 1. Call onTouch of mOnTouchListener. 1. If consumed, return true; 2. Otherwise, continue to call onTouchEvent; 1. If it is enabled, clickable is returned. 2. Otherwise, call mTouchDelegate’s onTouchEvent. 1. If consumed, return true; 2. Otherwise, 1. If the clickable (clickable) 1. Event flow (ACTION_DOWN, ACTION_MOVE, ACTION_UP, ACTION_CANCEL) processing (including focus, key status, key and long key); 2. Return true. 2. Otherwise return false;

annotation

DecorView
/** * Decor. * View tree root node. The ViewRootImpl first calls dispatchPointerEvent (implemented in the parent View class). * The event call forms a loop in the DecorView. (First send it to the Activity through the Window, */ public class extends FrameLayout {private PhoneWindow mWindow; @override public Boolean dispatchTouchEvent(MotionEvent ev) {// // Actual event distribution is called back by the Activity to superDispatchTouchEvent (the event distribution handling of the ViewGroup). // Call the WindowCallbackWrapper object of Window to continue distribution. final Window.Callback cb = mWindow.getCallback();returncb ! = null && ! mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); } public Boolean superDispatchTouchEvent(MotionEvent event) {// Call the parent ViewGroup for event distribution.returnsuper.dispatchTouchEvent(event); }}Copy the code
WindowCallbackWrapper
/** * Wrapper means Wrapper material. * A real shell around the Activity. */ public class WindowCallbackWrapper implements Window.Callback { final Window.Callback mWrapped; @override public Boolean dispatchTouchEvent(MotionEvent event) {// Send a relay event to the Callback (Activity).returnmWrapped.dispatchTouchEvent(event); }}Copy the code
Activity
/** * An Activity is a shell with no event distribution mechanism. If there is no consumption in the View tree, the Activity will miss out. */ public class Activity implements Window.Callback { private Window mWindow; public boolean dispatchTouchEvent(MotionEvent ev) {if(ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } // pass to the Window (PhoneWindow) relay event.if(getWindow().superDispatchTouchEvent(ev)) {// View tree consumes the eventreturn true; } // If the View tree does not consume events, the Activity has an opportunity to consume events. // Revelation: If the View tree consumes events, if the parent ViewGroup intercepts them during subsequent events after the event is pressed, // the subsequent consumption state returned has no effect on the event stream, but it does affect the Activity (the View number does not consume, and the Activity has a chance to consume).returnonTouchEvent(ev); } public Boolean onTouchEvent(MotionEvent event) {public Boolean onTouchEvent(MotionEvent event)if (mWindow.shouldCloseOnTouch(this, event)) {
            finish();
            return true;
        }

        return false; }}Copy the code
PhoneWindow
/** * PhoneWindow is also a shell that forwards events back to the DecorView distribution process. */ public class PhoneWindow extends Window { private DecorView mDecor; @override public Boolean superDispatchTouchEvent(MotionEvent event) {// Send the event to the DecorView relay. Start event distribution and consumption in viewgroups and Views).returnmDecor.superDispatchTouchEvent(event); }}Copy the code
ViewGroup
/** * ViewGroup; * dispatchTouchEvent Completes the time distribution logic. * onInterceptTouchEvent: it is an event intercepting interface. The parent control can actively intercept events for its own consumption. Otherwise, it can only be detected when the child Viwe tree does not consume. */ public implements class extends View implements ViewParent {@override public Boolean dispatchTouchEvent(MotionEvent ev) {if(mInputEventConsistencyVerifier ! = null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } // If the event targets the accessibility focused view and this is it, start // normal event dispatch. Maybe a descendant is what will handle the click.if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
            ev.setTargetAccessibilityFocus(false);
        }

        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event forThe Previous Gesture // Due to an app switch, ANR, or some other state change. (Only external interception method to resolve sliding conflict partners should note this reset, interception call must be done after this.) cancelAndClearTouchTargets(ev); resetTouchState(); } // CheckforInterception. // Whether to interception final Boolean intercepted; // Intercept condition 1, either by pressing the event or by not consuming the event directly yourself.if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! = null) {// Interception condition 2, allowing interception switch on. / / (default is turned on, the other View can call requestDisallowInterceptTouchEvent, / / as the child View off the parent View, sliding conflict external intercept method is by calling the interface control parent View intercept). Final Boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT)! = 0;if(! DisallowIntercept) {// Intercept control is invoked only if two conditions are met (only by overriding this method, no intercept by default). intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore actionin case it was changed
                } else {
                    intercepted = false; }}else{ // There are no touch targets and this action is not an initial down // so this view group continues to intercept [Touches] // I don't think I've ever seen this, but maybe [when I fart] intercepted =true;
            }

            // If intercepted, start normal event dispatch. Also if there is already
            // a view that is handling the gesture, do normal event dispatch.
            if(intercepted || mFirstTouchTarget ! = null) { ev.setTargetAccessibilityFocus(false);
            }

            // Check for cancelation.
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            // Update list of touch targets for pointer down, ifneeded. final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) ! = 0; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget =false; // Recursively find the target consumer sub-view condition 1: the event is not cancelled, nor is it blockedif(! canceled && ! intercepted) { // If the event is targeting accessiiblity focus we give it to the // view that has accessibility focus andifit does not handle it // we clear the flag and dispatch the event to all children as usual. // We are looking up the accessibility focused host to avoid keeping // state since these events are very rare. View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus() : null; // Recursively find the target consumer sub-view condition 2: the event must be a press event. [Multi-touch is not on the table, but NEITHER am I]if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    // Clean up earlier touch targets for this pointer id in case they
                    // have become out of sync.
                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;
                    if(newTouchTarget == null && childrenCount ! = 0) { finalfloat x = ev.getX(actionIndex);
                        final floaty = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. Skip the final ArrayList < View > preorderedList = buildTouchDispatchChildList (); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; // Reverse order traversal, draw behind the View, overlay on topfor (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                            // If there is a view that has accessibility focus we want it
                            // to get the event first and if not handled we will perform a
                            // normal dispatch. We may do a double iteration but this is
                            // safer given the timeframe.
                            if(childWithAccessibilityFocus ! = null) {if(childWithAccessibilityFocus ! = child) {continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } // Consume event View qualification 1: The coordinates of the event are in the View area.if(! canViewReceivePointerEvents(child) || ! isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if(newTouchTarget ! = null) { // Child is already receiving touch within its bounds. // Give it the new pointerin addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break; } resetCancelNextUpFlag(child); // Consume events View qualification 2: Consume events yourself or a sub-view tree. Enter recursive event distribution.if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if(preorderedList ! = null) { // childIndex points into presorted list, find original indexfor (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break; }}}else{ mLastTouchDownIndex = childIndex; } mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); // Mark the current View as the target consumption child View, the consumption path is the parent View mark direct child View (distribution is not needed). There is no span. // I don't understand why the whole chain structure stores the target consumer child View. I don't have more than one target consumer subview. NewTouchTarget = addTouchTarget(Child, idBitsToAssign); alreadyDispatchedToNewTouchTarget =true;
                                break;
                            }

                            // The accessibility focus did not handle the event, so clear
                            // the flag and do a normal dispatch to all children.
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if(preorderedList ! = null) preorderedList.clear(); }if(newTouchTarget == null && mFirstTouchTarget ! = null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget;while(newTouchTarget.next ! = null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; }} // Dispatch to touch targets. (Either blocked by itself, or the subview tree is not consuming)if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while(target ! = null) { final TouchTarget next = target.next; // If the event is pressed, the consumption state is set totrue
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else{ final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; // Non-pressed events will either continue to process consumption normally or will be blocked (events are converted to cancelled events or continue to be distributed to the target View)if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if(cancelChild) {// Clears the target consumer child View if it is a cancelChild event (either blocked or passed).if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            }

            // Update list of touch targets for pointer up or cancel, if needed.
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if(split && actionMasked == MotionEvent.ACTION_POINTER_UP) { final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); }}if(! handled && mInputEventConsistencyVerifier ! = null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } // return consumption statusreturnhandled; 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; } // Event distribution handles submethods that encapsulate part of the logic, Realize the cancellation event conversion private Boolean dispatchTransformedTouchEvent (MotionEvent event, Boolean cancel, View the child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. Wedo not need to perform any transformations
        // or filtering.  The important part is the action, not the contents.
        final int oldAction = event.getAction();
        if(cancel | | oldAction = = MotionEvent. ACTION_CANCEL) {/ / into a cancellation event event. The setAction (MotionEvent. ACTION_CANCEL);if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // Calculate the number of pointers to deliver.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        // If for some reason we ended up in an inconsistent state where it looks like we
        // might produce a motion event with no pointers in it, then drop the event.
        if (newPointerIdBits == 0) {
            return false;
        }

        // If the number of pointers is the same and we do not need to perform any fancy
        // irreversible transformations, then we can reuse the motion event for this
        // dispatch as long as we are careful to revert any changes we make.
        // Otherwise we need to make a copy.
        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);

                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }

        // Perform any necessary transformations and dispatch.
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // Done.
        transformedEvent.recycle();
        returnhandled; }}Copy the code

View
Public class View {public Final Boolean dispatchPointerEvent(MotionEvent event) { It starts with ViewRootImpl calling the DecorView method. // The dispatchTouchEvent method is then called to the DecorView.if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            returndispatchGenericMotionEvent(event); Public Boolean dispatchTouchEvent(MotionEvent event) {// If the event should be handled by accessibility focus first.if (event.isTargetAccessibilityFocus()) {
            // We do not have focus or no virtual descendant has it, do not handle the event.
            if(! isAccessibilityFocusedViewOrHost()) {return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if(mInputEventConsistencyVerifier ! = null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } final int actionMasked = event.getActionMasked();if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true; } //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; // Precedence mOnTouchListener consumption processing, if consumed, directly return consumedif(li ! = null && li.mOnTouchListener ! = null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result =true; } // Handle the consumption itself, encapsulated in onTouchEventif(! result && onTouchEvent(event)) { result =true; }}if(! result && mInputEventConsistencyVerifier ! = null) { mInputEventConsistencyVerifier.onUnhandledEvent(event, 0); } // Clean up after nested scrollsif this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we did not want the rest
        // of the gesture.
        if(actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && ! result)) { stopNestedScroll(); }returnresult; } // For the full event stream (ACTION\_DOWN -> ACTION\_MOVE(*) -> ACTION\_UP/ACTION\_CANCEL), keystroke listening, long keystroke listening, focus and keystroke status processing are completed. public boolean onTouchEvent(MotionEvent event) { finalfloat x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

        if((viewFlags & ENABLED_MASK) == DISABLED) {// The key is not enabled, and the clicked state is returned.if(action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) ! = 0) {setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            // A disabled view that is clickable still consumes the touch
            // events, it just does not respond to them.
            returnclickable; } // Valid touch proxy consumption events that can be used to extend click-hotspot control. If consumed, return consumed directly.if(mTouchDelegate ! = null) {if (mTouchDelegate.onTouchEvent(event)) {
                return true; }}if(clickable | | (viewFlags & TOOLTIP) = = TOOLTIP) {/ / can click to key processing. switch (action) {case MotionEvent.ACTION_UP:
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    if ((viewFlags & TOOLTIP) == TOOLTIP) {
                        handleTooltipUp();
                    }
                    if(! clickable) { removeTapCallback(); removeLongPressCallback(); mInContextButtonPress =false;
                        mHasPerformedLongPress = false;
                        mIgnoreNextUpEvent = false;
                        break; } boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) ! = 0; // Check the state of the key flag, and proceed only if the state is pressed.if((mPrivateFlags & PFLAG_PRESSED) ! = 0 || prepressed) { // take focusif we do not have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if(isFocusable() && isFocusableInTouchMode() && ! isFocused()) { focusTaken = requestFocus(); }if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed.  Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                        }

                        if(! mHasPerformedLongPress && ! MIgnoreNextUpEvent) {// This is a tap, so remove the longpress check  removeLongPressCallback(); // Only perform take click actionsif we were in the pressed state
                            if(! focusTaken) { // Use a Runnable and post this rather than calling // performClick directly. This lets other visual state  // of the view update before click actions start.if(mPerformClick == null) { mPerformClick = new PerformClick(); } // Execute the click event.if(! post(mPerformClick)) { performClick(); }}}if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }

                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if(! post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } removeTapCallback(); } mIgnoreNextUpEvent =false;
                    break;

                case MotionEvent.ACTION_DOWN:
                    if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                        mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                    }
                    mHasPerformedLongPress = false;

                    if(! clickable) { checkForLongClick(0, x, y);break;
                    }

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we are inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case// This is a scroll. // Set the key marker to down state and trigger the delay (500ms) of long scroll click event. // The following is the scroll and non-scroll processing.if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);
                        checkForLongClick(0, x, y);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    if (clickable) {
                        setPressed(false); } // Set the button flag bit to non-pressed state, remove the delayed ACTION_DOWN event. removeTapCallback(); removeLongPressCallback(); mInContextButtonPress =false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    break;

                case MotionEvent.ACTION_MOVE:
                    if(clickable) { drawableHotspotChanged(x, y); } // Be lenient about moving outside of buttons // Check whether the button coordinates are outside the View area.if(! PointInView (x, y, mTouchSlop) {// Outside button // Remove any future long press/tap checks // Also removes the delayed ACTION\_DOWN click event. removeTapCallback(); removeLongPressCallback();if((mPrivateFlags & PFLAG_PRESSED) ! = 0) {setPressed(false);
                        }
                        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                    }
                    break;
            }

            return true;
        }

        return false; }}Copy the code

Flow of events

DemoParentInterceptTouchEventActivity page git repository

Use the RULE MECE (Mad Exclusive, Collectively Exhaustive)

conditions The results of
1. Block parent control ACTION_DOWN

2. The parent control consumes events
1. Receive press event – DOWN – > Parent. DispatchTouchEvent – DOWN – > Parent. OnInterceptTouchEvent – true – > Parent. DispatchTouchEvent -down -> Parent.(super)dispatchTouchEvent{Parent handle transaction} -down -> Parent. OnTouchEvent -true-> The Parent. DispatchTouchEvent – true – > return true consumption state

2. Receive mobile events – a MOVE – > Parent. DispatchTouchEvent – MOVE – > Parent. (super) dispatchTouchEvent} {the Parent processing consumption – a MOVE – > Parent. Consumption state – > onTouchEvent – Parent. DispatchTouchEvent – true – > return true consumption state

3. Receive release events – UP – > Parent. DispatchTouchEvent – UP – > Parent. (super) dispatchTouchEvent} {the Parent processing consumption – UP – > Parent. OnTouchEvent – true – > Parent. DispatchTouchEvent – true – > return true consumption state
1. Block parent control ACTION_DOWN

2. The parent control does not consume events
4. Receive press event – DOWN – > Parent. DispatchTouchEvent – DOWN – > Parent. OnInterceptTouchEvent – true – > Parent. DispatchTouchEvent -down -> Parent.(super)dispatchTouchEvent{Parent handle transaction} -down -> Parent. OnTouchEvent -false-> Parent. DispatchTouchEvent – false – > return false consumption state

5. No movement event is received

6. 5
1. Parent control ACTION_MOVE interception

2. Child controls consume events
7. Receive press event – DOWN – > Parent. DispatchTouchEvent – DOWN – > Parent. OnInterceptTouchEvent – false – > Parent. DispatchTouchEvent -down -> child.dispatchTouchEvent {Parent dispatchEvent, Parent dispatchEvent, Child dispatchEvent, Child dispatchEvent} -down -> TargetChild OnTouchEvent {if there are more than one Child, Premise is in front of the Child are Child. Returns false} – true – > dispatchTouchEvent – true – > Parent. DispatchTouchEvent Child for the View} {record target consumer – true – > Returns the consumption state true

8. Receive mobile events – a MOVE – > Parent. DispatchTouchEvent – MOVE – > Parent. OnInterceptTouchEvent – true – > Parent. DispatchTouchEvent -CANCEL->Child (destination consumer Child). DispatchTouchEvent {Child processing consumer} -CANCEL-> child.onTouchEvent – consumption status -> Child.dispatchtouchevent – Consumption Status -> Return consumption status

9. Receive release events – UP – > Parent. DispatchTouchEvent – UP – > Parent. (super) dispatchTouchEvent} {the Parent processing consumption – UP – > Parent. OnTouchEvent Consumption status – > Parent. DispatchTouchEvent – consumption – > return consumption state
1. Parent control ACTION_MOVE interception

2. Child controls do not consume events

3. The parent control consumes events
10. Receive press event – DOWN – > Parent. DispatchTouchEvent – DOWN – > Parent. OnInterceptTouchEvent – false – > Parent. DispatchTouchEvent -down -> child. dispatchTouchEvent{Parent dispatchEvent, Parent dispatchEvent, Child dispatchEvent} -down -> TargetChild } -false-> Child.dispatchTouchEvent -false-> Child.dispatchTouchEvent Parent. DispatchTouchEvent Child} {no target consumer – DOWN – > Parent. (super) dispatchTouchEvent} {the Parent processing consumption – DOWN – > Parent. OnTouchEvent -true-> Returns the consumption status true

11. 2

12. 3
1. Parent control ACTION_MOVE interception

2. Child controls do not consume events

3. The parent control does not consume events
13. Receive press event – DOWN – > Parent. DispatchTouchEvent – DOWN – > Parent. OnInterceptTouchEvent – false – > Parent. DispatchTouchEvent -down -> child. dispatchTouchEvent{Parent dispatchEvent, Parent dispatchEvent, Child dispatchEvent} -down -> TargetChild } -false-> Child.dispatchTouchEvent -false-> Child.dispatchTouchEvent Parent. DispatchTouchEvent Child} {no target consumer – DOWN – > Parent. (super) dispatchTouchEvent} {the Parent processing consumption – DOWN – > Parent. OnTouchEvent -false-> Return the consumption status false

14. 5

15. 5
1. Parent control ACTION_UP interception

2. Child controls consume events
16. 7

17. Receive mobile events – a MOVE – > Parent. DispatchTouchEvent – MOVE – > Parent. OnInterceptTouchEvent – false – > Parent. DispatchTouchEvent -move -> Child(target consumer Child).dispatchTouchEvent-move -> child.onTouchEvent-true -> child.dispatchTouchEvent-true -> The Parent. DispatchTouchEvent – true – > return true consumption state

18. Receive release events – UP – > Parent. DispatchTouchEvent – UP – > Parent. OnInterceptTouchEvent – true – > Parent. DispatchTouchEvent -CANCEL-> Child(destination consumer Child). DispatchTouchEvent {Child processing consumer} -CANCEL-> child.onTouchEvent – Consumption status -> child.dispatchTouchEvent – true – > Parent. DispatchTouchEvent – true – > return true consumption state
1. Parent control ACTION_UP interception

2. Child controls do not consume events

3. The parent control consumes events
19. With 10

20. With 2

21. With 3
1. Parent control ACTION_UP interception

2. Child controls do not consume events

3. The parent control does not consume events
22. With 13

23. With 5

24. With 5
1. The parent control does not intercept

2. Child controls consume events
25. With 7

26. With 17

27. Receive release events – UP – > Parent. DispatchTouchEvent – UP – > Parent. OnInterceptTouchEvent – false – > Parent. DispatchTouchEvent – UP – > Child(target consumer Child).dispatchTouchEvent -UP-> child.onTouchEvent -true-> child.dispatchTouchEvent -true-> The Parent. DispatchTouchEvent – true – > return true consumption state
1. The parent control does not intercept

2. Child controls do not consume events

3. The parent control consumes events
28. With 10

29. With 2

30. 3
1. The parent control does not intercept

2. Child controls do not consume events

3. The parent control does not consume events
31. With 13

32. 5

33. With 5

revelation

ACTION_DOWN performs the event distribution search (traverses the sub-view, recursively distributes the search, if the sub-view does not consume, then goes back to its own consumption, backtracks up in turn, until it finds the target consumption View) to find the target consumption sub-view. Subsequent events no longer need to be found and are sent directly to the target consumer sub-view. If not, they are consumed by themselves.





argument

1. Consistency Guarantees: 1. If the ViewGroup returns false (do not consume the event) for ACTION_DOWN event distribution, subsequent events (ACTION_MOVE, ACTION_UP/ACTION_CANCEL) will not be received. 2. If the ViewGroup returns true for ACTION_DOWN event distribution, subsequent events (ACTION_MOVE, ACTION_UP/ACTION_CANCEL) will be received. If the ViewGroup intercepts subsequent events, The first interception passes the event as ACTION_CANCEL to the target consumer child View (the terminating child View receives subsequent events), and subsequent subsequent events consume themselves. 3. The consumption status returned by ViewGroup in non-ACTION_DOWN event distribution has no effect on the overall event flow. 2. It can be proved from the annotation that: View.dispatchTouchEvent method completes the event consumption processing; ViewGroup. DispatchTouchEvent method accomplishes the distributed event; ViewGroup. OnInterceptTouchEvent method accomplishes the intercept events; The ViewGroup on the event distribution path must call the onInterceptTouchEvent method when ACTION_DOWN or when the event is not directly consumed by itself. And the onTouchEvent method of the View class to handle event consumption.

A picture

appreciation

ACTION_DOWN triggers a search for the target consumption View, and the child View attempts to consume first. If the child View still does not consume, the parent control attempts to consume in sequence (up to the DecorView, then the Activity attempts to consume), and if found, the backtrace returns true. 2. The premise of the execution of ACTION_DOWN subsequent events is that the end of the event distribution path is the target consumption View, and the parent control of the target consumption View will call the event interception (so that the parent control has the opportunity to intercept and change the event flow). If the parent control of the target consumption View intercepts, The intercepted event will be converted to ACTION_CANCEL and continue to be distributed in the original path. Subsequent events will no longer be distributed to the target consumer View, but will be consumed by the parent control itself. 3. Non-action_down consumption status has no effect on the event flow. If it is not consumed, it is called back to the Activity.

standard

Common mistakes

When onInterceptTouchEvent and onTouchEvent are called, but if dispatchTouchEvent is called every time, write the logic directly into the dispatchTouchEvent override method. Problem: Event stream consistency is not met, and the target consumption View ends without receiving ACTION_UP/ACTION_CANCEL, resulting in the focus, keystroke status, or keystroke events not meeting expectations. OnInterceptTouchEvent is often called. The logic is provided in onInterceptTouchEvent. Problem: onInterceptTouchEvent is no longer called when the View itself consumes it or when the event stream is intercepted, which hides the trap deeper. 3. Shotgun method, dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent will call logic. Problem: Way too wild… 4. Feel cool, logic scattered in dispatchTouchEvent, onInterceptTouchEvent, onTouchEvent. Problem: poor readability, logic confusion. 5. Only ACTION_DOWN, ACTION_MOVE, and ACTION_UP are processed in event messages, but ACTION_CANCEL or other multi-touch events are not fault-tolerant. Problems: There are always unusual problems.

Best practices

1. Specify the sequence of event flow invocation and the intercepted event flow. 2. DispatchTouchEvent: Normally it is not recommended to rewrite the dispatchTouchEvent method to change the system event distribution mechanism. At best, remember the coordinate points, but never call super.DispatchTouchEvent to ensure that the system events are dispatched correctly. OnInterceptTouchEvent: Handles only the intercepting logic and streams events to onTouchEvent when appropriate. 4. OnTouchEvent: Really handle the logic. 5. In addition to common event processing, be sure to handle residual event fault-tolerant processing.

fishing

methodology

1. MECE law and Pyramid Principle 2. How to understand SCQA architecture?

tool

1. AS source English translation, Android emulator GenyMotion 2. GenyMotion creates the same API AS targetSdkVersion in App build.gradle Level simulator can Debug the corresponding source code. How to debug the Android Framework? 3. Android Studio Debugging Tips you didn’t know Key log output, using static proxy, advanced reference to the principle of Android plug-in analysis — Hook mechanism of dynamic proxy 4. Drawing tools 1. ProcessOn 2. Edraw 5. Personal homepage 1. Convert plain TeXt to static website and blog 2. TeXt theme template 3. How to guide a novice to Markdown?

the

2. Event distribution is no longer a matter, afraid of a ball 3. All kinds of cool dazzle animation and custom controls dry up 4. Don’t worry about the interview chatting event distribution 5. Learn from the above immature “fishing” to do whatever you like

The advanced

Android NestedScrolling allows you to scroll to NestedScrolling scrolling. 3. GestureDecetor

reference

2. Android responds to user gestures on the screen 3. Android MotionEvent Android Multi-touch — MotionEvent 6. 7. 图 文 Android event passing View section 7

A long song

Niannujiao · Tianding enraged Wanyan Liang (Jin Dynasty)

Tianding rage, overturned the silver sea, scattered beads foil (BO). Six strange flowers flying billow, flat fill the mountain gully. (six out: snow hexagonal, because used for the alias of snow.) Hao tiger madness, su Lin rampant (CH āng Jue), cast (CHE, pull) broken pearl cable. (Hao Tiger: white tiger. Su Lin: white unicorn. Yulong war, scales falling all over the sky.

Who read Wanli Guanshan, zheng Fu stiff, stripe (gǎo) with flag feet. Freeze: To stand stiff with cold. Chimera: white ribbon.) The color reflects the spear, the light shakes the sword and halberd, and the murderous spirit crosses the curtain. (Military tent: a tent used for marching and fighting.) PI (PI) Tiger Haoxiong, partial square (PI) heroic, and talk about military lue. (2). Have to spell a drunk, look at the blue sky vast.