基本概念
事件类型
主要有如下三种:
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