这几天以来对Android开发艺术探索中关于事件机制的总结有点不太明白,今天特地通过demo测试如下两个结论:
1. 某个View一旦决定拦截,那么这一个事件序列都只能由他来处理(如果事件序列能够传递给他的话),并且他的onInterceptTouchEvent不会再被调用;

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的话);



更多相关文章

  1. Java的单元测试(Junit)
  2. 根据给出的语料库,训练n-gram模型。根据训练出的模型,判断测试集中
  3. 【Android应用开发技术:应用组件】Android事件处理机制
  4. Android View事件传播机制
  5. osmdroid 6.0.1基础测试
  6. 无法向CalendarView事件添加侦听器
  7. Android自动化测试学习路线
  8. Android_单元测试_依赖注入_入门
  9. AutoCompleteTextView 设置了点击事件需要点击两下解决办法

随机推荐

  1. 数据库对象的创建和管理
  2. centos65安装简测mysql cluster 7.3.7
  3. [z]如何在一台windows主机上搭建mysql主
  4. MySQL免安装配置(亲测过,请放心借鉴)
  5. SQL Server 2005 所允许的 SQL 语句的最
  6. JPA hibernate spring repository pgsql
  7. 【原】Mysql最大连接数
  8. MySQL5.5.22版本安装配置以及基本命令的
  9. 请问用PL/SQL如何判断两个日期之间相差的
  10. 最近用到的SQL语句