Android事件冲突原理及解决方法

简介: Android事件冲突原理及解决方法

MotionEvent 事件类型

ACTION_DOWN: 表示手指按下屏幕
ACTION_MOVE: 手指在屏幕上滑动时,会产生一系列的MOVE事件
ACTION_UP: 手指抬起,离开屏幕、
ACTION_CANCEL:当出现异常情况事件序列被中断,会产生该类型事件
ACTION_POINTER_DOWN: 当已经有一个手指按下的情况下,另一个手指按下会产生该事件
ACTION_POINTER_UP: 多个手指同时按下的情况下,抬起其中一个手指会产生该事件

事件分发流程

事件分发主要分三块:分发、拦截、消费。

当我们触摸到屏幕的时候,默认会先走 Activity 的分发,接着走 ViewGroup 的分发,
  然后到 ViewGroup 的拦截,后面再到 View 的分发事件,最后会传到 View 的消费事件。
如果 View 不消费,紧接着回传到 ViewGroup 的消费事件;
  如果 ViewGroup 也不消费,最后回到 View 的消费事件。
  
// 分发的细节流程:
如果 ViewGroup 的 dispatchTouchEvent 返回 true 或 false ,touch 事件不会往子 view 中传递,
  false 的时候只会触发 action_down ,ViewGroup 的 onTouchEvent 事件也不会被触发。
  只有在返回 super.dispatchTouchEvent 时候 touch 事件才会传递到子 view。

如果 ViewGroup 的 onInterceptTouchEvent 返回 false 或者 super.onInterceptTouchEvent 时,
  touch 事件会传递到子 view。返回 true 事件不会向下传递,交给自己的 ontouchEvent 处理。

如果 view 的 dispatchTouchEvent 返回 true 或 false ,touch 事件不会传给自己的 ontouchEvent 事件,
  返回 false ,只会触发 action_down,move 和 up 不会触发;
  返回 true,才会触发 move 和 up。
  返回 super.dispatchTouchEvent,touch 事件才会交给自己的 onTouchEvent 处理。

如果 view 的 ontouchEvent 返回 false,只会有 action_down 事件,touch 事件交给上一层处理,
  如果返回 true 才会消费,事件不会向上传递,
  如果返回 super.ontouchEvent,得看 clickable 是不是返回 true。

onTouch 和 onClick 冲突

当我们 setOnTouchListener 且在 onTouch() 返回 true 表示事件被消费,
  setOnClickListener 的 onClick 不会执行

我们设置了 setOnClickListener/setOnTouchListener,mListenerInfo 就不会为空,
  如果 li.mOnTouchListener.onTouch(this, event) 为 true,则 result 为 true,
  !result 短路就不会执行 onTouchEvent(event) 方法,而这个方法在 ACTION_UP 中回调 onClick。

down 事件分析

事件都是以 DOWN 开始,UP 事件结束,中间多个 MOVE 事件,所以我们先从 DOWN 事件开始分析

DOWN 事件进来,会先重置 mGroupFlags 变量,disallowIntercept 标志位为 false,
  进入onInterceptTouchEvent 来判断是否拦截。
看下 requestDisallowInterceptTouchEvent,表示请求父类不要拦截事件

当 View 设置 requestDisallowInterceptTouchEvent 为 true 的时候,
  mGroupFlags = mGroupFlags|FLAG_DISALLOW_INTERCEPT,
  使得 disallowIntercept = mGroupFlags|FLAG_DISALLOW_INTERCEPT & FLAG_DISALLOW_INTERCEPT != 0,
  disallowIntercept 标志位为true,不会执行 onInterceptTouchEvent,父 View 也不会拦截子 View

但是当前事件是 DOWN 事件,requestDisallowInterceptTouchEvent 是无效的。会调用 resetTouchState 重置
  mGroupFlags 使得 disallowIntercept 为 false,进入 onInterceptTouchEvent 来判断是否拦截
- 未被拦截:intercepted 为 false,事件分发给子 View 处理
- 被拦截:intercepted 为 true,会跳过 if (!canceled && !intercepted)

首次事件处理,mFirstTouchTarget == null,调用 dispatchTransformedTouchEvent,传入的 child 为 null,
  调用 super.dispatchTouchEvent(transformedEvent) 进入 View 的 dispatchTouchEvent 方法。

完成了 DOWN 事件分发和处理后,再次分发 MOVE 事件,还是从 ViewGroup 的 dispatchTouchEvent 方法开始,
  而 MOVE 事件是可以反复调用此方法的,MOVE 事件不进行分发,直接找 DOWN 事件确定的对象
 事件结束,中间多个 MOVE 事件,所以我们先从 DOWN 事件开始分析

冲突解决方法

- 外部拦截法 (在 ViewGroup 中对事件进行拦截)
  重写 parentView 中的 onInterceptTouchEvent
  
- 内部拦截法(ViewGroup 不拦截,子 View 需要事件就消耗掉,否则交给父 View 处理)
  重写 parentView 的 onInterceptTouchEvent
  重写 childView 的 dispatchTouchEvent
目录
相关文章
|
3月前
|
安全 Android开发 Kotlin
Android经典实战之SurfaceView原理和实践
本文介绍了 `SurfaceView` 这一强大的 UI 组件,尤其适合高性能绘制任务,如视频播放和游戏。文章详细讲解了 `SurfaceView` 的原理、与 `Surface` 类的关系及其实现示例,并强调了使用时需注意的线程安全、生命周期管理和性能优化等问题。
169 8
|
1月前
|
缓存 Java 数据库
Android的ANR原理
【10月更文挑战第18天】了解 ANR 的原理对于开发高质量的 Android 应用至关重要。通过合理的设计和优化,可以有效避免 ANR 的发生,提升应用的性能和用户体验。
58 8
|
2月前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
Android面试高频知识点(1) 图解 Android 事件分发机制
39 1
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
Android开发
Android 事件分发机制详细解读
Android 事件分发机制详细解读
40 4
|
2月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
39 3
|
3月前
|
ARouter 测试技术 API
Android经典面试题之组件化原理、优缺点、实现方法?
本文介绍了组件化在Android开发中的应用,详细阐述了其原理、优缺点及实现方式,包括模块化、接口编程、依赖注入、路由机制等内容,并提供了具体代码示例。
48 2
|
3月前
|
编解码 前端开发 Android开发
Android经典实战之TextureView原理和高级用法
本文介绍了 `TextureView` 的原理和特点,包括其硬件加速渲染的优势及与其他视图叠加使用的灵活性,并提供了视频播放和自定义绘制的示例代码。通过合理管理生命周期和资源,`TextureView` 可实现高效流畅的图形和视频渲染。
251 12
|
2月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
32 0