Android事件分发机制

在此简单记录Android中的事件分发机制:

当触发一个点击事件时,最先会由 Activity 的 dispatchTouchEvent 方法接受到事件,默认是返回 false, 需要调用父类的方法作为返回值, 才能进行正常的事件分发.

接下来是由 ViewGroup 的 dispatchTouchEvent 接受到事件, 该方法的返回 false 会直接回到父控件的 onTouchEvent 方法处理事件, 该方法返回 true ,事件到此消费, 停止传递.   调用 super 作为返回值(为true), 可以进行正常的事件分发, 其内部会调用 onInterceptTouchEvent 方法, 源码:

// ViewGroup 的 dispatchTouchEvent 方法
public boolean dispatchTouchEvent(MotionEvent ev) {
    // 省略其他无关代码....
    // 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;
            }
 }

如果 onInterceptTouchEvent 返回了 true ,那么该事件会由 ViewGroup 的 onTouchEvent 方法执行, 并且在同一个事件序列中,后续事件都不会再分发到 onInterceptTouchEvent 方法内部, 会直接分发到 onTouchEvent 方法内部 (官方描述) (如果ViewGroup的onTouchEvent 方法返回了true的话,否则只会分发到 返回了 true 的那个 onTouchEvent 方法内部).

onInterceptTouchEvent 方法默认返回 false, 返回super效果一样, 接下来会进入到 View 的事件分发.


VIew的事件分发中, 也是 dispatchTouchEvent 先接受到事件, 该方法返回 false, 代表分发失败, 直接返回到父控件的 onTouchEvent 方法, 如果该方法返回 true, 代表事件消费,终止传递, 因此,要使 view 的 onTouchEvent 方法得到正常执行, 应该以 super 作为返回值(为true), 其内部会调用 view 的 onTouchEvent 方法,(会在调用 onTouchEvent 方法之前查看是否设置了 onTouchListener,  如果设置了,就会先执行 onTouch 方法) 源码:

public boolean dispatchTouchEvent(MotionEvent event) {
    ...
       if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) { // 注意此处
                result = true;
            }

            if (!result && onTouchEvent(event)) { // 注意此处
                result = true;
            }
        }
    ...
}

对于 onTouchEvent 方法的返回值的区别:

View 的 onTouchEvent 方法返回 false, 代表事件未处理完成, 会从下往上依次调用 父控件的 onTouchEvent 方法.   该方法返回 true, 代表事件消费, 终止传递(从而导致 onCLick 等其他方法无法执行). View的 super.OnTouchEvent 方法默认为返回为 true, 但其内部执行了performClick() 方法,从而可以让 onClick 等其他后续方法得以执行.

ViewGroup 的 onTouchEvent 方法默认返回 false. 事件会依次传递到父控件.


onTouchEvent 方法返回值对 后续 move 和 up 事件接受的影响:

无论是 view 还是 viewGroup, 如果 dispatchTouchEvent 方法返回 false, 事件都会直接回到父控件的 onTouchEvent 方法内部处理.

而 onTouchEvent 方法无论是 view 还是 viewGroup, 返回 false 都代表事件未处理完成, 从而导致事件 往父控件传递, 因此在同一个事件序列中的后续 move 和 up 事件都无法接收.

后续的 move 和 up 事件只会分发到 onTouchEvent 方法返回了 true 的控件的该方法内部, 但是 activity 的 onTouchEvent 方法比较特殊, 无论其返回 false 和 true 都可以接受到后续的 move 和 up 事件, 估计是因为其是顶级处理者的原因.


view 的 clickable , longClickable 和 enable  属性对事件分发的影响: 

view 的 enable 属性不会对 onTouchEvent 方法的调用和的返回值产生影响.

只要这个 view的 clickable 和 longClickable 其中一个为 true, 那么onTouchEvent 方法就会返回 true, 注意 : view 的 longClickable 属性默认为 false, clickable 属性要分具体情况而定, 如果是可点击的 view , 如 button 就为 true, 一些不可点击的 view, 如 textView 就为 false. 同时 setClickListener 和 setLongClickListener 会把默认这两个属性置为 true.

view 的 onTouchEvent 部分源码:

       if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return clickable;
        }

        省略后续对不同的 action 处理...

从源码中我们可以看到, 如果 一个 view 是 disable 状态的, 其 onTouchEvent 方法会在对不同的 action 事件处理之前返回一个 clickable, 所以也就可以解释为什么我们设置了 disable 状态之后, touchListener , onClickListener, onLongClickListener 等等监听都不可用的原因了.


补充:

如果不是自定义控件, enable 属性表示当前 view 是否可用, 如果为 false 将会导致所有监听不可用.

view 的 onTouchEvent 默认返回 true,  viewGroup 的 onTouchEvent 默认返回 false.

一个 view 是 clickable 且同时接受到了 down 和 up 事件才能触发 onClick 回调, 少一样都不行.

onLongClick 只关注于 down 事件,(官方描述: 在一个 view 被点击且保持的情况下, 会回调该方法), 因此其返回值代表是否消费这个长按的 down 事件(其实也就是一个 down 事件, 只是时间较长), 如果消费, 必然不会触发 onClick,(因为无法接收到 down 事件).

view 的 requestDisallowInterceptTouchEvent 方法可以去影响父控件的对事件的拦截, 需要注意的是, 此方法对 down 事件无效, 也就是说, 如果设定了不拦截, 父控件依然可以对 down 事件决定是否拦截, 原因是因为 viewGroup 在 down 事件中会把标志重置. 具体原因 : 探究requestDisallowInterceptTouchEvent失效的原因

action cancel 事件的触发情况为: 当父控件需要收回事件处理权的时候, 其子控件都会收到一条 action cancel 事件, 不管是 view 还是 viewGroup. 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值