源码分析为什么requestDisallowInterceptTouchEvent(true)能阻止父View拦截事件

  事件分发机制是Android中的一个难点,但是现在很多人都在写关于Android事件分发机制的文章,并且讲的都很不错,很多人也基本明白了事件是首先一级一级向下分发(如果父View不拦截的话,即父View的onInterceptTouchEvent方法返回false),交由子View去处理,然后子View再将事件的处理结果一级一级向上反馈,子View没有处理完(子View的onTouchEvent方法返回了false),那么事件又会向上传递给父View,交由父View进行处理。
  很多时候子View并不希望父View拦截事件,这时我们该怎么做能够阻止父View拦截事件呢?首先我们看下ViewGroup的dispatchTouchEvent方法的部分源码:

// 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;
}

  从源码中我们可以看到这一句:
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
首先我们可以看到mGrouipFlag的声明protected int mGroupFlags;它并没有被赋值,因此默认为0,而FLAG_DISALLOW_INTERCEPT默认为0x80000,因此默认情况下(mGroupFlags & FLAG_DISALLOW_INTERCEPT) = 0,所以disallowIntercept为false,!disallowIntercept就为true了,因此代码接着向下执行intercepted = onInterceptTouchEvent(ev);即我们的父View会默认执行拦截事件。那我们怎么能够让父View默认不执行拦截事件呢,我们很多人都知道调用requestDisallowInterceptTouchEvent(true)方法就能够阻止父View拦截事件。那么为什么呢,接下来我们就从源码的角度来讲解这一句到底做了什么让父View就不拦截事件了,我们采用逆推的方法来说明。
  要想让dispatchTouchEvent方法中intercepted = onInterceptTouchEvent(ev);这一句不执行,必须的保证disallowIntercept 为 true,即(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;成立,现在我们就只要证明为什么(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;一定成立,接着我们就要看requestDisallowInterceptTouchEvent()方法的源码了:

/**
 * {@inheritDoc}
 */
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);
    }
}

我们设置了参数disallowIntercept 为 true,首先我们看到该方法中的第一个判断

if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
        // We're already in this state, assume our ancestors are too
        return;
    }

1、假设(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0) 为 true,此时if语句的条件成立,然后就return了,接着回到dispatchTouchEvent方法中,父View不拦截事件的要求就是要保证(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;明显成立,所以父View将不会拦截事件。
2、假设(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0) 为 false,因为disallowIntercept为true,因此会接着向下执行mGroupFlags |= FLAG_DISALLOW_INTERCEPT;即mGroupFlags = mGroupFlags | FLAG_DISALLOW_INTERCEPT;,然后我们回到dispatchTouchEvent方法中,父View不拦截事件的要求是要保证(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;即(mGroupFlags | FLAG_DISALLOW_INTERCEPT & FLAG_DISALLOW_INTERCEPT) != 0成立,而这个又等价于 FLAG_DISALLOW_INTERCEPT != 0,显然FLAG_DISALLOW_INTERCEPT是不等于0的,所以条件成立,父View不会拦截事件。
  至于为什么(mGroupFlags | FLAG_DISALLOW_INTERCEPT & FLAG_DISALLOW_INTERCEPT) != 0等价于FLAG_DISALLOW_INTERCEPT != 0,即为什么a | b & b = b,有兴趣的同学可以去了解java逻辑运算相关的知识。

你可能感兴趣的:(Android,Java)