所谓Android的触摸事件其实就是Android中的MotionEvent对象,即当一个MotionEvent产生之后,系统需要吧这个事件传递给一个具体的View,而这个传递的过程就是分发的过程。
事件的分发过程由系统提供的三个方法来共同完成:
dispatchTouchEvent(ev)
onInterceptTouchEvent(ev)
onTouchEvent(event)。
public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发,如果事件能够传递给当前的View,那么该方法一定会被调用,返回结果收到当前的View的ToychEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗掉当前的事件
public boolean onInterceptTouchEvent(MotionEvent ev)
在dispatchTouchEvent(ev)方法内部调用,表示是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列内,此方法不会被再次调用,返回结果表示是否拦截当前事件。(只有在ViewGroup中才有此方法,ViewGroup中此方法默认返回为false,但是在在一些有事件处理的View之中(比如:ScrollView)此方法默认返回为true)
public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent(ev)方法内部调用,用来处理当前事件,返回结果表示是否消耗掉当前的事件(与onInterceptTouchEvent(ev)方法不同的是,onInterceptTouchEvent(ev)方法返回true的时候,他的子View不会调用dispatchTouchEvent(ev)方法,因为该事件被当前的ViewGroup拦截了。而onTouchEvent(event)返回为true的时候,表示当前的事件被消费掉,如果是在ViewGroup中,表示他的子View调用了dispatchTouchEvent(ev),但没有消费掉当前的事件。),如果不消费掉该事件,那么在同一个事件序列中当前的View无法再次接收到事件。如果给该View设置了onTouchListener,那么onTouchListener中的onTouch方法会被回调,如果返回为false,该View的onTouchEvent方法被调用,返回为true则不会调用onTouchEvent方法
借用任总的代码来表示Android事件传递的过程。
public boolean dispatchTouchEvent(MotionEvent event){
boolean consume = false;
//在dispatchTouchEvent(event)方法内部调用onInterceptTouchEvent(event)方法
// ,如果返回为true表示当前的View拦截了该事件
//.返回为false则把事件传递给它的子类,调用他的子类的dispatchTouchEvent(event)方法
//循环往复直到该事件被View消费掉或者该事件回调到Activity之中被Activity消费掉
if (onInterceptTouchEvent(event)){
consume = onTouchEvent(event);
}else{
consume = child.dispatchTouchEvent(event);
}
return consume;
}
下面通过代码来演示事件的传递过程。
首先我们创建一个Activity,一个TestViewGroup,一个TestView,分别重写他们的dispatchTouchEvent(ev),onInterceptTouchEvent(ev),onTouchEvent(event)方法,注意在Activity和TestView中是没有onInterceptTouchEvent(ev)方法的。结构如下
当我们点击TestView的时候打印结果如下:
﹕ Activity + dispatchTouchEvent
﹕ ViewGroup + dispatchTouchEvent
﹕ ViewGroup + onInterceptTouchEvent
﹕ View + dispatchTouchEvent
﹕ View + onTouchEvent
﹕ ViewGroup + onTouchEvent
﹕ Activity + onTouchEvent
通过打印结果我们可以清楚的看到事件的传递过程:activity –> viewGroup –> view (view没有处理当前事件) –> viewGroup(viewGroup没有处理当前事件) –> activity。
接下来我们修改一下代码,我们将TestView中的onTouchEvent方法返回为true。看一下打印的结果。
﹕ Activity + dispatchTouchEvent
﹕ ViewGroup + dispatchTouchEvent
﹕ ViewGroup + onInterceptTouchEvent
﹕ View + dispatchTouchEvent
﹕ View + onTouchEvent
因为当前的View消费了此事件,所以当前事件没有再像父容器传递。并且当前的这个事件只有一个View去处理了,并没有其他的View去处理该事件。所以我们可以说默认情况下只有一个View会消费掉用户的触摸事件。
接下来我们继续修改代码。在TestView中的onTouchEvent方法中增加如下代码
@Override
public boolean onTouchEvent(MotionEvent event) {
System.out.println("View + onTouchEvent");
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
System.out.println("View + Down");
return false;
case MotionEvent.ACTION_MOVE:
System.out.println("View + Move");
break;
}
return super.onTouchEvent(event);
}
即我们捕获掉他的Down事件,看一下打印结果。
﹕ Activity + dispatchTouchEvent
﹕ ViewGroup + dispatchTouchEvent
﹕ ViewGroup + onInterceptTouchEvent
﹕ View + dispatchTouchEvent
﹕ View + onTouchEvent
﹕ View + Down
﹕ ViewGroup + onTouchEvent
﹕ Activity + onTouchEvent
﹕ Activity + dispatchTouchEvent
﹕ Activity + onTouchEvent
﹕ Activity + dispatchTouchEvent
﹕ Activity + onTouchEvent
我先按下屏幕,然后移动手指,可以看到,我们在Down事件之中返回了fasle。那么同一个事件序列(事件序列是指当我们按下手指开始到离开手指之后中间发生的一系列事件)中的其他事件并没有再次交给View处理,而是直接在Activity中的进行了处理。也就是说事件一旦交给了一个View来处理,那么它就必须消耗掉该事件,否则同一事件序列中的剩下的事件就不会再交由它处理了。
其他的各种情况大家可以去自己实验,这里就不再多陈述了。
站在巨人的肩膀上,我们才能看的更远!