源码 Activity 事件传递分发过程

1,Activity 对点击事件的传递过程
 点击事件用 MotionEvent 来表示,当点击事件发生时,事件由 WMS最先传递给当前
Activity,在由Activity 的 dispatchTouchEvent 来进行分发。

Activity # dispatchTouchEvent
调用处理触摸屏事件,Activity # dispatchTouchEvent 截取所有的触摸屏幕事件窗口.

/**
 * 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.
 *  调用处理触摸屏事件。你可以把它覆盖到在被发送之前,截取所有的触摸屏幕事件窗口
 * @param ev The touch screen event.
 *
 * @return boolean Return true if this event was consumed.
 *  如果这个事件被消耗,布尔返回true。
 */
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

Activity # onTouchEvent
当触摸屏幕事件没有被任何其子视图处理时,由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.
 *  当触摸屏幕事件没有被任何其子视图处理时,由自己处理。
 * @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.
 * 如果你已经消费了这个事件,那就返回true,
    默认的实现总是返回false。
 */
public boolean onTouchEvent(MotionEvent event) {
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }

    return false;
}

Window # superDispatchTouchEvent
Window 传递触摸屏事件向下层视图传递。 由 PhoneWindow # superDispatchTouchEvent 实现方法。

/**
 * 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.
 *  使用自定义窗口,例如对话框,来传递触摸屏事件进一步向下视图层次结构
 */
public abstract boolean superDispatchTouchEvent(MotionEvent event);

PhoneWindow # superDispatchTouchEvent
PhoneWindow 将事件交给了 DecorView

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}
// This is the top-level view of the window, containing the window decor.
   这是窗口的顶层视图,包含了窗口的装饰。
private DecorView mDecor;
@Override
public final View getDecorView() {
    if (mDecor == null) {
        installDecor();
    }
    return mDecor;
}

PhoneWindow 内部类 DecorView
由于 DevorView 继承 FrameLayout,所以事件已经传递到 顶级 View,
即在 Activity 中通过 setContentView 所设置的 view。

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

2,顶级 View (ViewGroup)对点击事件的分发过程

ViewGroup # dispatchTouchEvent
如果顶级 ViewGroup 拦截了事件 即 onInterceptTouchEvent 返回 true,事件由 ViewGroup 处理,
并且后续事件默认都由它处理,不再会调用 onInterceptTouchEvent 方法。
如果我们想提前处理所有点击事件,要选择 dispatchTouchEvent 方法中 处理。

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
如果顶级 ViewGroup 不拦截事件,事件向下分发由它的子 view 处理。
子 View 是否接受点击事件:
1、子元素 是否在播放动画
2、点击事件坐标是否落在子元素的区域内

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
调用子元素的 dispatchTouchEvent,如果返回 true,在子元素内部分发。

/**
 * 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
child == null 时,这里点击事件交由 View 处理。

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

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

3、 View 对点击事件的处理过程
注意:mOnTouchListener.onTouch() 返回 true,onTouchEvent 就不会处理,可见 OnTouchListener 优先级高。

/**
 * Pass the touch screen motion event down to the target view, or this
 * view if it is the target.
 * 将触摸屏幕运动事件传递到目标视图
 * @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 依然会消耗点击事件。
2、clickable , long_clickable 为 true时,调用 preformClick。

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 依然会消耗点击事件。**
        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
调用 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 笔记:https://github.com/ycyangchun/androidNode

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值