【事件分发机制】关于事件分发机制的一些记录

事件分发笔记


为什么写这篇

因为学事件分发看得最多的就是 u 型图,不知道有没有人跟我一样一直很疑惑为什么子View不消费就会让父View消费呢,为什么一旦接受了down事件,后续事件都会由它来处理呢,为什么拦截了之后后续事件都会发送给这个View呢…所以才想着记录下来,单纯记结论我感觉太难了(很容易搞乱…)


核心方法是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;

            //重置mFirstTouchTarget
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            //【分析点1】
            final boolean intercepted;
            //判断是否调用onInterceptTouchEvent是根据两个条件
            //1、事件是否为DOWN
            //2、mFirstTouchTarget是否被赋值了。
            //mFirstTouchTarget是一个很重要的变量,它代表着
            //是否有子View消费了事件。
            //这个变量的赋值在addTouchTarget中,
            //而addTouchTarget是在遍历子View的for循环中调用的
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action);
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }

			...

            // Check for cancelation.
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

			//【分析点2】
			//这是一块很长的代码块,只能在这个代码块里面才执行遍历子View
            if (!canceled && !intercepted) {
				
				//【分析点3】
				//ACTION_DOWN、ACTION_POINTER_DOWN、ACTION_HOVER_MOVE才会进入遍历方法
				//mFirstTouchTarget才有可能被赋值
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                    final int childrenCount = mChildrenCount;
                    //子view存在,开始遍历
                    if (newTouchTarget == null && childrenCount != 0) {
                    	...
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                        	...
                        	
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

							...
							//【分析点4】
                            //这个方法会调用child的dispatchTouchEvent
                            //如果返回true,即代表子View消费了事件(因为这个方法是带返回值的),给
                            //mFirstTouchTarget赋值
                            //否则跳过
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) 
                                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();
                                //【分析点5】
                                //给mFirstTouchTarget赋值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                //跳出for循环
                                break;
                            }
                        }
                    }
					...
                }
            }

            //【分析点6】
            //如果(!canceled && !intercepted)不成立,又或者满足,但是事件不是
            //ACTION_DOWN、ACTION_POINTER_DOWN、ACTION_HOVER_MOVE其中之一,就进入这里
            if (mFirstTouchTarget == null) {
            	//为空代表没有子View消费事件,注意dispatchTransformedTouchEvent的第三个参数null
            	//它会导致调用super.dispatchTouchEvent(event);
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
         
                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;
                        //【分析点7】
                		//mFirstTouchTarget != null的情况,也就是消费了DOWN事件
                		//可以理解为这里是后续的MOVE、UP会调用的地方  
                        //调用分发,注意这个pointerIdBits,dispatchTransformedTouchEvent根据这个
                        //来判断是新的事件还是旧的事件,从而实现让消费了DOWN的事件也能继续消费MOVE和
                        //UP
                        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;
                }
            }

        }

        return handled;
    }



1、为什么子View的onTouchEvent返回false(不消费),就会调用父View的onTouchEvent

可以看到分析点5、6,因为mFirstTouchTarget只有在for遍历里面通过addTouchTarget赋值,因为子View不消费,所以dispatchTransformedTouchEvent返回false,代码就会继续执行到分析点6,而且传入的第三个参数是为null,从而调用到super.dispatchTouchEvent(event);,又因为ViewGroup的父类是ViewdispatchTouchEvent最终会调用到onTouchEvent,所以就表现为自己消费



2、为什么一旦接受了down事件,后续事件都会由它来处理

根据分析点7mFirstTouchTarget已经被赋值的情况下,进入dispatchTransformedTouchEvent通过对比pointerIdBits来判断是否还是原来消费DOWN的那个目标View,是的话继续分发给它



3、为什么拦截了之后后续事件都会发送给这个View呢

同1,最主要的原因还是mFirstTouchTarget这个变量,因为interceptedtrue,就不会进入for循环的代码块,导致mFirstTouchTargetnull,进而促使它调用自己的dispatchTouchEvent



在这里插入图片描述
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值