基本概念
事件类型
主要有如下三种:
1 2 3
| - MotionEvent.ACTION_DOWN - MotionEvent.ACTION_MOVE - MotionEvent.ACTION_UP
|
事件处理 API
1 2 3 4 5 6
| - Activity.dispatchTouchEvent() - Activity.onTouchEvent() - ViewGroup.dispatchTouchEvent() - ViewGroup.onInterceptTouchEvent() - View.dispatchTouchEvent() - View.onTouchEvent()
|
主要是描述 Touch 事件的分发,拦载,响应三者的关系。 Touch 事件发生时,会由根元素逐级分发直到最内层 View,然后反过来逐级响应该事件。
名词解释
分发:该方法封装了事件分发的整个过程,是事件分发的调度者和指挥官。
拦载:该方法表示是否拦截事件,只有 ViewGroup 有该回调。
返回 true 表示拦截,返回 false 或者 super.onInterceptTouchEvent 表示不拦截
响应:该方法表示响应并处理事件
消费:表示事件不再继续逐级向下传递分发,或者不再继续逐级向上响应。也就是事件在某个方法中被消费后,该方法就是整个分发、拦载、响应流程的终点。
处理顺序
分发顺序为逐级向下:
dispatchTouchEvent: Activity(Window) -> ViewGroup -> View
响应顺序为逐级向上:
onTouchEvent: View -> ViewGroup --> Activity
事件分发流程
DOWN 事件 U 型流程图
流程图是针对 ACTION_DOWN 事件的分析,而 ACTION_MOVE/ACTION_UP 和事件的消费有关,放到后面分析。
事件的正常流程是:由上向下分发,并由下向上响应的一个 U 型图。箭头上的值表示返回值,其中 super 表示返回父类实现。

流程图总结
- 所有
API 返回值都为 super ,表示事件能走完整个 U 型流程。
- 分发和响应只要返回值为
true 都表示消费该事件,流程不再继续流转。
Activity 中的分发和响应
分发:只有返回 super 才能逐级向下分发,true/false 都表示事件最终被消费。
响应:不管返回什么值,都表示事件被最终消费。
ViewGroup 和 View 的分发和响应返回 false ,都表示流转到上一级执行 onTouchEvent (响应)。
- 拦载
返回 true 表示拦载,事件不会继续分发, 当前 ViewGroup 直接响应事件, U 型流程继续向前走。
返回 false 或者 super 表示不拦载,事件继续向下分发。
MOVE 和 UP 事件流程
ACTION_MOVE/ACTION_UP 和 ACTION_DOWN 事件的消费有关。在 U 型图中,ACTION_DOWN 事件在哪一层被消费,ACTION_MOVE/ACTION_UP 就只能到达这一层。
- 分发
如果 DOWN 事件被分发消费,UP 事件只能逐级向下分发到这一层。
- 响应
如果 DOWN 事件被响应后消费,UP 事件逐级向下分发到这一层,并在这一层响应后直接被消费。
示例 1
View 分发时消费 DOWN 事件。
View 在 dispatchTouchEvent 中消费 DOWN 事件, MVOE/UP 也只能分发到 CustomView 这一层。

示例 2
ViewGroup 响应时消费 DOWN 事件。
ViewGroup 在 onTouchEvent 中消费 DOWN 事件, MOVE/UP 只能分发到这一层,并在这一层响应后直接消费。

示例 Log 分析
示例结构图
CustomView :为自定义 View
CustomLayout :为自定义 ViewGroup
CustomLayout2 :继承 LinearLayout

