android系统中的每个ViewGroup的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
注意:不是所有的View的子类,只有可以向里面添加View的控件才需要分发,比如TextView它本身就是最小的view了,所以不用再向它的子视图分发了,它也没有子视图了,所以它没有dispatch和Intercept,只有touchEvent。而 Activity 对 onInterceptTouchEvent(MotionEvent ev) 也就是事件拦截不进行响应。
三个方法的用法
dispatchTouchEvent():用来分派事件。
其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法。
Touch 事件发生时 Activity 的 dispatchTouchEvent(MotionEvent ev) 方法会以隧道方式(从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递)将事件传递给最外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法,并由该 View 的 dispatchTouchEvent(MotionEvent ev) 方法对事件进行分发。dispatchTouchEvent 的事件分发逻辑如下:
如果 return true,事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费,同时事件会停止向下传递; 如果 return false,会将事件返回给父 View 的 onTouchEvent 进行消费。 如果返回系统默认的 super.dispatchTouchEvent(ev),事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。
注意:dispatchTouchEvent()的三种返回值效果各不相同。可简单理解为: 返回true,不传给别人,自己消费,事件消失; 返回false,向上传递,即反方向返回传递,向父类onTouchEvent传递。 返回super.dispatchTouchEvent(ev),默认继续传递,传递给当前onInterceptTouchEvent。
可以用一段伪代码来理解:
[代码]java代码:
1 2 3 4 5 6 7 8 9 | public boolean dispatchTouchEvent(MotionEvent ev){ boolean consume = false ; if (onInterceptTouchEvent(ev)){ consume = onTouchEvent(ev) ; } else { consume = child.dispatchTouchEvent(ev) ; } return consume ; } |
onInterceptTouchEvent():用来拦截事件。
在外层 View 的 dispatchTouchEvent(MotionEvent ev) 方法返回系统默认的 super.dispatchTouchEvent(ev) 情况下,事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。onInterceptTouchEvent 的事件拦截逻辑如下:
如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理; 如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发; 如果 onInterceptTouchEvent 返回 super.onInterceptTouchEvent(ev),同false效果,将事件放行。
简单理解为:onInterceptTouchEvent()决定了事件是继续向子控件传递,还是被当前view处理。返回false和super.onInterceptTouchEvent(ev)时不拦截,继续传递。返回true时不再按顺序传递(要么ontouch自己处理掉,要么反向传递给父控件)。
注意:手指点击的地方的最上层view,不管onInterceptTouchEvent()返回什么,都一定会继续走自己的onTouchEvent()。 手指点击的地方不一定是最上层view,例如最上层是一个对话框,只遮挡中间一部分屏幕。那么手指按下对话框的外面部分,这个外面部分 在整个屏幕范围内并不是最上层view,但是手指按在上面的时候,他是手指按下的这块面积的最上层view,因为对话框没有遮挡到这里。 这时这个外面部分的layout的onInterceptTouchEvent()不管返回什么,都会继续执行自己的onTouchEvent(),因为这时他就是最上层!!! 而所谓的对话框,并没有被触摸时,不会触发任何方法。
onTouchEvent():用来处理事件。
在 dispatchTouchEvent 返回 super.dispatchTouchEvent(ev) 并且 onInterceptTouchEvent 返回 true 的情况下 onTouchEvent 会被调用(按照上面的说明,onInterceptTouchEvent返回什么都有可能被调用)。onTouchEvent 的事件响应逻辑如下:
如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收
,如果传递到上面的 onTouchEvent 也返回 false,这个事件由最上层view执行一次,然后反向传递(onTouchEvent中传递), 返回false和super的都接收不到下一次事件,哪一个返回true了,哪一个才能接受到下一次事件。如果都返回false,则由activity的onTouchEvent处理。
如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。
简单理解为: onTouchEvent()有两种状态:自己消费,不再传递,return true; 由子控件传到父控件(反向传),return false 或者return super.onTouchEvent(ev)。
举例说明
布局文件如下
1、TextView的onTouchEvent()方法返回false,其他返回super
2、TextView的onTouchEvent()方法返回true,其他返回super
3、LinearLayout的onInterceptTouchEvent()方法返回true,拦截事件,再重写onTouchEvent()方法,返回true 注意,最后一个ACTION_MOVE传递跳过了onInterceptTouchEvent,直接进入了onTouchEvent,参考下面的“记忆”功能。
总结
1、Android中的事件传递基于先捕获然后冒泡的形式。 在捕获阶段,事件先由外部的View接收,然后传递给其内层的View,依次传递到更够接收此事件的最小View单元,完成事件捕获过程; 在冒泡阶段,事件则从事件源的最小View单元开始,依次向外冒泡,将事件对层传递。
2、这一系列的传递流程都是dispatchTouchEvent()方法来控制的,如果不人为地干预, 事件将由上自下依次传递(因为默认是返回false不会拦截的),从activity传递到最底层的View,就由它的onTouchEvent()方法来处理事件, 若处理成功返回true,若处理失败返回false,事件依次向上传递,每个View都调用自己的onTouchEvent()方法来处理事件, 若处理成功就终止传递,若处理失败就继续向上传递。
3、经过人为的干预,若在向下传递的过程中被拦截了,即onInterceptTouchEvent()方法返回true,则事件将停止向下传递,直接由当前的onTouchEvent()方法来处理,若处理成功则OK,若处理不成功,则事件会向上传递。
4、另外,dispatchTouchEvent()方法中还有“记忆”的功能,如果第一次事件向下传递到某View,它把事件继续传递交给它的子View,它会记录该事件是否被它下面的View 给处理成功了,(怎么能知道呢?如果该事件会再次被向上传递到我这里来由我的onTouchEvent()来处理,那就说明下面的View都没能成功处理该事件);当第二次事件向下传递到该View, 该View的dispatchTouchEvent()方法就会判断,若上次的事件由下面的view成功处理了,那么这次的事件就继续交给下面的来处理,若上次的事件没有被下面的处理成功, 那么这次的事件就不会向下传递了,该View直接调用自己的onTouchEvent()方法来处理该事件。
5、“记忆”功能的信息只在一系列事件完成之前有效,如从ACTION_DOWN事件开始,直到后续事件ACTION_MOVE,ACTION_UP结束后,“记忆”的信息就会清除。也就是说如果某View处理ACTION_DOWN事件失败了(onTouchEvent()返回false),那么后续的ACTION_MOVE,ACTION_UP等事件就不会再传递到该View了,由其父View自己来处理。在下一次发生ACTION_DOWN事件的时候,还是会传递到该View的。