dispatchTouchEvent事件分发浅析(七)reque

作者: dodo_lihao | 来源:发表于2016-08-28 23:52 被阅读472次

    上一篇,我们大体理解了对应的ACTION_DOWN 之后
    对应的ACTION_MOVE 和 ACTION_UP 的简单过程
    当然,还分是否消费等

    具体代码可以见https://github.com/2954722256/demo_event

    这篇,我们一起来看下简单了解下事件冲突
    还有一个简单解决事件冲突的例子


    requestDisallowInterceptTouchEvent方法说明

    我们可以看看API
    在android的sdk,对应的
    XxxAndroidSdkDir/docs/reference/android/view/ViewGroup.html
    (因为这里不是将ViewGroup,所以其他略)
    我们可以 搜索 requestDisallowInterceptTouchEvent, 找到对应的方法

        void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
    
        Called when a child does not want this parent and its ancestors to intercept touch events with onInterceptTouchEvent(MotionEvent).
    

    大概就是说,当 子View 不想被 父View 拦截的时候,
    就可以调用requestDisallowInterceptTouchEvent(MotionEvent)方法,
    这样,可以放父View的 onInterceptTouchEvent(MotionEvent)失效
    (当然,还有其他情况,现在暂时略)


    dispatchTouchEvent, onInterceptTouchEvent, requestDisallowInterceptTouchEvent简单关系

    前面我们有说过,onInterceptTouchEvent 方法,只有ViewGroup有,并且默认是return false的

    **onInterceptTouchEvent **

        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }
    

    那我们来看看 dispatchTouchEvent 方法
    (我们先贴一下源码,再一起看看)

    前面其实有一些修改Flags值的地方,暂时略
    
    ...前面省略...
    
            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;
        }
        ......
        if (!canceled && !intercepted) {
            ......
    
    ...后面省略...
    

    这里我们可以看见,对应的判定,
    都是和2个局部变量canceled 和 intercepted有关
    canceled 先不考虑,
    intercepted 是和 mGroupFlags & FLAG_DISALLOW_INTERCEPT 有关
    这2个变量,取与来判断 !=0
    换句话说,就是对应的二进制码, 有没有都为 1 的地方。


    mGroupFlags & FLAG_DISALLOW_INTERCEPT 简单分析

    因为上面 intercepted 是和 mGroupFlags & FLAG_DISALLOW_INTERCEPT 有关
    我们只要跟踪这2个变量,就可以大体找到对应的情况了

    我们可以发现,
    mGroupFlags 和很多方法有关,方法中和很多二进制的变量有关,并且这些二进制码都不太一样, 比较复杂,只能暂时放一下
    FLAG_DISALLOW_INTERCEPT 就比较简单点, 只和
    dispatchTouchEvent
    resetTouchState()
    requestDisallowInterceptTouchEvent
    这3个方法有关
    (先忽略resetTouchState()这个方法,也就是上面说的别的情况, dispatchTouchEvent上面已经贴了相关的代码)
    这里我们贴一下 requestDisallowInterceptTouchEvent 方法实现

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

    mGroupFlags 我们发现没赋初值
    FLAG_DISALLOW_INTERCEPT = 0x80000;
    这里是16进制的,16进制1位相当于2进制的4位,
    8是2的3次方,也就是1000
    后面4个0,换成2进制,也就是16个0
    整体就是10000000000000000000, 也就是1后面19个0

    我们可以发现,对应的16进制值对应位数上都是不同的2的次方数,换句话说,对应的2进制数值,对应的位的1值,是不会相同的
    所以 mGroupFlags & FLAG_DISALLOW_INTERCEPT 判断的时候,只需要判断那个第20位的1,即可
    而后面 mGroupFlags的取与,取非后的或, 都只会那个第20位的数字
    (我们可以理解,这里 mGroupFlags , 就是很多Flag对应boolean值的集合,每一位就一个不同的Flag变量的boolean值容器思维上和 Bloom Filter 的实现理论相似

    这里 requestDisallowInterceptTouchEvent 方法
    其实,就是修改当前 mGroupFlags 对应 FLAG_DISALLOW_INTERCEPT 位上的boolean值,再如果有 父View,修改父View的 mGroupFlags 值

    如果为true,则会执行后面的方法,修改 父View对应的 mGroupFlags 值


    最后,我们回到 上面贴的 dispatchTouchEvent 方法中,
    判断是否走onInterceptTouchEvent(ev)的地方
    如果 disallowIntercept 为 false,就会走
    再看下 disallowIntercept 的判断

    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    

    这个地方,如果 mGroupFlags 对应位上是1, 就为true,就不会走

    也就是 requestDisallowInterceptTouchEvent 设置 true,
    就不会走 onInterceptTouchEvent(ev) 方法


    ScrollView 与 WebView 的冲突

    我们有的时候会遇到这种情况,
    整个是 ScrollView ,内部有一个 WebView ,还有其他的一些 子View
    这时候,WebView 显示不全
    但是, 滑动的时候, 又会触发外面 ScrollView的上下滚动,
    于是 内部的WebView就看不全了

    Paste_Image.png

    我们来看一下 ScrollView 的 onInterceptTouchEvent(MotionEvent ev) 方法

        final int action = ev.getAction();
        if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
            return true;
        }
    ...后面省略...
    

    换句话说,只要是 MotionEvent.ACTION_MOVE, 后面都不用看, 就会被拦截
    那内部的 子View的滑动,肯定不会被监听到了

    我们在搜索下 WebView
    里面完全没有 requestDisallowInterceptTouchEvent 相关方法的调用

    所以, ScrollView 中套用 WebView 肯定会有这样的问题


    简单解决ScrollView 与 WebView 的冲突

    根据上面的思路, 我们只需要 Override对应的
    onInterceptTouchEvent(MotionEvent ev) 方法
    调用 requestDisallowInterceptTouchEvent(true); 即可

    经检验, 是可以的
    (对应的代码,见上面的github地址,在 disallowintercept 的Module中)


    下一篇我们可以了解
    dispatchTouchEvent事件分发浅析(八)简单解决ScrollView 与 WebView 的冲突

    相关文章

      网友评论

        本文标题:dispatchTouchEvent事件分发浅析(七)reque

        本文链接:https://www.haomeiwen.com/subject/ighxettx.html