Android中view的Touch事件

来源:互联网 发布:淘宝2.5亿火炬红包 编辑:程序博客网 时间:2024/04/30 04:54

当屏幕中包含一个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方法。


0_1302589875305B.gif
总结一下,如果这一次事件没有人消耗掉,则系统不会给你下一次事件,因为他会认为你这次的事件阻塞了,没必要给下一次。onTouchEvent如果不消耗的话,会从子view传递到父view。


一个例子:

原文作者: king.chen

原文地址: http://my.eoe.cn/521690/archive/4285.html

需求:要做一个完全通过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函数,代码如下:

1234567
  @Override  public boolean dispatchTouchEvent(MotionEvent event) {         if (_flipDetector.onTouchEvent(event)) {                 event.setAction(MotionEvent.ACTION_CANCEL);         }         return super.dispatchTouchEvent(event);  }

于是效果实现。也就是在分发之前便进行手势检测处理,若检测成功,则取消下层的一切处理过程。

总结一下就是:onInterceptTouchEvent可以接受到所有的Touch事件,而onTouchEvent则不一定。

声明:eoe文章著作权属于作者,受法律保护,转载时请务必以超链接形式附带如下信息

原文作者: king.chen

原文地址: http://my.eoe.cn/521690/archive/4285.html