概念
Touch事件分发中的对象:ViewGroup和View。
View的相关事件只有两个:dispatchTouchEvent、onTouchEvent。
ViewGroup的相关事件有三个:onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent。
触摸事件的动作:ACTION_DOWN、ACTION_MOVE、ACTION_UP
事件序列:ACTION_DOWN-->若干个ACTION_MOVE-->ACTION_UP(包含多个触摸事件的动作)
距离常量(TouchSlop):系统中预定义的用来判断用户手指在屏幕上的滑动是否是一个ACTION_MOVE动作
情景测试
Activity层和View层测试代码如下(只是将打印日志区分)
@Override public boolean onTouchEvent(MotionEvent event) { Log.e(Tag,"onTouchEvent_act"); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag,"onTouchEvent_act_down"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"onTouchEvent_act_move"); break; case MotionEvent.ACTION_UP: Log.e(Tag,"onTouchEvent_act_up"); break; } return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e(Tag,"dispatchTouchEvent_act"); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag,"dispatchTouchEvent_act_down"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"dispatchTouchEvent_act_move"); break; case MotionEvent.ACTION_UP: Log.e(Tag,"dispatchTouchEvent_act_up"); break; } return super.dispatchTouchEvent(ev); }
ViewGroup层测试代码同上,多了一个onInterceptTouchEvent方法
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e(Tag,"onInterceptTouchEvent_linear"); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: Log.e(Tag,"onInterceptTouchEvent_linear_down"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"onInterceptTouchEvent_linear_move"); break; case MotionEvent.ACTION_UP: Log.e(Tag,"onInterceptTouchEvent_linear_up"); break; } return super.onInterceptTouchEvent(ev); }
情景一:所有的操作都保持默认,点击View区域;结果如下
07-25 09:55:00.042 6764-6764/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_down
dispatchTouchEvent_linear
dispatchTouchEvent_linear_down
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_down
dispatchTouchEvent_view
dispatchTouchEvent_view_down
onTouchEvent_view
onTouchEvent_view_down
onTouchEvent_linear
onTouchEvent_linear_down
onTouchEvent_act
onTouchEvent_act_down
07-25 09:55:00.089 6764-6764/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_move
onTouchEvent_act
onTouchEvent_act_move
07-25 09:55:00.122 6764-6764/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_move
onTouchEvent_act
onTouchEvent_act_move
07-25 09:55:00.189 6764-6764/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_up
onTouchEvent_act
onTouchEvent_act_up
分析:可以看到一个事件列包含多个事件的传递
1,事件传递是拿事件列的Down事件作为令牌,通过第一次事件传递判断谁得到令牌使用权;接下来就通过再次事件传递将事件列的其它事件(Move,Up等事件)直接传递给该View;上面例子中的事件没有被消费掉,所以最终消费权又回到了activity层;调用了Activity的 onTouchEvent_act_move ,onTouchEvent_act_up
2,事件传递的过程是 先 dispatch(Activity外层 ——> ViewGroup中层——>View里层)
再 onTouch(Activity外层 <—— ViewGroup中层<——View里层)
注意:ViewGroup中如果存在onIntercept,在dispatch的过程中在ViewGroup层调用dispatchTouchEvent()之后会调用onInterceptTouchEvent();
事件传递的过程是 先 dispatch(Activity外层 —> ViewGroup中层(派遣) —> ViewGroup中层(拦截)—>View里层)
再 onTouch(Activity外层 <—— ViewGroup中层<——View里层)
情景二:改变dispatchTouchEvent的返回结果;进行观察
1,修改Activity外层dispatchTouchEvent返回为true,其他不变
07-25 11:00:23.312 16925-16925/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_down
07-25 11:00:23.392 16925-16925/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_move
07-25 11:00:23.472 16925-16925/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_move
07-25 11:00:23.482 16925-16925/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_up
分析:事件派发在activity层中止! Activity层的onTouch()没有被触发。
2,在上面1的基础上,将onTouchEvent()返回true
07-25 11:28:41.472 21808-21808/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_down
07-25 11:28:41.592 21808-21808/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_move
07-25 11:28:41.602 21808-21808/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_move
07-25 11:28:41.622 21808-21808/com.roi.audio2video E/rrrrrrrrr:
dispatchTouchEvent_act
dispatchTouchEvent_act_up
分析:返回的结果和上面的一样;还是要看一下Activity中的源码
/** * Called to process touch screen events. You can override this to * intercept all touch screen events before they are dispatched to the * window. Be sure to call this implementation for touch screen events * that should be handled normally. * * @param ev The touch screen event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
分析:当我们重写dispatchTouchEvent()时,是没有调用onTouch()方法的;也就是说ontouchEvent()本身是在dispatchTouchEvent()内部调用的;重写的时候直接返回true或者false当然是不能触发onTouch()方法的;
情景三:改变onTouchEvent的返回结果;进行观察
1,修改Activity层的onTouchEvent返回值为true;其他不变!
07-25 11:54:43.692 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_down
dispatchTouchEvent_linear
dispatchTouchEvent_linear_down
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_down
dispatchTouchEvent_view
dispatchTouchEvent_view_down
onTouchEvent_view
onTouchEvent_view_down
onTouchEvent_linear
onTouchEvent_linear_down
onTouchEvent_act
onTouchEvent_act_down
07-25 11:54:43.762 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
onTouchEvent_act
onTouchEvent_act_move
07-25 11:54:43.782 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
onTouchEvent_act
onTouchEvent_act_move
07-25 11:54:43.782 25835-25835/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_up
onTouchEvent_act
onTouchEvent_act_up
分析:可以很明显的发现,down事件是贯穿了整个传递的过程;但是事件列的其他事件(up,move)就直接送到activity外层的onTouchEvent()中被消费;而不会向内层View进行传递;
2,修改ViewGroup层的onTouchEvent返回值为true;其他不变!
07-25 13:44:22.282 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_down
dispatchTouchEvent_linear
dispatchTouchEvent_linear_down
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_down
dispatchTouchEvent_view
dispatchTouchEvent_view_down
onTouchEvent_view
onTouchEvent_view_down
onTouchEvent_linear
onTouchEvent_linear_down
07-25 13:44:22.392 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
dispatchTouchEvent_linear
dispatchTouchEvent_linear_move
onTouchEvent_linear
onTouchEvent_linear_move
07-25 13:44:22.592 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
dispatchTouchEvent_linear
dispatchTouchEvent_linear_move
onTouchEvent_linear
onTouchEvent_linear_move
07-25 13:44:22.612 5233-5233/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_up
dispatchTouchEvent_linear
dispatchTouchEvent_linear_up
onTouchEvent_linear
onTouchEvent_linear_up
分析:因为ViewGroup层消费掉了该事件,down事件在ViewGroup的onTouchEvent()中中断,停止向上层Activity中传递;事件列的其他事件(up,move)也跟随down事件建立的连接传到ViewGroup层中;不会向内层View进行传递;
情景四:改变onInterceptTouchEvent()的返回结果;进行观察
1,修改ViewGroup层的onInterceptTouchEvent()返回值为true;其他不变!
07-25 16:21:44.082 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_down
dispatchTouchEvent_linear
dispatchTouchEvent_linear_down
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_down
onTouchEvent_linear
onTouchEvent_linear_down
onTouchEvent_act
onTouchEvent_act_down
07-25 16:21:44.162 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
onTouchEvent_act
onTouchEvent_act_move
07-25 16:21:44.172 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
onTouchEvent_act
onTouchEvent_act_move
07-25 16:21:44.212 30048-30048/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_up
onTouchEvent_act
onTouchEvent_act_up
分析:当中间层ViewGroup对事件进行拦截之后;dispatchTouchEvent中断,无法向下传到View层;然后从ViewGroup开始onTouchEvent往外层的Activity进行传递;而onTouchEvent因为dispatchTouchEvent中断而无法到达最里层的View中
2,修改ViewGroup层的onInterceptTouchEvent()只对最里层view的纵向滑动进行拦截;并将里层View的onTouchEvent改为true!
private float startX,startY; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.e(Tag,"onInterceptTouchEvent_linear"); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: startX = ev.getX(); startY = ev.getY(); Log.e(Tag,"onInterceptTouchEvent_linear_down"); break; case MotionEvent.ACTION_MOVE: Log.e(Tag,"onInterceptTouchEvent_linear_move"); float dexX = Math.abs(ev.getX()-startX); float dexY = Math.abs(ev.getY()-startY); if (dexY > dexX && dexY > scaledTouchSlop) { Log.e(Tag,"拦截纵向滑动"); return true; } break; case MotionEvent.ACTION_UP: Log.e(Tag,"onInterceptTouchEvent_linear_up"); break; } return super.onInterceptTouchEvent(ev); }
在View上面纵向滑动,先看一下down事件的传递,因为View的onTouchEvent为true;所以Down事件到View层不会往上传递
07-25 16:46:46.622 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_down
dispatchTouchEvent_linear
dispatchTouchEvent_linear_down
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_down
dispatchTouchEvent_view
dispatchTouchEvent_view_down
onTouchEvent_view
onTouchEvent_view_down
在看一下事件列里面的其他事件的传递(move事件开始的时候是正常传递到里层的View,被拦截之后就无法到达了,从中间层直接往上传到Activity层了)
07-25 16:46:46.692 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
dispatchTouchEvent_linear
dispatchTouchEvent_linear_move
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_move
dispatchTouchEvent_view
dispatchTouchEvent_view_move
onTouchEvent_view
onTouchEvent_view_move
07-25 16:46:46.712 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
dispatchTouchEvent_linear
dispatchTouchEvent_linear_move
onInterceptTouchEvent_linear
onInterceptTouchEvent_linear_move
拦截纵向滑动
dispatchTouchEvent_view
onTouchEvent_view
07-25 16:46:46.732 3074-3074/com.roi.audio2video E/rrrrrrrrr: dispatchTouchEvent_act
dispatchTouchEvent_act_move
dispatchTouchEvent_linear
dispatchTouchEvent_linear_move
onTouchEvent_linear
onTouchEvent_linear_move
onTouchEvent_act
onTouchEvent_act_move