触摸事件传递机制
本文最后更新于:1 年前
触摸事件传递的三个阶段
分发
分发对应dispatchTouchEvent()方法。
如果当前视图是ViewGroup及其子类,则会调用onInterceptTouchEvent()方法判断是否拦截该事件。
该方法返回true或false表示该事件被当前视图分发过,不继续分发该事件给子视图,其中true表示该事件被当前视图消费掉,false表示该事件未被当前视图消费掉;返回super.dispatchTouchEvent()表示继续分发该事件给子视图。
该事件将会按照嵌套层次从外向内传递,到达最内层的视图时,由该视图的onTouchEvent()方法处理,如果该方法能消费该事件,则返回true,如果消费不了,返回false,这时事件开始按照嵌套层次从内向外传递,并由外层的视图的 onTouchEvent()方法进行尝试消费,其中,返回true为消费完毕,返回false为未消费,可继续向外层传递。
拦截
拦截对应onInterceptTouchEvent()方法。
该方法只在ViewGroup及其子类中存在,在Activity和View中不存在。
该方法返回true表示拦截该事件,不继续分发给子视图,同时交给自身的onTouchEvent()方法进行尝试消费,其中,返回true为消费完毕,返回false为未消费,可继续向外层传递;返回false或super.onInterceptTouchEvent表示不拦截该事件,继续分发给子视图。
消费
消费对应onTouchEvent()方法。
该方法返回true表示处理该事件,不传递该事件给父视图;返回false表示当前视图不处理该事件,传递该事件给父视图的onTouchEvent()方法尝试消费。
其他说明
- 如果同时有
Activity、ViewGroup、View时,嵌套层次由外向内是Activity、ViewGroup、View。 View控件的事件触发顺序是先执行onTouch()方法,再执行onClick()方法。如果onTouch()方法中返回true,最后不会调用onClick()方法。- 事件的类型有三种:
ACTION_DOWN:手指按下操作,表示触摸事件的开始。ACTION_MOVE:手指移动距离超过阈值会被计为一次ACTION_MOVE,所以一次完整的触摸事件可能包含多个ACTION_MOVE,也可能一个都没有。ACTION_UP:手指离开屏幕,表示触摸事件的结束。
- 如果一层级的最外层控件的
dispatchTouchEvent()方法返回false,该层级会不响应触摸事件并且事件也不会被消费,会继续让该层级后面的下一层级处理,相当于点击“穿透”了。
验证触摸事件传递过程
默认的传递过程
代码
1.Test_Touch\app\src\main\java\io\weichao\test_touch\MainActivity.java
1 | |
2.Test_Touch\app\src\main\res\layout\activity_main.xml
1 | |
3.Test_Touch\app\src\main\java\io\weichao\test_touch\CustomRelativeLayout.java
1 | |
4.Test_Touch\app\src\main\java\io\weichao\test_touch\CustomButton.java
1 | |
显示的界面

点击下面的紫色区域,显示 log

按下会触发1次,弹起会触发1次,每判定出一次移动会触发1次。
点击红色区域,显示 log

中间部分每判定出一次移动会触发1次,这里触发了3次。
点击灰色区域,显示 log

中间部分每判定出一次移动会触发1次,这里触发了3次。
只修改 Activity
只修改 dispatchTouchEvent()方法,返回 true 或 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
事件被MainActivity分发过,不管是否被消费,都不会再分发,所以不执行任何onTouchEvent()方法。
只修改 onTouchEvent()方法,返回 true 或 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
无变化,但是返回true时最后不会调用MainAcitivity的onClick()方法。
只修改 CustomRelativeLayout
只修改 dispatchTouchEvent()方法,返回 true
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
事件被CustomRelativeLayout分发过,且被消费了,所以不执行任何onTouchEvent()方法。
只修改 dispatchTouchEvent()方法,返回 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
事件被CustomRelativeLayout分发过,但未被消费过,所以该事件被MainActivity消费掉,执行MainActivity的onTouchEvent()方法。
只修改 onInterceptTouchEvent()方法,返回 true
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
CustomRelativeLayout拦截向下传递,所以执行CustomRelativeLayout和MainActivity的onTouchEvent()方法。
只修改 onInterceptTouchEvent()方法,返回 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
CustomRelativeLayout不拦截向下传递,所以当CustomButton有触摸事件时,会消费掉该事件,所以执行CustomButton的onTouchEvent()方法;否则,CustomRelativeLayout不会消费掉该事件,所以执行CustomRelativeLayout和MainActivity的onTouchEvent()方法。
只修改 onTouchEvent()方法,返回 true
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
CustomRelativeLayout不拦截向下传递,所以当CustomButton有触摸事件时,会消费掉该事件,所以执行CustomButton的onTouchEvent()方法;否则,CustomRelativeLayout会消费掉该事件,所以执行CustomRelativeLayout的onTouchEvent()方法。
只修改 onTouchEvent()方法,返回 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
和【只修改 onInterceptTouchEvent()方法,返回 false】一样。
只修改 CustomButton
只修改 dispatchTouchEvent()方法,返回 true
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
CustomRelativeLayout不拦截向下传递,所以当CustomButton有触摸事件时,会试图消费掉该事件,但因为CustomButton已分发过,所以不执行CustomButton的onTouchEvent()方法,同时该事件也被消费了,所以不会再被CustomRelativeLayout或MainActivity消费;否则,默认CustomRelativeLayout也不会消费掉该事件,所以执行CustomRelativeLayout和MainActivity的onTouchEvent()方法。
只修改 dispatchTouchEvent()方法,返回 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
CustomRelativeLayout不拦截向下传递,所以当CustomButton有触摸事件时,会试图消费掉该事件,但因为CustomButton已分发过,所以不执行CustomButton的onTouchEvent()方法,但同时未消费过,但默认CustomRelativeLayout也不会消费掉该事件,所以执行CustomRelativeLayout和MainActivity的onTouchEvent()方法;否则,默认CustomRelativeLayout也不会消费掉该事件,所以执行CustomRelativeLayout和MainActivity的onTouchEvent()方法。
只修改 onTouchEvent()方法,返回 true
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
CustomRelativeLayout不拦截向下传递,所以当CustomButton有触摸事件时,会消费掉该事件;否则,默认CustomRelativeLayout也不会消费掉该事件,所以执行CustomRelativeLayout和MainActivity的onTouchEvent()方法。
只修改 onTouchEvent()方法,返回 false
点击下面的紫色区域,显示 log

点击红色区域,显示 log

点击灰色区域,显示 log

与默认的传递过程对比
完全一样。