本文来自网易云社区

作者:孙有军

上面的两次执行中每次都调用了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. 接触mybatis使用

    1.mybatis mybatis是一个自定义sql.存储过程和高级映射的持久层框架,是Apache下的顶级项目. mybatis可以让程序员将主要精力放在sql上,通过mybatis提供的映射方式. ...

  2. 相机拍摄时最重要的三个参数——光圈、快门、ISO

    注:这篇文章我四年前发布在其他地方,现在移过来. 如果你对相机只有很少了解,那么看这篇文章再好不过啦,我结合很多资料,力图用最通俗易懂的方式进行讲解. 相机拍摄时最重要的3个参数就是——光圈.快门.I ...

  3. css简单分页

    html代码 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...

  4. php调试利器之phpdbg

    信海龙的博客 php调试利器之phpdbg 简介 PHPDBG是一个PHP的SAPI模块,可以在不用修改代码和不影响性能的情况下控制PHP的运行环境. PHPDBG的目标是成为一个轻量级.强大.易用的 ...

  5. ftp sftp vsftp

    ftp  sftp (secure)  是文件传输 协议 vsftp(very secure) 是 ftp 服务端 sftp 是 ssh 的一部分

  6. fms +fme 视频直播

    1.安装fms 按默认安装即可 2.安装fme 安装完成后启动fme FMS URL是发布到fms的地址默认就可以, 然后点击 Connect 连接成功后左下角会出现connected的提示, 然后点 ...

  7. 2018.07.23 codeforces 438D. The Child and Sequence(线段树)

    传送门 线段树维护区间取模,单点修改,区间求和. 这题老套路了,对一个数来说,每次取模至少让它减少一半,这样每次单点修改对时间复杂度的贡献就是一个log" role="presen ...

  8. docker入门实战

    基本概念 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上. Docker是一个重新定义了程序开发测试.交付和 ...

  9. BZOJ 1011 [HNOI2008]遥远的行星 (误差分析)

    1011: [HNOI2008]遥远的行星 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 4974  Solved ...

  10. HDU 2136 Largest prime factor (素数打表。。。)

    题意:给你一个数,让你求它的最大因子在素数表的位置. 析:看起来挺简单的题,可是我却WA了一晚上,后来终于明白了,这个第一层循环不是到平方根, 这个题和判断素数不一样,只要明白了这一点,就很简单了. ...