探究requestDisallowInterceptTouchEvent失效的原因

昨天在用requestDisallowInterceptTouchEvent的时候,发如今设置了requestDisallowInterceptTouchEvent(true)之后,父View的onInterceptTouchEvent方法照样运行。

怎么会这样呢?仅仅能通过查看源代码来一探到底了。

首先看下requestDisallowInterceptTouchEvent方法的源代码,在android.view.ViewGroup类中:

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

这里看到,requestDisallowInterceptTouchEvent方法做了两个操作:

1. 设置ViewGroup自己的FLAG_DISALLOW_INTERCEPT标志位为true。

2. 递归设置ViewGroup其父View的FLAG_DISALLOW_INTERCEPT标志位为true。

主要和FLAG_DISALLOW_INTERCEPT标志位有关。这里全局看下android.view.ViewGroup的源码,发现仅仅有三个地方用到了FLAG_DISALLOW_INTERCEPT:

read:dispatchTouchEvent方法。

write:requestDisallowInterceptTouchEvent方法,resetTouchState方法。

先来看下涉及到write操作的地方,requestDisallowInterceptTouchEvent方法前面看过了,这里看下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方法会把ViewGroup的FLAG_DISALLOW_INTERCEPT置为false。

再来看下涉及到读操作的dispatchTouchEvent方法的源码:

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

            ......
    }

这里就是整个ViewGroup代码中唯一用到FLAG_DISALLOW_INTERCEPT的地方。它的主要罗辑就是推断ViewGroup的FLAG_DISALLOW_INTERCEPT标志位是否为true。假设为true,调用ViewGroup的onInterceptTouchEvent的方法。假设不是则不会走。

这也和requestDisallowInterceptTouchEvent的官方解释也是一致的。

而我遇到的问题是:明明调用了requestDisallowInterceptTouchEvent(true)方法。而还是走了ViewGroup的onInterceptTouchEvent方法,那仅仅有一个可能:FLAG_DISALLOW_INTERCEPT被改回了false。对FLAG_DISALLOW_INTERCEPT进行写操作的除了requestDisallowInterceptTouchEvent方法,仅仅有resetTouchState方法,一定是某个地方调用了resetTouchState方法,把FLAG_DISALLOW_INTERCEPT标志位置为false。才造成了我遇到的问题。

看一下resetTouchState的调用关系,发现仅仅有一个方法调用了:

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

            ......

            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch gesture.
                // The framework may have dropped the up or cancel event for the previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            ......	

            // Update list of touch targets for pointer up or cancel, if needed.
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }

            ......
    }

看完代码就发现,原来在ViewGroup的ACTION_DOWN时,FLAG_DISALLOW_INTERCEPT标志位被置为false,这就是我使用requestDisallowInterceptTouchEvent方法失效的原因。由于我是在findView之后,直接调用的requestDisallowInterceptTouchEvent(true),而在事件处理的開始(ACTION_DOWN)。这里又被设为了false。

怎么解决问题呢?仅仅有重写自己用到的View的onTouchEvent方法,在其ACTION_DOWN的时候,调用父View的requestDisallowInterceptTouchEvent(true)方法设置,在ACTION_UP或者ACTION_CANCEL的时候。调用调用父View的requestDisallowInterceptTouchEvent(false)方法重置。

时间: 2024-10-10 16:14:31

探究requestDisallowInterceptTouchEvent失效的原因的相关文章

js报TypeError $(...) is null错误,jquery失效的原因及解决办法

最近在工作中发现个问题,原本好好的网页,写了一些自己的jquery代 码之后,竟然总是不起作用,无论写的多么简单,都不起作用,似乎jquery失效了一般,在火狐下调试看了下,页面报TypeError $(...) is null这种错误,找了半天原因最后发现竟是页面中加载的一个插件给捣的鬼,是它将jquery的$方法给覆盖了.对于这个问题,现在分享两种解决方法. (1)删冲突插件,jquery作为基础库,当然是没有理由被删了.这个方法最直接了. (2)将jquery的$方法改名,具体改名方法如下

关于动态生成dom绑定事件失效的原因

