当屏幕中包含一个ViewGroup,而这个ViewGroup又包含一个子view,这个时候android系统如何处理Touch事件呢?到底是ViewGroup来处理Touch事件,还是子view来处理Touch事件呢?我只能很肯定的对你说不一定。呵呵,为什么呢?看看下面我的调查结果你
就明白了。
android系统中的每个View的子类都具有下面三个和TouchEvent处理密切相关的方法:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent
当TouchEvent发生时,首先Activity将TouchEvent传递给最顶层的View,
TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,
如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,
如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,
如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,
如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。
如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。
而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”,而且接收不到下一次事件。
先看一下ViewGroup的dispatchTouchEvent的关键源码:
public boolean dispatchTouchEvent(MotionEvent ev) {
...
if (action == MotionEvent.ACTION_DOWN) {
...
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
final View[] children = mChildren;
final int count = mChildrenCount;
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
...
if (child.dispatchTouchEvent(ev)) {
// Event handled, we have a target now.
mMotionTarget = child;
return true;
}
}
}
}
}
...
final View target = mMotionTarget;
if (target == null) {
...
return super.dispatchTouchEvent(ev);
}
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
...
if (!target.dispatchTouchEvent(ev)) {
// target didn't handle ACTION_CANCEL. not much we can do
// but they should have.
}
// clear the target
mMotionTarget = null;
// Don't dispatch this event to our own view, because we already
// saw it when intercepting; we just want to give the following
// event to the normal onTouchEvent().
return true;
}
...
return target.dispatchTouchEvent(ev);
}
根据以上代码可知,如果onInterceptTouchEvent方法返回true,会直接调用当前ViewGroup的onTouch方法,否则则会依次调用其内包含的子控件的dispatchTouchEvent方法。
总结一下,如果这一次事件没有人消耗掉,则系统不会给你下一次事件,因为他会认为你这次的事件阻塞了,没必要给下一次。onTouchEvent如果不消耗的话,会从子view传递到父view。
需求:要做一个完全通过flip手势来切换的界面。在最上层用一个ViewFlipper作为容器,并检测flip手势操作。
难题:ViewFlipper的flip手势检测需要的MotionEvent会被各种子View的触摸检测给拦截了。比如界面上有一个Button,则当手指按下Button(还没有抬起)然后flip出Button,则最上层的flip手势检测无效。
原因:android对Touch Event的分发逻辑是View从上层分发到下层(dispatchTouchEvent函数),然后下层优先开始处理Event(先mOnTouchListener,再onTouchEvent)并向上返回处理情况(boolean值),若返回true,则上层不再处理。
于是难题出现了,你若把Touch Event都想办法给传到上层了(只能通过返回false来传到上层),那么下层的各种子View就不能处理后续事件了。
解决方案:
开始仅着眼于Touch Event处理完后的回传过程,想了N久不得,毕竟我想实现的是一个需要打破android事件处理逻辑的效果(就是一个连续性操作,只有不满足上层要求时,才轮到下层处理)。然后突然想到事件的分发过程,便豁然开朗:
覆写最上层的View的dispatchTouchEvent函数,代码如下:
于是效果实现。也就是在分发之前便进行手势检测处理,若检测成功,则取消下层的一切处理过程。
总结一下就是:onInterceptTouchEvent可以接受到所有的Touch事件,而onTouchEvent则不一定。
声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息
原文作者: king.chen
原文地址: http://my.eoe.cn/521690/archive/4285.html