改不完的 Bug,写不完的矫情。公众号 杨正友 现在专注移动基础开发 ,涵盖音视频和 APM,信息安全等各个知识领域;只做全网最 Geek 的公众号,欢迎您的关注!
触摸反馈应该是自定义view最简单的部分了,不过内部的原理是比较复杂的,去了解里面的核心机制,需要自己去阅读源码,才能更好理解整个触摸机制,当然,知其所以然也是远远不够的,下面我就带大家了解自定义view触摸机制的难点重点。
一. 概念
触摸反馈就是View对你的用户的触摸事件进行自定义,重写onTouchEvent方法
二.自定义单View的触摸反馈
-
重写 onTouchEvent(),在方法内部定制触摸反馈算法
-
是否消费事件取决于 ACTION_DOWN 事件 或 POINT_DOWN 事件是否返回 为true
-
MotionEvent
-
getActionMasked() 和 getAction()
-
POINT_DOWN / POINT_UP 和 getActionIndex()
-
三.触摸反馈的流程
- Activity.dispatchEvent()
- 递归: ViewGroup(View).dispatchTouchEvent()
- ViewGroup.onInterceptTouchEvent()
- child.dispatchTouchEvent()
- super.dispatchTouchEvent()
- View.onTouchEvent()
- Activity.onTouchEvent()
四.View.dispatchTouchEvent()
-
如果设置了
OnTouchListener,调用OnTouchListener.onTouch()- 如果
OnTouchListener消费了该事件,返回true - 如果
OnTouchListener没有消费该事件,继续调用自己的onTouchEvent并返回 和onTouchEvent相同的结果
- 如果
-
如果没有设置
OnTouchListener,同上
五.ViewGroup.dispatchTouchEvent()
-
如果是用户初次按下 (ACTION_DOWN) ,清空TouchTargets 和 DISALLOW_INTERCEPT 标记
- 拦截处理 getParent().requestDisallowInterceptTouchEvent()
- 如果不拦截并不是 CANCEL 事件,并且是down 或者 POINT_DOWN ,尝试把pointer (手指) 通过 touchTarget 分配给子View;并且 如果 分配给 新的子View ,调用 child.dispatchEvent 把 DOWN 事件传给子View
-
看有没有 TouchTargets
- 如果没有,调用super.dispatchEvent()
- 如果有,调用child.dispatchEvent() 把事件传给对应的子View(如果有的话)
-
如果是 POINT_UP ,从 TouchTargets 中清除 POINTER 信息;如果 UP 或者 CANCEL 重置状态
六.TouchTarget
- 作用: 记录单
view是被哪些pointer(手指)按下的 - 结构: 单向链表
七.面试题分享
Alibaba面试题: 有一个ViewGroup, 然后手指头接触Button,手指头滑开了,滑开又松手的过程,整个事件发生了什么?经历了什么?
一开始ViewGroup 会接受到整个事件序列的第一个事件: ACTION_DOWN,ViewGroup#dispatchTouchEvent 收到ACTION_DOWN 后,
开始询问 ViewGroup#onInterceptTouchEvent 是否需要拦截,
默认情况下 ViewGroup#onInterceptTouchEvent 返回false 不拦截,开始向下传递ACTION_DOWN 事件,
Buttton#dispatchTouchEvent 收到ACTION_DOWN 询问onTouchEvent 是否处理,
Button 默认处理,此后的所有事件序列都直接跨过 ViewGroup#onInterceptTouchEvent 的判断直接传递给Button,
但 ViewGroup#dispatchTouchEvent 会收到所有事件。随着手指的滑动Button 的坐标发生了改变,当手指抬起时触发 Button#onClick 事件。