博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ViewGroup事件分发机制
阅读量:6714 次
发布时间:2019-06-25

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

1、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) {                //做一些重置工作                //将mFirstTouchTarget置空                cancelAndClearTouchTargets(ev);                //将FLAG_DISALLOW_INTERCEPT等状态标识还原                resetTouchState();            }            //是否要做事件拦截,如果为false,不做拦截继续分发,为true则阻止分发            final boolean intercepted;            (1)//按下并且mFirstTouchTarget不为空            if (actionMasked == MotionEvent.ACTION_DOWN                    || mFirstTouchTarget != null) {                (2)//FLAG_DISALLOW_INTERCEPT标志位                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;                               if (!disallowIntercept) {	                (3)                    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;            }            if (intercepted || mFirstTouchTarget != null) {                ev.setTargetAccessibilityFocus(false);            }            // Check for cancelation.            final boolean canceled = resetCancelNextUpFlag(this)                    || actionMasked == MotionEvent.ACTION_CANCEL;            // 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;            //主要做对子View事件分发的工作            if (!canceled && !intercepted) {                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()                        ? findChildWithAccessibilityFocus() : null;*/                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 = buildTouchDispatchChildList(); final boolean customOrder = preorderedList == null && isChildrenDrawingOrderEnabled(); final View[] children = mChildren; //对子View的集合进行遍历 for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = getAndVerifyPreorderedIndex( childrenCount, i, customOrder); final View child = getAndVerifyPreorderedView( preorderedList, children, childIndex); // If there is a view that has accessibility focus we want it // to get the event first and if not handled we will perform a // normal dispatch. We may do a double iteration but this is // safer given the timeframe. if (childWithAccessibilityFocus != null) { if (childWithAccessibilityFocus != child) { continue; } childWithAccessibilityFocus = null; i = childrenCount - 1; } if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { ev.setTargetAccessibilityFocus(false); continue; } //查找到当前可以接受事件的子View,整个循环结束 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); (4)//用来将事件传递给子View 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(); //实现了mFirstTouchTarget的赋值 newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } // The accessibility focus didn't handle the event, so clear // the flag and do a normal dispatch to all children. ev.setTargetAccessibilityFocus(false); } if (preorderedList != null) preorderedList.clear(); } ...... return handled; }复制代码

(1)mFirstTouchTarget: 如果事件由子元素处理,mFirstTouchTarget会被赋值并指向子元素,此时mFirstTouchTarget != null。如果mFirstTouchTarget == null,会导致ViewGroup的onInterceptTouchEvent方法不会再被调用,并且同一系列其他的事件默认交给ViewGroup来处理。

(2)FLAG_DISALLOW_INTERCEPT标识位: FLAG_DISALLOW_INTERCEPT标识位可以来干预ViewGroup的分发,可以在子元素中通过调用reqeustDisallowInterceptTouchEvent方法来干预父元素的事件分发过程,但是ACTION_DOWN除外。 因为ACTION_DOWN事件会重置FLAG_DISALLOW_INTERCEPT这个标志位,导致子View设置的这个标志位无效。 (3)intercepted : 这个标识量为true,则不会再调用子View的dispatchTouchEvent方法。intercepted 通过onInterceptTouchEvent来返回值进行赋值。也就是说onInterceptTouchEvent返回true,则不会进行子View的事件分发。

/**     * 子view可以通过该方法控制父View的拦截     * @param disallowIntercept     */    @Override    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {            // We're already in this state, assume our ancestors are too            return;        }        if (disallowIntercept) {            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;        } else {            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;        }        // Pass it up to our parent        if (mParent != null) {            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);        }    }复制代码

(4) dispatchTransformedTouchEvent 遍历ViewGroup所有子元素,判断子元素能否接受点击事件(通过点击区域和是否播放动画来判断)。如果可以接受点击事件,则调用dispatchTransformedTouchEvent方法,内部通过child.dispatchTouchEvent(event)方法实现到子View的事件传递。

/**     * 对子View的事件分发     */    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,                                                  View child, int desiredPointerIdBits) {        final boolean handled;        // Canceling motions is a special case.  We don't need to perform any transformations        // or filtering.  The important part is the action, not the contents.        final int oldAction = event.getAction();        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {            event.setAction(MotionEvent.ACTION_CANCEL);            if (child == null) {                //子view为空则调用View的dispatchTouchEvent方法                handled = super.dispatchTouchEvent(event);            } else {                //调用child的dispatchTouchEvent方法                handled = child.dispatchTouchEvent(event);            }            event.setAction(oldAction);            return handled;        }      ......    }复制代码

2、onInterceptTouchEvent:

默认返回false

public boolean onInterceptTouchEvent(MotionEvent ev) {        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)                && ev.getAction() == MotionEvent.ACTION_DOWN                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)                && isOnScrollbarThumb(ev.getX(), ev.getY())) {            return true;        }        return false;    }复制代码

3、onTouchEvent:

调用了View的onTouchEvent方法

4、三个方法之间的关系:

public boolean dispatchTouchEvent(MotionEvent ev) {      // 默认状态为没有消费过      boolean result = false;      // 如果没有拦截交给子View进行分发      if (!onInterceptTouchEvent(ev)) {            result = child.dispatchTouchEvent(ev);       }      // 如果事件没有被消费,询问自身onTouchEvent      if (!result) {            result = onTouchEvent(ev);       }       return result;  }  复制代码
  • 如果ViewGroup不拦截,交于子View的onTouchEvent处理。
  • 如果子View的onTouchEvent返回false,就会调用该ViewGroup的onTouchEvent方法,如果ViewGroup的onTouchEvent也返回false,最终会抛回Activity的onTouchEvent方法,一层层传递回去。

转载地址:http://ctelo.baihongyu.com/

你可能感兴趣的文章
理解Java常量池
查看>>
JVM调优总结-调优方法
查看>>
微信小程序 watch监听数据变化 类似vue中的watch
查看>>
u检验、t检验、F检验、X2检验 (转)
查看>>
不可不知的Python模块: collections
查看>>
PAT 1066. Root of AVL Tree (25)
查看>>
细说多线程之Thread与Runnable
查看>>
【Codeforces #134 Div2】Solutions 【Updated】
查看>>
数据库优化案例——————某知名零售企业ERP系统
查看>>
计算月份差方法封装
查看>>
setsockopt 设置socket 详细用法
查看>>
抽象工厂不同接口反射
查看>>
hdu1052
查看>>
服务器端推送技术
查看>>
python开发工具
查看>>
Home Assistant系列 -- 自动语音播报天气
查看>>
Hyberledger-Fabric 1.00 RPC学习(1)
查看>>
SDNU 1450.报时助手
查看>>
BZOJ 4144 Dijkstra+Kruskal+倍增LCA
查看>>
阻塞与非阻塞,同步与异步
查看>>