【Android源码学习】事件分发机制

关于Android事件分发机制,一直半清不楚。Android事件传递机制——这篇博客实验做的很好,不过对于dispatchTouchEvent的返回值解释错误。直到看到这篇博文,才有了比较清晰的认识Android事件分发流程——这篇博客图文较多便于理解但是没有涉及原理。这篇文章对原理讲解的比较清楚以伪代码的形式展示事件分发结果,配合第二篇博客效果非常好
总结:
1、Activity既不是View也不是ViewGroup,但是它有dispatchTouchEvent,因为Activity实现了Window.Callback并在Activity的attach方法中设置了Activity作为PhoneWindow的回调,也就是PhoneWindow内部类DecorView的实际回调。
而触摸事件实际上是由WindowManagerService捕捉并包装成InputChannel传递到ViewRootImpl的WindowInputEventReceiver,继而提交给了DecorView(ViewRootImpl中的mView就是DecorView)。
这里就要提到PhoneWindow、DecorView和ViewRootImpl的关系。Activity的setContentView方法实际上调用的就是PhoneWindow的setContentView,而这个方法会调用installDecor生成DecorView,这样PhoneWindow就和DecorView互相关联起来了。而ViewRootImpl却是在ActivityThread的performResumeActivity中调用Activity的makeVisible中创建并关联的。
Android触摸事件来源
这样Activity实现dispatchTouchEvent和onTouchEvent也就情有可原了。这里为什么不直接从DecorView往子ViewGroup和子View传递的原因就不清楚了,上文说的给DecorView最后一次处理TouchEvent的机会领悟不了。
2、dispatchTouchEvent表示分发事件。它在Activity、View和ViewGroup中的表现都不一样。

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

可以看出在Activity中,如果重写了此方法且没有调用super,即没用调用默认逻辑。那么触摸事件根本不会继续传递,也不会触发onTouchEvent。
如果没有重写,则会调用DecorView的superDispatchTouchEvent其实就是ViewGroup的dispatchTouchEvent继续分发。
ViewGroup的dispatchTouchEvent源代码很长,这里截取关键部分。

            // 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会先调用onInterceptTouchEvent判断是否拦截事件。如果没有拦截,从后往前遍历每个子View调用dispatchTransformedTouchEvent分发事件。如果拦截了或者子View的dispatchTouchEvent返回false,则调用自身的onTouchEvent。

再看View的dispatchTouchEvent。

            //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;
            }

可以看出,如果View设置了有效onTouchListener,并且View没有被禁用,那么监听器的onTouch方法就会被触发。如果onTouch方法返回true,那么onTouchEvent就不会执行,View的onClick就不会触发了。也就是说OnTouchListener和OnClickListener可以同时工作,但如果OnTouchListener的onTouch返回true,那么OnClickListener就不能工作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值