嵌套两层滑动view下,IllegalArgumentException: pointerIndex out of range的解决方法

感谢a405942873 的投递,笔记原文: http://blog.csdn.net/a405942873/article/details/40264685 

----------------------发现问题期间的一些记录,日期是后面补充的,不一定准确,不想看可以直接翻到下面。---------------------------

2014年10月17日23:48:11

10-17 17:51:26.370: W/System.err(2789): java.lang.IllegalArgumentException: pointerIndex out of range

10-17 17:51:26.370: W/System.err(2789): at android.view.MotionEvent.nativeGetAxisValue(Native Method)

10-17 17:51:26.375: W/System.err(2789): at android.view.MotionEvent.getX(MotionEvent.java:2148)

(debug,看看**getX(**传入了什么参数,返回了什么参数

10-17 18:02:55.385: W/System.err(5522): at android.widget.HorizontalScrollView.onTouchEvent(HorizontalScrollView.java:807)

(debug,看看传入了什么参数,返回了什么参数

------------------------------------------------------------以上可以忽略---------------------------------------------

2014年10月19日15:48:46

在HorizontalScrollView 里嵌套了一个类似viewpager的view。

重写了onInterceptTouchEvent 和 onTouchEvent 之后,

滑动内层,报下列错误。google 百度 stackoverfloew 有相关的问题,但无法解决。

网络上try catch  IllegalArgumentException,或者return false什么的,不能解决。

java.lang.IllegalArgumentException: pointerIndex out of range
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
at android.view.MotionEvent.getX(MotionEvent.java:2148)

2014年10月19日17:25:14

重写了,Menu的onInterceptTouchEvent 之后, return 的值被我人为的限定成(true  false),并没有调用super.onInterceptTouchEvent。

这时,如果条件满足拦截下了Touch事件,就会进入Menu的onTouchEvent。

在该方法最后的返回值为:super.onTouchEvent(ev);  

意味着,返回值是通过父类HorizontalScrollView 的 onTouchEvent 计算得出的。

在父类的onTouchEvent  里面

case MotionEvent.ACTION_MOVE:
/*这里必定会取出-1.原因是因为在down事件的时候,onInterceptTouchEvent没有对activePointerIndex 进行合理赋值。*/
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
if (activePointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
break;
}

Log.e(TAG,"Invalid pointerId="+ mActivePointerId +" in onTouchEvent");

所以Android 4.4 就会输出这一句。

Invalid pointerId=-1 in onTouchEvent

而Android  4.4以下,很可能会输出:

java.lang.IllegalArgumentException: pointerIndex out of range
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
at android.view.MotionEvent.getX(MotionEvent.java:2148)

pointerIndex out of range

pointerIndex 不在合理范围之内,是因为其值为-1。

 

结论:

由于没有调用 super.onInterceptTouchEvent(); 造成 mActivePointerIdactivePointerIndex的值不能正确获取。

导致父类 onTouchEvent取值错误,最终 onTouchEvent不能正确执行。

解决方法:

解决方法很简单,只要在重写过的 onInterceptTouchEvent里面 添加 super.onInterceptTouchEvent();既可。

注意,根据你的需求来return 相应的值,不一定要return 它,只需要调用。

目的是为了让父类的onInterceptTouchEvent 获得正确的参数。

例如mActivePointerIdactivePointerIndex 等。

思考:

为什么会出以上的问题?

首先,检讨自己对android系统的执行流程、原理不熟悉,就随意重写系统方法。

但是在出错之后,我是如何发现解决方法的呢?

走过许多弯路。百度 google stackflowover github 同有人有相同的问题,但不能完美解决。

最早,重写了onInterceptTouchEvent,并且,根据项目需求,写出拦截逻辑之后。

就出现了刚刚提到的报错提示。

1 由于报错来自系统,就忽略。一直以来都容易忽略系统级别的错误提示 ,需要反思。

2 改写逻辑,发现还是同样提示(反复调试修改、看一切关于Touch事件的文章,费时2天左右,无实际进展,但是默默积累了Touch事件的知识)。

3 通过搜索,发现只要是如此嵌套和重写onInterceptTouchEvent 就会出现该问题。

4 重新审视报错提示()。

5 反复进入系统源码跟踪调错调错调错调错,最终发现问题(周三到周日周末两天效率极其低下,只能算1天,历时4天,原谅我智商低)。

    在这个过程中,得到经验教训:

如果要调试系统源码,必须要用官方的虚拟机,相应版本的sdk,以及相应版本的编译target。只有这样才能正确debug系统源码。不然你会发现,debug 的时候没有章节、不合理的跳来跳去。

   真机和genymotion的系统能否完全对应sdk的源码?无法确定, 因此,不要以为有了 真机 或 genymotion就无敌了。

X86的ABI虽然流畅,但是会有莫名其妙的问题,建议还是用ARM。


2014年10月19日15:48:46

    20141019183528480.gif

这样子,内外层都能滑动,而且不冲突了。

这样结论:

由于没有调用 super.onInterceptTouchEvent(); 造成 mActivePointerIdactivePointerIndex的值不能正确获取。

导致父类 onTouchEvent取值错误,最终 onTouchEvent不能正确执行。