之前做项目都是直接用jquery的bind绑定事件,不过当时都不是动态生成dom元素,而是已经页面中原本存在的dom元素进行事件绑定,最近在测试给动态生成的dom绑定事件的时候发现事件失效,于是就测试了一下: 1.事件失效的原因:(1)bind事件绑定只对dom中存在的元素有效,对于我们后来动态增加的元素是监测不到,所以绑定不了 (2)同样,当你使用var aa = document.getElementsByTagName("动态生成的元素");来获取动态生成的元素的时候也是获取不到

Spring3.x事务失效的原因以及解决办法

项目中如果使用spring来管理事务,可能会出现事务失效的情况,我认为主要的原因是cglib无法获取到代代理的实例.. 如果带上事务,那么用annotation方式的事务注解和bean配置,事务会失效,要将service的bean配置到xml文件中才行,这样springmvc就不会扫描到@Service的类了 这个问题有另一种解决办法: 首先在主容器中(applicationContext.xml),将Controller的注解排除掉 <context:component-scan base-p

在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法

在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解是不会生效的. 比如,下面代码例子中,有两方法,一个有@Transational注解,一个没有.如果调用了有注解的addPerson()方法,会启动一个Transaction:如果调用updatePersonByPhoneNo(),因为它内部调用了有注解的addPerson(),如果你以为系统也会为它启动一个Transaction,那就错了,实际上是没有的. @Service public cla

Spring事务失效的原因

Spring事务失效的原因 5种大的原因 如使用mysql且引擎是MyISAM,则事务会不起作用,原因是MyISAM不支持事务,可以改成InnoDB 假如有兴趣了解 mysql中 " engine=innodb " 以及 " engine=innodb 和engine=myisam的区别 ",可以读读这篇文章:http://blog.sina.com.cn/s/blog_6ac4c6cb01018pb1.html 可使用下述语句之一检查表的标类型: SHOW TAB

@Transational)的方法,注解失效的原因和解决方法

在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解是不会生效的. 比如,下面代码例子中,有两方法,一个有@Transational注解,一个没有.如果调用了有注解的addPerson()方法,会启动一个Transaction:如果调用updatePersonByPhoneNo(),因为它内部调用了有注解的addPerson(),如果你以为系统也会为它启动一个Transaction,那就错了,实际上是没有的. @Service public cla

wpf 绑定失效的原因及解决方案

有时候,您会发现在程序开始时还能正常运行的绑定失效了.就个人经验而言,绑定的失效主要分为两种情况:对于One-way绑定而言,如果软件开发人员绕过绑定直接更改了目标属性,那么绑定将会失效.而对于Two-way绑定而言,如果软件开发人员没有通过绑定直接更改了目标属性,而目标属性对源属性的更新由于抛出异常等原因失败,那么绑定也将失效. 建议解决方案,针对于Two-way的绑定,自定义一个实现了IValueConverter的类,应用到绑定的Converter属性,在Convert和ConvertBa

百度地图API 与 jquery 同时使用时报 TypeError $(...) is null错误 失效的原因及解决办法

在引用百度地图API后,发现jquery 根据id 找不到 form.但是对于别的控件没有问题. 在排除了 html加载的问题后. 上网查找 发现以下解决办法: 原因应该是有冲突的插件. 解决办法将 $符号改为jQuery 引用: http://www.phpernote.com/jquery/851.html 最近在工作中发现个问题,原本好好的网页,写了一些自己的jquery代码之后,竟然总是不起作用,无论写的多么简单,都不起作用,似乎 jquery失效了一般,在火狐下调试看了下,页面报Typ

胡博君讲解CSS中注释失效的原因

在Dreamweaver自动生成的网页中,head中的样式表往往是这样的形式: <style type="text/css"> <!-- ...... --> </style> 在样式表的开头和结尾会自动添加一套html注释标记:<!--和-->.如果以为这个跟html的注释标记是一样,用这个写注释,会导致样式表定义失效. 当<!--后面加上字之后,下面的内容就全被忽略了,直到</style>结束. 这是一个很容易被忽视