本文来自网易云社区

作者:孙有军

上面的两次执行中每次都调用了onInterceptTouchEvent事件,这个到底又是啥?我们去看看他的返回值是什么?

public boolean onInterceptTouchEvent(MotionEvent ev) 
{    
return false;
}

可以看到默认返回false,注释长的吓人,那我们就来改写一下他的返回值,这个函数是ViewGroup才有的,说明与布局容器有关.
       round 3

我们将Layout的onInterceptTouchEvent的返回值改为true。重新执行。执行顺序如下:

05-05 14:59:17.829 15157-15157/com.sunny.event E/Event: Layout    dispatchTouchEvent ACTION_DOWN05-05 14:59:17.830 15157-15157/com.sunny.event E/Event: Layout    onInterceptTouchEvent ACTION_DOWN05-05 14:59:17.830 15157-15157/com.sunny.event E/Event: Layout    onTouchEvent ACTION_DOWN

从日志可以发现,只有最外层的控件能够执行事件,TextView已经收不到任何事件。

小结

父控件onInterceptTouchEvent返回true会拦截子控件的事件

追根溯源

我们从代码的层面来看看他是怎么执行的,当屏幕接收到点击事件时会首先传递到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. 第一告诉用户ACTION_DOWN,用户可以复写onUserInteraction来处理点击开始

  2. 调用了getWindow().superDispatchTouchEvent(ev),这里的getWindow得到是PhoneWindow对象,因此执行的PhoneWindow的superDispatchTouchEvent函数,

  3. 调用了Activity的onTouchEvent事件

PhoneWindow的superDispatchTouchEvent又调用了DecorView的superDispatchTouchEvent函数,每一个Activity都有一个PhoneWindow,每一个PhoneWindow都有一个DecorView,DecoView继承自FrameLayout,这里又调用了super.dispatchTouchEvent(event),FrameLayout里面是没有改函数的,所以最终执行的是ViewGroup的dispatchTouchEvent函数。

这里我们先穿插一点界面的知识,以我测试手机为例,DecorView中有两个child,分别是ViewStub和LinerLayout,LinerLayout中又包含了FrameLayout,FrameLayout中包含了一个ActionBarOverlayLayout,ActionBarOverlayLayout里又包含了两个child,分别是ActionBarContainer与ContentFrameLayout。

接下来我们去看看ViewGroup的dispatchTouchEvent函数:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) { 
    .........
    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) {
            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;
        }
        ............
        // Update list of touch targets for pointer down, if needed.
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        if (!canceled && !intercepted) {             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) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    // Find a child that can receive the event.
                    // Scan children from front to back.
                    final ArrayList<View> preorderedList = buildOrderedChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = customOrder
                                ? getChildDrawingOrder(childrenCount, i) : i;
                        final View child = (preorderedList == null)
                                ? children[childIndex] : preorderedList.get(childIndex);
                        .......                         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 pointer in addition to the ones it is handling.
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }                         resetCancelNextUpFlag(child);
                        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 index
                                for (int j = 0; j < childrenCount; j++) {
                                    if (children[childIndex] == mChildren[j]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                    }
                    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.
        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 (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    if (cancelChild) {
                        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 handled;
}

网易云免费体验馆,0成本体验20+款云产品!

更多网易研发、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 知物由学 | 如何应对日益强大的零日攻击
【推荐】 云课堂直播频道的策划

Android事件分发机制浅析(2)的更多相关文章

  1. Android事件分发机制浅析(1)

    本文来自网易云社区 作者:孙有军 事件机制是Android中一个比较复杂且重要的知识点,比如你想自定义拦截事件,或者某系组件中嵌套了其他布局,往往会出现这样那样的事件冲突,坑爹啊!!事件主要涵盖onT ...

  2. Android事件分发机制浅析(3)

    本文来自网易云社区 作者:孙有军 我们只看最重要的部分 1: 事件为ACTION_DOWN时,执行了cancelAndClearTouchTargets函数,该函数主要清除上一次点击传递的路径,之后执 ...

  3. Android进阶——Android事件分发机制之dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

    Android事件分发机制可以说是我们Android工程师面试题中的必考题,弄懂它的原理是我们避不开的任务,所以长痛不如短痛,花点时间干掉他,废话不多说,开车啦 Android事件分发机制的发生在Vi ...

  4. Android事件分发机制(下)

    这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...

  5. Android事件分发机制(上)

    Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...

  6. android事件分发机制

    android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...

  7. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  8. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  9. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

随机推荐

  1. 6-Qt给父widget加上styleSheet(添加背景图)而不改变子widget的styleSheet的方法

    Qt给父widget加上styleSheet(添加背景图)而不改变子widget的styleSheet的方法 比如用stylesheet给widget加背景图,可以用qt designer修改ui文件 ...

  2. 论坛遇到附件上传失败问题总结(discuz)

    (1)bbs/source/class/class_upload.php 50行左右,注释$attach['target'] $attach['target'] = DISCUZ_ROOT.'./da ...

  3. RSA生成、加密、解密、签名。

    首先,要会生成RSA密码对. https://app.alipay.com/market/document.htm?name=saomazhifu#page-23    (事例中的密钥对好像有问题,最 ...

  4. group by 和 distinct 的区别

    SELECT fs.card_id, fs. NAME, fs.email, fs.phone_num, fs.weixin_num, fs.permission, fs.open_id FROM f ...

  5. 2018秋季C语言基础课第1次作业

    1.翻阅邹欣老师博客关于师生关系博客,并回答下列问题: 1)大学和高中最大的不同是没有人天天看着你,请看大学理想的师生关系是?有何感想? 答:是  Coach / Trainee (健身教练 / 健身 ...

  6. 为了记忆和方便翻阅 vue构建后的结构目录说明

    一. ├── build              // 项目构建(webpack)相关代码             记忆:(够贱)    9个 │ ├── build.js       // 生产环 ...

  7. NET(C#)连接各类数据库-集锦

    1.C#连接连接Access程序代码:------------------------------------------------------------------------------- u ...

  8. 23. Man and His Natural Habitat 人类及其自然栖息地

    . Man and His Natural Habitat 人类及其自然栖息地 ① Ecology is that branch of science which concerns itself wi ...

  9. 云服务器vps

    0.云计算时代,是一个很时髦的词,人们常常谈起,挂在嘴边.其实云计算通俗点就是电脑托管到了远端的机房,然后不用去买配件主机,是摸不到的,但通过网络远程连接,就可以使用云服务器的资源和功能(搭建网站,测 ...

  10. Quartus调用MOdelsim仿真过程

    quarutsii调用modelsim实际是相对比较简单的.因为不需要选择要编译的库. 调用前的设置:设置调用的工具,也就是下面的Tool name,选择仿真语言,在这里选择为Verilog HDL, ...