改不完的 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
事件。