Android事件分发机制深度分析(三)

本文详细探讨了ViewGroup的dispatchTouchEvent方法及其在事件分发中的作用,包括onInterceptTouchEvent、ACTION_DOWN事件处理、子View的事件处理、FLAG_DISALLOW_INTERCEPT标志的影响,以及事件分发流程图的总结。
摘要由CSDN通过智能技术生成

ViewGroup事件分发的源码解析

虽然ViewGroup是继承自View,但ViewGroup和View的事件分发的处理还是不一样的,所以这里分开来讲。
当点击事件到达ViewGroup时,会调用ViewGroup的dispatchTouchEvent方法。而dispatchTouchEvent方法中又会调用onInterceptTouchEvent方法,这时会出现下面两种情况:

如果该ViewGroup拦截该事件,则onInterceptTouchEvent方法返回true,该事件将由该ViewGroup处理,如果ViewGroup的mOnTouchListener被设置,则mOnTouchListener回调中的onTouch方法会被调用,否则onTouchEvent会被调用。也就是说,如果都提供的话,mOnTouchListener会屏蔽掉onTouchEvent。在onTouchEvent中,如果设置了mOnClickListener,则onClick会被调用。

如果该ViewGroup不拦截事件,则事件会传递给它所在的点击事件链上的子View,这时子View的dispatchTouchEvent会被调用。到此为止,事件已经从顶级View传递给了下一层View, 接下来的传递过程和顶级View是一致的,如此循环,完成整个事件的分发。

可以看出ViewGroup的事件分发最先从dispatchTouchEvent()方法开始,同时这个方法也是ViewGroup事件分发过程中最主要的方法,方法比较长,我们分段进行解读。dispatchTouchEvent()方法开头便调用了onFilterTouchEventForSecurity()方法,这个方法主要是对触摸事件进行过滤,用于组件安全方面的考虑,比如View在一些特定的条件下需要屏蔽器触摸事件。接下来会监测点击事件如果是ACTION_DOWN时,则会清除所有以前的状态。

        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) {
   
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
            ...
        }

resetTouchState()方法中继续调用clearTouchTargets方法。其中我们主要关注对mGroupFlags中FLAG_DISALLOW_INTERCEPT标志的重置,以及设置mFirstTouchTarget为null,在后面分析时会用到这两个对象。

    private void resetTouchState() {
   
        clearTouchTargets();
        resetCancelNextUpFlag(this);
        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
        mNestedScrollAxes = SCROLL_AXIS_NONE;
    }
    private void clearTouchTargets() {
   
        TouchTarget target = mFirstTouchTarget;
        if (target != null) {
   
            do {
   
                TouchTarget next = target.next;
                target.recycle();
                target = next;
            } while (target != null);
            mFirstTouchTarget = null;
        }
    }

继续向下,如果当前的点击事件是ACTION_DOWN或者mFirstTouchTarget不为null时执行if代码块,点击事件是ACTION_DOWN这个条件好理解,那么mFirstTouchTarget什么时候不为null呢?这个从后面的代码逻辑可以看出来,当事件由ViewGroup的子元素成功处理时, mFirstTouchTarget会被赋值并指向子元素 ,也就是说当ViewGroup不拦截事件并将事件交由子元素处理时mFirstTouchTarget != null。 反 过 来 ,一旦事件由当前ViewGroup拦截时 ,mFirstTouchTarget != null就不成立。那么当 ACTION_MOVE 和 ACTION_UP 事件到来时,由于(actionMasked == MotionEvent. ACTIQN_DOWN || mFirstTouchTarget != null)这个条件为 false,执行else代码块:intercepted = true,这将导致ViewGroup的onlnterceptTouchEvent不会再被调用,并且同一序列中的其他事件都会默认交给ViewGroup处理。

代码中还有一个比较关键的标志位FLAG_DISALLOW_INTERCEPT,可以看出当事件由子View处理时,该标志位才有意义。这个标记位是通过requestDisallowInterceptTouchEvent方法来设置的,一般用于子View中。FLAG_DISALLOW_INTERCEPT—旦设置后,ViewGroup将不再拦截除了ACTION_DOWN以外的其他点击事件。为什么ACTION_DOWN点击事件无法被拦截呢?这是因为在dispatchTouchEvent()方法开头便针对ACTION_DOWN点击事件会清除所有以前的状态,其中就含有对FLAG_DISALLOW_INTERCEPT标志的重置。所以导致子View中设置的FLAG_DISALLOW_INTERCEPT标志位失效。总结一下:FLAG_DISALLOW_INTERCEPT这个标志的作用是让ViewGroup不再拦截事件,当然前提是ViewGroup不拦截ACTION_DOWN事件。这点很重要,尤其在处理滑动冲突时特别有用。

            // 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
                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值