2. 某个View一旦开始执行处理事件,如果他不消费ACTION_DOWN事件(onTouchEvent返回false),那么同一事件序列中的其他事件都不再会交给他来处理,并且事件将重新交给他的父元素去处理;
测试用例代码下载!!!!
这里采用的测试布局文件是:
<com.hzw.eventtest.MyRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.hzw.eventtest.MyLinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <com.hzw.eventtest.MyButton android:id="@+id/mybutton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我的按钮" /> </com.hzw.eventtest.MyLinearLayout> </com.hzw.eventtest.MyRelativeLayout>
也即测试布局图是:
测试1:
对测试代码不做任何修改,Logcat输出:
图1
点击屏幕按钮会首先由MainActivity的dispatchTouchEvent进行分发,因为Activity没有拦截功能,接着由MyRelativeLayout的dispatchTouchEvent进行分发,同时调用它的onInterceptTouchEvent判断是否需要拦截,我们这里不拦截,则调用MyLinearLayout的dispatchTouchEvent进行分发,同时调用他的onInterceptTouchEvent判断是否需要拦截,这里也没进行拦截,那么就该MyButton进行分发了,因为他已经是View了,所以调用它的dispatchTouchEvent分发默认情况下onTouch方法返回false的话就执行onTouchEvent了,View的onTouchEvent默认返回值是true,因为我们上面所有方法的返回值都是super.的方式,所以事件到View的时候就被消费了;
测试2:
如果只修改MyLinearLayout$onInterceptTouchEvent如下:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("MyLinearLayout--->onInterceptTouchEvent--->ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: System.out.println("MyLinearLayout--->onInterceptTouchEvent--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("MyLinearLayout--->onInterceptTouchEvent--->ACTION_UP"); break; default: break; } return super.onInterceptTouchEvent(ev); }也就是说只在MyLinearLayout中拦截DOWN事件;
查看Logcat输出:
图2
可以看到事件分发到第5行时,因为MyLinearLayout对DOWN事件进行了拦截,因而会执行他的onTouchEvent方法,又因为我们没有在onTouchEvent中对DOWN事件进行消费,所以传给上级MyRelativeLayout的onTouchEvent来进行处理,上级也没有对DOWN事件进行消费,那么他仍然会传给上级MainActivity的onTouchEvent来处理了,这个事件在这里得到了消费,那么接下来的MOVE和UP事件根据结论2就知道,MyRelativeLayout和MyLinearLayout是没有资格进行处理的,因为他们都没消费DOWN事件,因而都是由MainActivity的onTouchEvent来处理了;
如果只修改MyLinearLayout$onTouchEvent会发生什么呢?
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("MyLinearLayout--->onTouchEvent--->ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: System.out.println("MyLinearLayout--->onTouchEvent--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("MyLinearLayout--->onTouchEvent--->ACTION_UP"); break; default: break; } return super.onTouchEvent(event); }只在MyLinearLayout中消费了他的DOWN事件;
查看Logcat输出:
图3
可以发现如果只消费事件而不拦截相对应事件的话,对整个分发过程是没什么影响的,因为最终事件是由最里层的View处理了,而且也没从View里面回传回来;
如果我们同时修改MyLinearLayout$onInterceptTouchEvent以及MyLinearLayout$onTouchEvent里面的DOWN事件,让他们都返回true的话会出现什么情况呢?
查看Logcat输出有:
图4
可以发现DOWN事件在传递到MyLinearLayout之后调用它的onTouchEvent事件并且将其消费,那么接下来的MOVE以及UP事件同样也要传递到MyLinearLayout,这点不同于图2,因为图2中只设置了MyLinearLayout的DOWN拦截事件,但是最终DOWN事件是由MainActivity来处理的,所以之后的MOVE以及UP事件就干脆不会向下分发了,因为即使分发下去他们也没有资格执行,索性直接由MainActivity来处理MOVE以及UP事件了,但是图3显然不一样,这里是MyLinearLayout消费的DOWN事件,那么接下来的MOVE以及UP事件显然也应该传给他让他判断自己是否要处理这两个事件,如果自己不处理的话,那么直接就由MainActivity来处理,注意这里直接是由MainActivity来处理的;
可能你对直接就由MainActivity来处理不太相信,我们可以在MyRelativeLayout的onTouchEvent中消费MOVE事件,假如你是正确的,那么事件在回传的过程中将会经过MyRelativeLayout的onTouchEvent方法,因为我们的布局是MainActivity--->MyRelativeLayout--->MyLinearLayout的,这时候如果你的观点是正确的话,那么会在MyRelativeLayout的onTouchEvent中消费了MOVE:
查看Logcat输出:
注意红框部分,你会发现仍然还是直接调用的MainActivity的onTouchEvent方法,这一点也就告诉我们是谁消费的DOWN事件,谁才有资格处理接下来的MOVE和UP事件,如果最后事件还是没有处理的话,那么只能由Activity来处理了;
测试3:
我们现在设置MyRelativeLayout来拦截MOVE事件,MyLinearLayout来拦截并且消费DOWN事件:
也就是我们需要修改MyRelativeLayout$onInterceptTouchEvent,以及MyLinearLayout$onInterceptTouchEvent以及MyLinearLayout$onTouchEvent;
MyRelativeLayout$onInterceptTouchEvent
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("MyRelativeLayout--->onInterceptTouchEvent--->ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("MyRelativeLayout--->onInterceptTouchEvent--->ACTION_MOVE"); return true; case MotionEvent.ACTION_UP: System.out.println("MyRelativeLayout--->onInterceptTouchEvent--->ACTION_UP"); break; default: break; } return super.onInterceptTouchEvent(ev); }MyLinearLayout$ onInterceptTouchEvent
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("MyLinearLayout--->onInterceptTouchEvent--->ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: System.out.println("MyLinearLayout--->onInterceptTouchEvent--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("MyLinearLayout--->onInterceptTouchEvent--->ACTION_UP"); break; default: break; } return super.onInterceptTouchEvent(ev); }MyLinearLayout$ onTouchEvent
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("MyLinearLayout--->onTouchEvent--->ACTION_DOWN"); return true; case MotionEvent.ACTION_MOVE: System.out.println("MyLinearLayout--->onTouchEvent--->ACTION_MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("MyLinearLayout--->onTouchEvent--->ACTION_UP"); break; default: break; } return super.onTouchEvent(event); }来查看Logcat输出:
注意红框部分。可以发现第一次虽然我们设置了MyRelativeLayout来消费掉MOVE事件,但是很明显第一次MOVE事件和这并没有关系,因为他是由MainActivity的onTouchEvent来进行处理的,但是接下来的第二次MOVE事件就会首先由MyRelativeLayout的onTouchEvent方法来查看是否要消费该MOVE事件了,不消费的话才会由MainActivity的onTouchEvent方法来执行,我们这里是不消费,那么他就会执行MainActivity的onTouchEvent方法了,同时后面的UP事件也不会向MyLinearLayout传递了,因为MyRelativeLayout已经决定拦截事件MOVE,那么MOVE之后的事件将都交给MyRelativeLayout来处理了(只要改事件能够到达MyRelativeLayout的话);