博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android事件分发机制浅析(2)
阅读量:5170 次
发布时间:2019-06-13

本文共 9886 字,大约阅读时间需要 32 分钟。

本文来自网易云社区

作者:孙有军

上面的两次执行中每次都调用了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函数:

@Overridepublic 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
 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+款云产品! 

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

相关文章:

【推荐】 

转载于:https://www.cnblogs.com/zyfd/p/9705704.html

你可能感兴趣的文章
编译原理 First,Follow,select集求法
查看>>
iOS开发 runtime实现原理以及实际开发中的应用
查看>>
android 学习资源网址
查看>>
qt安装遇到的错误
查看>>
java:Apache Shiro 权限管理
查看>>
objective c的注释规范
查看>>
FreeNas安装配置使用
查看>>
Django(一)框架简介
查看>>
Python操作SQLite数据库的方法详解
查看>>
菜单和工具条(二)
查看>>
hadoop17---RPC和Socket的区别
查看>>
使用JMeter代理录制app测试脚本
查看>>
Linq to Object实现分页获取数据
查看>>
mac常用系统命令
查看>>
android上传文件到服务器
查看>>
我回答了90%的面试题,为什么还被拒?
查看>>
Html - Table 表头固定和 tbody 设置 height 在IE不起作用的解决
查看>>
HDU 2262 回溯算法 递归枚举
查看>>
九度0J 1374 所有员工年龄排序
查看>>
微信小程序图片使用示例
查看>>