再论Android中的OnTouch事件和MotionEvent

昨天写程序的时候,碰到一个很棘手的问题,牵扯到OnTouch事件和MotionEvent事件,网上虽然有一些说明,但是对于我想要知道的不是很完整,比较零散,这里就总结一下android中的Ontouch机制。


要理解OnTouch机制,首先得明白有哪些OnTouch事件,常用的有这么四种:


ACTION_DOWN  

这个是OnTouchEvent事件的开始,任何事件都必须手指按下去才行。这个事件是一个从触摸屏无触摸状态到有触摸状态的转换。


ACTION_MOVE  

紧接着的Move事件,可能有人会以为手指移动就会调用这个MOVE,但是经我测试并非如此,这个事件,只要手指在屏幕上,即使不动也会调用。


ACTION_UP  

触摸事件的结束,正常情况下,手指离开屏幕;触摸屏从有触摸状态到无触摸状态的转换。


ACTION_CANCEL  

这个不是独特的触摸事件,而是由系统来判定的,一般认为你的手指从屏幕上你要点击的区域移出来,就cancel掉了。很简单的例子,你点击一个按钮,点击的时候,按钮变颜色,只要你的手指不拿开,按钮不会变回原来的颜色。这时候你手指拿开了,按钮颜色变回去,你就触发了ACTION_UP的事件;或者你的手指移出按钮区域了,按钮颜色也变回去,这时候你触发了ACTION_CANCEL事件,你点击按钮的方法也不会被触发。


我们知道,android中的View是按照一个View Hierarchy来组织的。而且,在设定onTouch事件的时候,触摸的顺序也是走的这个View Hierarchy从上往下传递,叫做dispatch。


ViewGroup里面有一个方法决定了这个ViewGroup的dispatch方式:


OnInterceptTouchEvent(MotionEvent ev)


这个方法由系统自己调用,但是在写自己的ViewGroup的时候,可以复写,工作过程和返回值说明如下:


Implement this method to intercept all touch screen motion events. This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.

Using this function takes some care, as it has a fairly complicated interaction with View.onTouchEvent(MotionEvent), and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:

  1. You will receive the down event here.

  2. The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle; this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it). Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.

  3. For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target‘s onTouchEvent().

  4. If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.


Parameters
evThe motion event being dispatched down the hierarchy.
Returns
  • Return true to steal motion events from the children and have them dispatched to this ViewGroup through onTouchEvent(). The current target will receive an ACTION_CANCEL event, and no further messages will be delivered here.



简单来说,任何触摸事件最初都是从这里开始,也就是MotionEvent.ACTION_DOWN。如果这个方法返回true了,那么这个onTouchEvent就不会被分配到子View了,而是在这个ViewGroup的OnTouchEvent事件里面处理,而子View则会收到一个ACTION_CANCEL,从而结束其onTouchEvent方法的调用。

所以可以简单记住,如果这个方法返回true,那么就是说不要继续dispatch事件了,所有后续的OnTouchEvent都留在这个ViewGroup里面处理,子View获得一个ACTION_CANCEL事件后就结束了。

如果这个方法返回false,那么就是说ViewGroup不截断onTouch事件,分发到相应的子View里面处理。



子View处理onTouch事件的时候,需要有一个boolean型的返回值;这个返回值的实际意义其实是,该触摸事件是否在这个onTouch方法里面被消耗掉了。如果返回true,那么就是被消耗掉了,该touch事件不会被继续处理。如果返回false,就是说没消耗掉,touch事件继续被处理。


被处理?被谁处理?被这个子View的parent处理。就是说,Parent View不是把OnTouch事件分发到子View就没事儿了,还要看看子View有没有对这个事件处理,如果子View没处理,他还得自己处理。所以综上而言,一个MotionEvent首先是从ParentView到子View,然后还有可能再从子View返回到ParentView那边去处理,是一个来回的过程。

是不是要从ParentView分发到子View,就看ParentView中OnInterceptTouchEvent事件的返回值;是不是要从子View返回到ParentView,就看子View的OnTouch事件返回值。


注意:一定不要把子View的onTouch事件返回值理解成是否要处理onTouch的后续事件。每一个MotionEvent都是独立处理的,不会影响到其后续事件。


举例子来说:ListView里面有很多Item

这时候,如果我们点击一个Item,那么这个点击事件首先被ListView接收,onInterceptTouchEvent返回false,分发到子View.然后子View来处理这个Touch事件。

这个时候,如果你的手指上下移动了(而不是左右),那么这个手势的含义就不是点击而是你要滑动ListView了,所以这个时候,ListView的onInterceptTouchEvent就会返回true,你点击的那个Item就收到了一个ACTION_CANCEL事件,以后你手指无论怎么移动,都和Item没关系,放到了ListView的OnTouch事件里面处理了。所以这个时候你在子View里面就不会监听到ACTION_UP事件,要想找到这个事件,你得到ListView的OnTouch事件里面去找了。



再论Android中的OnTouch事件和MotionEvent,,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。