Android 事件分发

Android 源码事件分发
  1. 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.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }
  1. Window的唯一实现类PhoneWindow#superDispatchTouchEvent
   @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
  1. DecorView#superDispatchTouchEvent方法,(DecorView是当前界面的底层容器,就是setContentView所设置View的父容器。)
public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }
  1. 看源码继承关系可知DecorView继承了FramLayout,所有会走到ViewGroup#dispatchTouchEvent方法中

伪代码如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
      boolean handled = false;
        if (onInterceptTouchEvent(ev)) {
            handled = onTouchEvent(ev);
        } else {
            handled = child.dispatchTouchEvent(ev)
        }
        return handled;
}  
  1. View对事件的分发,View的dispatchTouchEvent()方法。
public boolean dispatchTouchEvent(MotionEvent event) {
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        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会先判断自己是否有设置OnTouchListener,如果所设置的OnTouchListener得onTouch返回true,则直接消耗点击事件,不再执行onTouchEvent()方法。

  • 结论:OnTouchListener的优先级高于onTouchEvent()。这样做的好处是方便在外部处理事件。
  1. 如果没有设置OnTouchListener那就会执行到View的onTouchEvent(),继续看下
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) {
                    removeLongPressCallback();
                    if (!focusTaken) {
                        if (mPerformClick == null) {
                            mPerformClick = new PerformClick();
                        }
                        if (!post(mPerformClick)) {
                            performClick();
                        }
                    }
                }

                if (mUnsetPressedState == null) {
                    mUnsetPressedState = new UnsetPressedState();
                }

                if (prepressed) {
                    postDelayed(mUnsetPressedState,
                            ViewConfiguration.getPressedStateDuration());
                } else if (!post(mUnsetPressedState)) {
                    mUnsetPressedState.run();
                }

                removeTapCallback();
            }
            mIgnoreNextUpEvent = false;
            break;

        case MotionEvent.ACTION_DOWN:
            ......
            break;

        case MotionEvent.ACTION_CANCEL:
            ......
            break;

        case MotionEvent.ACTION_MOVE:
            ......
            break;
    }

    return true;
}

当View的CLICKABLE、LONG_CLICKABLE和CONTEXT_CLICKABLE有其中一个为true那么View就会消耗掉这个事件。并且在ACTION_UP的时候会执行performClick()方法:

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;
}
结论
  • 当一个点击事件产生后,它的传递过程遵循如下顺序:Activity -> Window ->View,即事件总是先传递给Activity, Activity再传递给Window,最后Window再传递给顶级View。顶级View接收到事件后,就会按照事件分发机制去分发事件。考虑一种情况,如果一个View的onTouchEvent返回false,那么它的父容器的onTouchEvent将会被调用,依此类推。如果所有的元素都不处理这个事件,那么这个事件将会最终传递给Activity处理,即Activity的onTouchEvent方法会被调用。
  • 一旦一个元素拦截了某此事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。
  • onInterceptTouchEvent()方法不一定会每次都执行,如果想对每个事件都进行处理,那还是在dispatchTouchEvent()里面处理吧。
  • OnTouchListener的优先级高于onTouchEvent()。这样做的好处是方便在外部处理事件。
  • 当我们把View设置为不可用状态,View依然会消耗点击事件,只是看起来不可用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌晨三点的北京

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值