说一说Android事件分发中的requestDisallowInterceptTouchEvent

Posted BennuCTech

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了说一说Android事件分发中的requestDisallowInterceptTouchEvent相关的知识,希望对你有一定的参考价值。

前言

我们知道在事件分发过程中是存在一个拦截机制的

onInterceptTouchEvent

当它返回true则不向下分发事件,否则向下分发。

但是在这个过程中,还有一个参与者:requestDisallowInterceptTouchEvent,这个函数直接影响事件的拦截。我们今天就来说一说这个这个函数是如何影响事件分发的。

源码分析

我们先看看这个函数的源码

@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);
    

可以看到它改变了一个开关FLAG_DISALLOW_INTERCEPT,同时调用其parent的函数。

那么这个开关有什么用?

在ViewGroup的dispatchTouchEvent函数开头有这样一段代码:

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;

先来看判断逻辑,当是down事件或者mFirstTouchTarget不为空,则进入一个代码段;否者拦截设置为true。

我们知道down事件分发过程中,如果有子view消费事件,则赋值给mFirstTouchTarget,后续事件会直接分发给mFirstTouchTarget

这里也可以看出,如果有子View消费了down事件,即mFirstTouchTarget不为空,所以后续事件还会检查拦截。

所以上面就可以理解了,如果down事件中没有子view消费事件,那么后续事件的拦截都为true。所以后续事件不会再遍历子View。

下面再看if代码段

一开始就使用了FLAG_DISALLOW_INTERCEPT开关,即disallowIntercept

disallowIntercept为true,则不拦截;否者判断onInterceptTouchEvent

所以简单来说requestDisallowInterceptTouchEvent设置为true可以跳过onInterceptTouchEvent,不拦截事件。

而且因为requestDisallowInterceptTouchEvent又调用了parent的函数,所以所有层次的父view都不再拦截。

所以requestDisallowInterceptTouchEvent的功能是让这个view及上面的所有父view都放开拦截,即使onInterceptTouchEvent为true。

所以我们一般如下使用

view.getParent().requestDisallowInterceptTouchEvent(true);

这样view的所有层次的父view都不会拦截事件了。

扩展思考

下面让我们再深入想想。上面这种的情况是在touch事件发生前设置onInterceptTouchEvent,也是我们一般的用法。但是如果事件发生过程中调用这个函数呢?

比如在view的onTouch的某个事件中使用

getParent().requestDisallowInterceptTouchEvent(true)

当事件开始分发时,down事件进入父view的dispatchTouchEvent时,这是子view还未得到事件,所以没有设置requestDisallowInterceptTouchEvent

这时如果父view的onInterceptTouchEvent返回true,即拦截的话,事件则不会分发给子view了,所以requestDisallowInterceptTouchEvent永远不会执行,子view则无法得到事件。

但是如果父view的onInterceptTouchEvent返回false,即不拦截的话,事件就可以分发到子view,requestDisallowInterceptTouchEvent执行,之后的事件都会跳过父view的onInterceptTouchEvent的判断

例如父view的onInterceptTouchEvent代码如下

   public boolean onInterceptTouchEvent(MotionEvent ev) 
        switch (ev.getAction()) 
            case MotionEvent.ACTION_DOWN:
                return false;
            case MotionEvent.ACTION_MOVE:  
                    return true;
            case MotionEvent.ACTION_UP:
                return true;
            default:
                break;
        
        return false;   
    

down事件不进行拦截,但是拦截了move和up事件。

如果子view的onTouch的down事件中使用

以上是关于说一说Android事件分发中的requestDisallowInterceptTouchEvent的主要内容,如果未能解决你的问题,请参考以下文章

Android重学系列 IMS与事件分发(下)

说一说Unty3d中的资源存放目录和获取方法

说一说垃圾回收的原理,讲一下过程?

Android触摸事件分发机制

说一说 Flux的小白知识

Android面试View的事件分发