研究requestDisallowInterceptTouchEvent失效的原因

前言:

前面我们讲到过onTouch事件的传递的原理,那么我们遇到事件冲突的时候就可以通过这个原理来设法处理了,一般来说我们分为父控件处理和子控件处理,其中子控件处理的时候会用到一个requestDisallowInterceptTouchEvent方法,接下来我们就一起来分析一下这个方法的原理以及使用时容易遇到的坑

1.基础

我们之前讲到过onTouch事件的传递的原理,如果对这块不熟悉的朋友可以先看一下这个文章

onTouch事件的传递

http://blog.csdn.net/yulyu/article/details/56846752

2.效果

requestDisallowInterceptTouchEvent,见名知意,主要的效果就是让控件的父控件不要调用onInterceptTouchEvent方法,并且不要拦截事件,这样子控件就能拿到所有的事件,然后根据自己的逻辑进行处理

3.调用

//不要父控件拦截
getParent().requestDisallowInterceptTouchEvent(true);
//需要父控件考虑拦截
getParent().requestDisallowInterceptTouchEvent(false);

4.原理

requestDisallowInterceptTouchEvent这个方法名太长,下面为了方便描述就用request代替

首先request的实现逻辑是在ViewGroup里面完成的

//ViewGroup.java

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

这里主要有两个地方要注意:

一个是这个方法是向上传递的,request里面又调用了父控件的request,也就是这个方法的效果是向上传递的

mParent.requestDisallowInterceptTouchEvent

一个是request方法里面主要是修改了mGroupFlags的值

(这里用的是 |= ,我们就不做详细的解释了,这里我们就简单的当作mGroupFlags= FLAG_DISALLOW_INTERCEPT)

mGroupFlags |= FLAG_DISALLOW_INTERCEPT;

我们搜索一下FLAG_DISALLOW_INTERCEPT的引用,可以发现只有三个地方用到了他

  • 1.requestDisallowInterceptTouchEvent: (就是我们刚刚介绍的将GroupFlags设为FLAG_DISALLOW_INTERCEPT)
  • 2.dispatchTouchEvent
  • 3.resetTouchState

dispatchTouchEvent里面的核心代码如下,主要就是指如果请求了不拦截,那么就不调用onInterceptTouchEvent

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

这里我们就可以看出来request方法是怎么发生作用的了,但是我们刚才说了还有一个地方调用了FLAG_DISALLOW_INTERCEPT,那就是resetTouchState

/**
 * Resets all touch state in preparation for a new cycle.
 */
private void resetTouchState() {
    clearTouchTargets();
    resetCancelNextUpFlag(this);
    mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
    mNestedScrollAxes = SCROLL_AXIS_NONE;
}

可以看出resetTouchState里面主要是做的一些还原处理,比如说将mGroupFlags设置为不等于FLAG_DISALLOW_INTERCEPT,那么resetTouchState又是什么时候被调用呢?

// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
    cancelAndClearTouchTargets(ev);
    resetTouchState();
}

我们搜索引用,可以看到在dispatchTouchEvent方法里面,当DOWN事件发生时,resetTouchState被调用了,于是mGroupFlags被设置为不等于FLAG_DISALLOW_INTERCEPT

5.填坑

如果不懂原理,那么requestDisallowInterceptTouchEvent使用时,会遇到一些奇怪的现象,只要我们注意两点,那么就不会再掉坑里去了

  • 1.每次DOWN事件发生的时候,会做一次清除设置的处理,所以在DOWN事件之前调用requestDisallowInterceptTouchEvent是没有意义的,一般我们都是在子类里面收到DOWN事件后请求父控件不要拦截接下来的事件
  • 2.就算是调用了requestDisallowInterceptTouchEvent,父控件的DOWN事件也是一定会走onInterceptTouchEvent的,所以想要父控件不要拦截,那么父控件的onInterceptTouchEvent在DOWN事件的时候一定要返回false,表示不要拦截DOWN事件

热门文章

  • onTouch事件传递
  • 进程间通信–AIDL
  • 序列化–Serializable与Parcelable
  • 如何解决内存溢出以及内存泄漏
  • Okhttputils终极封装
  • FaceBook推出的调试神器
  • Android代码优化工具
  • Glide-入门教程
  • Glide-图片预处理(圆角,高斯模糊等)
  • Glide-图片的压缩
  • Glide-内存缓存与磁盘缓存
  • Glide-自定义缓存

你可能感兴趣的:(安卓)