示例完整布局文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.***.view.ViewEvent">
<com.***.view.CustomLayout android:id="@+id/view_event_custom_layout" android:layout_width="300dp" android:layout_height="300dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="8dp" app:custom_orientation="vertical">
<com.***.view.CustomLayout2 android:id="@+id/view_event_custom_layout_2" android:layout_width="wrap_content" android:layout_height="wrap_content" app:custom_orientation="horizontal"> <com.***.view.CustomView android:id="@+id/view_event_custom_view_button" android:layout_width="wrap_content" android:layout_height="wrap_content" app:custom_text="@string/view_event_custom_view_button" app:custom_color="@color/colorAccent" app:custom_size="@dimen/smallTextSize" android:layout_marginLeft="@dimen/custom_margin_view"/>
<TextView android:id="@+id/view_event_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/view_event_textview" android:layout_marginLeft="@dimen/custom_margin_text"/> </com.***.view.CustomLayout2>
<TextView android:id="@+id/view_event_textview_2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/view_event_textview" app:custom_margin="@dimen/custom_margin_text"/>
</com.***.view.CustomLayout>
</android.support.constraint.ConstraintLayout>
|
正常流程,不做任何处理
所有 API 都返回的是 super.***,走完整个 U 型图的流程。如下示例为点击 CustomView 后,触发了事件逐级向下分发、拦载、逐级向上响应的完整流程,包含 ACTION_DOWN 和 ACTION_UP 事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| // 处理 ACTION_DOWN 事件,Activity 开始分发事件 19:26: 26395/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} // 第一层 ViewGroup 接收事件并向下分发 19:26: 26395/com.yD/:CustomLayout:: dispatchTouchEvent: // 第一层 ViewGroup 判断是否拦载 19:26: 26395/com.yD/:CustomLayout:: onInterceptTouchEvent: // 第二层 ViewGroup 接收事件并向下分发 19:26: 26395/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: // 第二层 ViewGroup 判断是否拦载 19:26: 26395/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: // 第三层 View 接收事件并分发 19:26: 26395/com.yD/:CustomView:: dispatchTouchEvent: // 第三层 View 响应事件 19:26: 26395/com.yD/:CustomView:: onTouchEvent: // 第二层 ViewGroup 向上响应事件 19:26: 26395/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: // 第一层 ViewGroup 向上响应事件 19:26: 26395/com.yD/:CustomLayout:: onTouchEvent: // Activity 开始响应事件并消费 19:26: 26395/com.yD/:ViewEventActivity:: onTouchEvent: // 点击结束抬手,ACTION_UP,Activity 开始分发事件 19:26: 26395/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} // 因为是 Activity 消费了 DOWN 事件,所以 UP 事件只到这一层响应并消费 19:26: 26395/com.yD/:ViewEventActivity:: onTouchEvent:
|
dispatchTouchEvent 的返回值
- 当层返回
super.dispatchTouchEvent
不影响事件分发和响应,和正常流程完全一样
- 当层返回
false
分发:表示事件不再逐级向下传递分发。
响应:同时该层也不会响应事件,该层(不含)逐级向上所有层都响应事件。
如下示例为在 CustomLayout2 层直接返回 false:
1 2 3 4 5 6 7 8 9 10 11 12
| // 处理 ACTION_DOWN 事件 19:40: 6728/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 19:40: 6728/com.yD/:CustomLayout:: dispatchTouchEvent: 19:40: 6728/com.yD/:CustomLayout:: onInterceptTouchEvent: // CustomLayout2 层直接返回 false ,事件不再向下分发传递,同时本层也不响应事件 19:40: 6728/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: // 该层(不含)逐级向上所有层都响应事件 19:40: 6728/com.yD/:CustomLayout:: onTouchEvent: 19:40: 6728/com.yD/:ViewEventActivity:: onTouchEvent: // 处理 ACTION_UP 事件,Activity 消费了 DOWN 事件,所以 UP 事件只到这一层响应并消费 19:40: 6728/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 19:40: 6728/com.yD/:ViewEventActivity:: onTouchEvent:
|
分发:事件不会继续在整个流程中传递分发
响应:所有层都不响应该事件(因为分发流程未走完,所以响应流程无法开始)
如下示例为在最底层的 CustomView 中直接返回 true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // 处理 ACTION_DOWN 事件 19:46: 11801/com.yD/:XMT:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 19:46: 11801/com.yD/:XMT:CustomLayout:: dispatchTouchEvent: 19:46: 11801/com.yD/:XMT:CustomLayout:: onInterceptTouchEvent: 19:46: 11801/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 19:46: 11801/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: // CustomView 层直接返回 true,事件不再继续分发,同时所有层都不会响应事件 19:46: 11801/com.yD/:XMT:CustomView:: dispatchTouchEvent: // 处理 ACTION_UP 事件,CustomView 的dispatchTouchEvent 消费了 DOWN 事件 19:46: 11801/com.yD/:XMT:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 19:46: 11801/com.yD/:XMT:CustomLayout:: dispatchTouchEvent: 19:46: 11801/com.yD/:XMT:CustomLayout:: onInterceptTouchEvent: 19:46: 11801/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 19:46: 11801/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: // ACTION_UP 传递分发到这一层的 dispatchTouchEvent 并消费 19:46: 11801/com.yD/:XMT:CustomView:: dispatchTouchEvent:
|
onInterceptTouchEvent 的返回值
无法消费事件。
分发:事件不再逐级向下传递。
响应:该层(含)逐级向上所有层都响应事件。
如下示例为在 CustomLayout2 中直接返回 true
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // 处理 ACTION_DOWN 事件 19:57: 22163/com.yD/:XMT:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 19:57: 22163/com.yD/:XMT:CustomLayout:: dispatchTouchEvent: 19:57: 22163/com.yD/:XMT:CustomLayout:: onInterceptTouchEvent: 19:57: 22163/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: // CustomLayout2 层直接返回true,拦载该事件,事件不再继续向下分发 19:57: 22163/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: // 从 CustomLayout2 层(含)开始逐级向上响应事件 19:57: 22163/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: 19:57: 22163/com.yD/:XMT:CustomLayout:: onTouchEvent: 19:57: 22163/com.yD/:XMT:ViewEventActivity:: onTouchEvent: // 处理 ACTION_UP 事件,Activity 消费了 DOWN 事件,所以 UP 事件只到这一层响应并消费 19:57: 22163/com.yD/:XMT:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 19:57: 22163/com.yD/:XMT:ViewEventActivity:: onTouchEvent:
|
onTouchEvent 返回值
分发:返回值不影响事件传递分发。
响应:决定该层(不含)逐级向上所有层是否响应事件。
当层返回 false 或者 super.onTouchEvent
表示不影响事件分发和响应,和正常流程完全一样。如果直接返回 false 仅仅是比 super.onTouchEvent 少调用一次父类的 onTouchEvent。
当层返回 true
表示该层消费了事件,即该层(不含)逐级向上所有层都不响应事件了。
该层(含)及之下层已经响应了事件。
如下示例为 CustomLayout2 中返回 true:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // 处理 ACTION_DOWN 事件 20:01: 25724/com.yD/:XMT:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 20:01: 25724/com.yD/:XMT:CustomLayout:: dispatchTouchEvent: 20:01: 25724/com.yD/:XMT:CustomLayout:: onInterceptTouchEvent: 20:01: 25724/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 20:01: 25724/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 20:01: 25724/com.yD/:XMT:CustomView:: dispatchTouchEvent: // 并不影响事件的分发,也不影响下层级的响应 20:01: 25724/com.yD/:XMT:CustomView:: onTouchEvent: // CustomLayout2 中返回 true,该层(不含)逐级向上将不再响应事件 20:01: 25724/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: // 处理 ACTION_UP 事件,CustomLayout2 的 onTouchEvent 响应并消费了事件 20:01: 25724/com.yD/:XMT:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 20:01: 25724/com.yD/:XMT:CustomLayout:: dispatchTouchEvent: 20:01: 25724/com.yD/:XMT:CustomLayout:: onInterceptTouchEvent: 20:01: 25724/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: // ACTION_UP 传递分发到这一层,onTouchEvent 响应并消费 20:01: 25724/com.yD/:XMT:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent:
|
CustomLayout2 继承 CustomLayout
如果 CustomLayout2 继承 CustomLayout ,则在调用 supder.*** 会多执行一次 CustomLayout 相关方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| // 事件正常下发 10:01: 12487/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 10:01: 12487/com.yD/:CustomLayout:: dispatchTouchEvent: 10:01: 12487/com.yD/:CustomLayout:: onInterceptTouchEvent: 10:01: 12487/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: // 调用 super.*** 10:01: 12487/com.yD/:CustomLayout:: dispatchTouchEvent: 10:01: 12487/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: // 调用 super.*** 10:01: 12487/com.yD/:CustomLayout:: onInterceptTouchEvent: 10:01: 12487/com.yD/:CustomView:: dispatchTouchEvent: 10:01: 12487/com.yD/:CustomView:: onTouchEvent: 10:01: 12487/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: // 调用 super.*** 10:01: 12487/com.yD/:CustomLayout:: onTouchEvent: 10:01: 12487/com.yD/:CustomLayout:: onTouchEvent: 10:01: 12487/com.yD/:ViewEventActivity:: onTouchEvent: 10:01: 12487/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 10:01: 12487/com.yD/:ViewEventActivity:: onTouchEvent:
|
CustomLayout 的 onTouchEvent 返回 true 时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| // 事件正常下发 10:10: 19035/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 10:10: 19035/com.yD/:CustomLayout:: dispatchTouchEvent: 10:10: 19035/com.yD/:CustomLayout:: onInterceptTouchEvent: 10:10: 19035/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: // 调用 super.*** 10:10: 19035/com.yD/:CustomLayout:: dispatchTouchEvent: 10:10: 19035/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: // 调用 super.*** 10:10: 19035/com.yD/:CustomLayout:: onInterceptTouchEvent: 10:10: 19035/com.yD/:CustomView:: dispatchTouchEvent: 10:10: 19035/com.yD/:CustomView:: onTouchEvent: 10:10: 19035/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: // 调用 super.***,但是 CustomLayout 在 onTouchEvent 响应完后消费了该事件 10:10: 19035/com.yD/:CustomLayout:: onTouchEvent: // 所以不再再次调用 CustomLayout:: onTouchEvent: // UP 事件正常下发 10:10: 19035/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 10:10: 19035/com.yD/:CustomLayout:: dispatchTouchEvent: 10:10: 19035/com.yD/:CustomLayout:: onInterceptTouchEvent: 10:10: 19035/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 10:10: 19035/com.yD/:CustomLayout:: dispatchTouchEvent: 10:10: 19035/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: // 同理调用 super.*** 时 CustomLayout 在 onTouchEvent 响应完后消费了该事件 10:10: 19035/com.yD/:CustomLayout:: onTouchEvent:
|
事件分发及响应的部分源码分析
1 2 3 4 5 6 7 8 9
| public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
|
大体的分发过程为:首先传递到 Activity,然后传给了 Activity 依附的 Window,接着由 Window 传给视图的顶层 View 也就是 DecorView,最后由 DecorView 向整个 ViewTree 分发。
在 getWindow().superDispathTouchEvent 就是用来分发事件到 DecorView 中。如果整个 ViewTree 分发没有消费事件,会调用 Activity 的 onTouchEvent。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public boolean dispatchTouchEvent(MotionEvent e) { boolean consumed = false; if (onInterceptTouchEvent(e)) { consumed = onTouchEvent(e); } else { for (View view: children) { consumed = view.dispatchTouchEvent(e); if (consumed) { break; } } if (!consumed) { consumed = onTouchEvent(e); } } return consumed; }
|
常见监听事件的消费
onTouch 监听
1 2 3 4 5 6
| view.setOnTouchListener(new View.OnTouchListener(){ @Override public boolean onTouch(View v, MotionEvent event) { ... } }
|
onTouch 返回值
返回 false 表示不影响整个事件分发响应流程。
返回 true 表示 dispatchTouchEvent 分发消费这次事件。
对应的 Log 打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| // CustomView 监听 onTouch 事件后的分发流程 // onTouch 返回 false 走完整个 U 型流程 17:07: 29706/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 17:07: 29706/com.yD/:CustomLayout:: dispatchTouchEvent: 17:07: 29706/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:07: 29706/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:07: 29706/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:07: 29706/com.yD/:CustomView:: dispatchTouchEvent: 17:07: 29706/com.yD/:ViewEventActivity:: customView, onTouch: 17:07: 29706/com.yD/:CustomView:: onTouchEvent: 17:07: 29706/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onTouchEvent: 17:07: 29706/com.yD/:CustomLayout:: onTouchEvent: 17:07: 29706/com.yD/:ViewEventActivity:: onTouchEvent: 17:07: 29706/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 17:07: 29706/com.yD/:ViewEventActivity:: onTouchEvent:
// CustomView 监听 onTouch 事件后的分发流程 // onTouch 返回 true // 查看源码 onTouch 是在 View 的 dispatchTouchEvent 中调用的 // 返回 true 表示 消费了该事件 17:09: 31152/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 17:09: 31152/com.yD/:CustomLayout:: dispatchTouchEvent: 17:09: 31152/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:09: 31152/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:09: 31152/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:09: 31152/com.yD/:CustomView:: dispatchTouchEvent: 17:09: 31152/com.yD/:ViewEventActivity:: customView, onTouch: 17:09: 31152/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 17:09: 31152/com.yD/:CustomLayout:: dispatchTouchEvent: 17:09: 31152/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:09: 31152/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:09: 31152/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:09: 31152/com.yD/:CustomView:: dispatchTouchEvent: 17:09: 31152/com.yD/:ViewEventActivity:: customView, onTouch:
|
- 源码分析
源码可以看出,onTouch 回调是在 dispatchTouchEvent 中调用的,所以返回 true 时,表示分发消费了事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public boolean dispatchTouchEvent(MotionEvent event) { ... ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; }
if (!result && onTouchEvent(event)) { result = true; } ... }
|
onLongClick 监听
1 2 3 4 5 6
| view.setOnLongClickListener(new View.OnLongClickListener(){ @Override public boolean onLongClick(View v) { ... } }
|
onLongClick 返回值
不管返回 true 还是 false,都表示在 onTouchEvent 响应中消费该事件,并且是在 ACTION_DOWN 中执行 onLongClick 回调。分析见下面 onTouchEvent 部分源码。对应的 Log 打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 17:14: 3237/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 17:14: 3237/com.yD/:CustomLayout:: dispatchTouchEvent: 17:14: 3237/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:14: 3237/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:14: 3237/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:14: 3237/com.yD/:CustomView:: dispatchTouchEvent: 17:14: 3237/com.yD/:CustomView:: onTouchEvent:
17:14: 3237/com.yD/:ViewEventActivity:: customView, onLongClick: 17:14: 3237/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 17:14: 3237/com.yD/:CustomLayout:: dispatchTouchEvent: 17:14: 3237/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:14: 3237/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:14: 3237/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:14: 3237/com.yD/:CustomView:: dispatchTouchEvent: 17:14: 3237/com.yD/:CustomView:: onTouchEvent:
|
onClick 监听
1 2 3 4 5 6
| view.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { ... } }
|
- 事件消费
在 onTouchEvent 响应中消费该事件,并且是在 ACTION_UP 中执行 onClick 回调。对应的 Log 打印:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // CustomView 监听 onClick 事件后的分发流程 // 相当于在 onTouchEvent 中消费了该事件 17:03: 26616/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_DOWN...} 17:03: 26616/com.yD/:CustomLayout:: dispatchTouchEvent: 17:03: 26616/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:03: 26616/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:03: 26616/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:03: 26616/com.yD/:CustomView:: dispatchTouchEvent: 17:03: 26616/com.yD/:CustomView:: onTouchEvent: 17:03: 26616/com.yD/:ViewEventActivity:: dispatchTouchEvent: ev MotionEvent { action=ACTION_UP...} 17:03: 26616/com.yD/:CustomLayout:: dispatchTouchEvent: 17:03: 26616/com.yD/:CustomLayout:: onInterceptTouchEvent: 17:03: 26616/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, dispatchTouchEvent: 17:03: 26616/com.yD/:CustomLayout2:: :XMT:CustomLayout2:, onInterceptTouchEvent: 17:03: 26616/com.yD/:CustomView:: dispatchTouchEvent: 17:03: 26616/com.yD/:CustomView:: onTouchEvent: // ACTION_UP 中响应 onClick 事件 17:03: 26616/com.yD/:ViewEventActivity:: customView, onClick:
|
- 源码分析
参考如下源码,onLongClick 和 onClick 事件都是在 onTouchEvent 响应中回调了监听,其中:
onLongClick 是在 ACTION_DOWN 中处理监听回调
onClick 是在 ACTION_UP 中处理监听回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public boolean onTouchEvent(MotionEvent event) { ... final int action = event.getAction(); ... if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) { switch (action) { case MotionEvent.ACTION_UP: ... if (mPerformClick == null) { mPerformClick = new PerformClick(); } if (!post(mPerformClick)) { performClick(); } ... case MotionEvent.ACTION_DOWN: ... setPressed(true, x, y); checkForLongClick(0, x, y); ... } return true; } ... }
|
ViewGroup 中拒绝拦载事件
可以通过设置 FLAG_DISALLOW_INTERCEPT 来要求 ViewGroup 拒绝拦载事件,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| @Override public void requestDisallowInterceptTouchEvent( boolean disallowIntercept) {
if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) { return; }
if (disallowIntercept) { mGroupFlags |= FLAG_DISALLOW_INTERCEPT; } else { mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; }
if (mParent != null) { mParent.requestDisallowInterceptTouchEvent(disallowIntercept); } }
|
通过 requestDisallowInterceptTouchEvent 设置好 FLAG 后,在 ViewGroup 的事件分发机制中可以看到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Override public boolean dispatchTouchEvent(MotionEvent ev) { ... boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { ... final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); } else { intercepted = false; } } else { intercepted = true; } ... ... }
|
参考链接
- http://www.jianshu.com/p/e99b5e8bd67b
- http://www.jianshu.com/p/7daf0feb6c2d
- http://blog.csdn.net/guolin_blog/article/details/9097463/
- http://blog.csdn.net/guolin_blog/article/details/9153747/
- http://www.cnblogs.com/duoduohuakai/p/3996385.html
- http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html
- http://blog.csdn.net/carson_ho/article/details/54136311