Source Activity Event Delivery and Distribution Process

Keywords: Windows github

1. The Passing Process of Activity to Click Events
_Click events are represented by Motion Event. When a click event occurs, the event is first passed to the current user by WMS.
Activity, which is distributed by Activity's dispatch TouchEvent.

Activity # dispatchTouchEvent
Call to handle touch screen events, Activity dispatchTouchEvent intercepts all touch screen event windows.

/**
 * Called to process touch screen events.  You can override this to
 * intercept all touch screen events before they are dispatched to the
 * window.  Be sure to call this implementation for touch screen events
 * that should be handled normally.
 *  Call to handle touch screen events. You can overwrite it and intercept all touch screen event windows before it is sent
 * @param ev The touch screen event.
 *
 * @return boolean Return true if this event was consumed.
 *  If this event is consumed, Boolean returns true.
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

Activity # onTouchEvent
When touchscreen events are not handled by any of their child views, they are handled by Activity onTouchEvent.

/**
 * Called when a touch screen event was not handled by any of the views
 * under it.  This is most useful to process touch events that happen
 * outside of your window bounds, where there is no view to receive it.
 *  When touchscreen events are not handled by any of its sub-views, they are handled by themselves.
 * @param event The touch screen event being processed.
 *
 * @return Return true if you have consumed the event, false if you haven't.
 * The default implementation always returns false.
 * If you've consumed the event, go back.true,
    The default implementation always returnsfalse. 
 */
public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }

    return false;
}

Window # superDispatchTouchEvent
Windows Passes Touch Screen Events to the Lower View. The method is implemented by PhoneWindow superDispatch TouchEvent.

/**
 * Used by custom windows, such as Dialog, to pass the touch screen event
 * further down the view hierarchy. Application developers should
 * not need to implement or call this.
 *  Use custom windows, such as dialog boxes, to pass touch screen events further down the view hierarchy
 */
public abstract boolean superDispatchTouchEvent(MotionEvent event);

PhoneWindow # superDispatchTouchEvent
Phone Windows handed over the event to DecorView

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
// This is the top-level view of the window, containing the window decor.
   //This is the top view of the window, which contains the decoration of the window.
private DecorView mDecor;
@Override
public final View getDecorView() {
    if (mDecor == null) {
        installDecor();
    }
    return mDecor;
}

Phone Windows Internal Class DecorView
Since DevorView inherits FrameLayout, events have been passed to the top-level View.
That is, the view set through setContentView in Activity.

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {public boolean superDispatchTouchEvent(MotionEvent event) {
      return super.dispatchTouchEvent(event);
 }
}

2. Distribution of Click Events by Top View Group

ViewGroup # dispatchTouchEvent
If the top-level ViewGroup intercepts the event, onInterceptTouchEvent returns true, and the event is handled by ViewGroup.
And subsequent events are handled by default, and the onInterceptTouchEvent method is no longer called.
If we want to process all click events in advance, we need to select the dispatchTouchEvent method to process them.

public boolean dispatchTouchEvent(MotionEvent ev) {
// 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 for the previous gesture
    // due to an app switch, ANR, or some other state change.
    cancelAndClearTouchTargets(ev);
    resetTouchState();
}
        // Check for interception.
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                intercepted = **onInterceptTouchEvent**(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
            // There are no touch targets and this action is not an initial down
            // so this view group continues to intercept touches.
            intercepted = true;
        }
.....
}

ViewGroup # dispatchTouchEvent
If the top-level ViewGroup does not intercept events, the downward distribution of events is handled by its sub-view s.
Does Sub View Accept Click Events:
1. Whether the child element is playing animation
2. Click on the event coordinate to see if it falls within the sub-element area

final boolean customOrder = preorderedList == null
        && isChildrenDrawingOrderEnabled();

final View[] children = mChildren;
    for (int i = childrenCount - 1; i >= 0; i--) {


        if (!canViewReceivePointerEvents(child)
                || !isTransformedTouchPointInView(x, y, child, null)) {
            ev.setTargetAccessibilityFocus(false);
            continue;
        }
        resetCancelNextUpFlag(child);
        if (**dispatchTransformedTouchEvent**(ev, false, child, idBitsToAssign)) {
            // Child wants to receive touch within its bounds.
            mLastTouchDownTime = ev.getDownTime();

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

        // The accessibility focus didn't handle the event, so clear
        // the flag and do a normal dispatch to all children.
        ev.setTargetAccessibilityFocus(false);
    }

}

ViewGroup # dispatchTransformedTouchEvent
Call dispatchTouchEvent of the child element, and if true is returned, distribute within the child element.

/**
 * Transforms a motion event into the coordinate space of a particular child view,
 * filters out irrelevant pointer ids, and overrides its action if necessary.
 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
 */
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;

    // Canceling motions is a special case.  We don't 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) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.**dispatchTouchEvent**(event);
        }
        event.setAction(oldAction);
        return handled;
    }
.....

}

ViewGroup # dispatchTransformedTouchEvent
When child = null, the click event is handled by View.

// Perform any necessary transformations and dispatch.
if (child == null) {
    handled = super.dispatchTouchEvent(transformedEvent);
} else {

    handled = child.**dispatchTouchEvent**(transformedEvent);
}

3. View's Processing of Click Events
Note: When mOnTouchListener.onTouch() returns true, onTouchEvent will not process it, so OnTouchListener has a high priority.

/**
 * Pass the touch screen motion event down to the target view, or this
 * view if it is the target.
 * Pass the touchscreen motion event to the target view
 * @param event The motion event to be dispatched.
 * @return True if the event was handled by the view, false otherwise.
 */
public boolean dispatchTouchEvent(MotionEvent event) {

    boolean result = false;

    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        //noinspection SimplifiableIfStatement
        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;
}

View # onTouchEvent
1. view in unavailable state will still consume click events.
2. When clickable, long_clickable is true, preformClick is called.

public boolean onTouchEvent(MotionEvent event) {

    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        // A disabled view that is clickable still consumes the touch
        // events, it just doesn't respond to them.
        // view in unavailable state still consumes click events. * *
        return (((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
    }
    if (mTouchDelegate != null) {
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }

    if (((viewFlags & CLICKABLE) == CLICKABLE ||
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    boolean focusTaken = false;
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus();
                    }

                    if (prepressed) {        
                        setPressed(true, x, y);
                   }
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // This is a tap, so remove the longpress check
                        removeLongPressCallback();
                        // Only perform take click actions if we were in the pressed state
                        if (!focusTaken) {  
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                **performClick**();
                            }
                        }
                    }
}

View # performClick
Call onClick

public boolean performClick() {
    final boolean result;
    final ListenerInfo li = mListenerInfo;
    if (li != null && li.mOnClickListener != null) {
        playSoundEffect(SoundEffectConstants.CLICK);
        li.mOnClickListener.**onClick**(this);
        result = true;
    } else {
        result = false;
    }

    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
    return result;
}

myBase Notes: https://github.com/ycyangchun/androidNode

Posted by troinfo on Sat, 25 May 2019 15:14:10 -0700