触摸反馈基础
自定义单 View 的触摸反馈
- 重写
onTouchEvent()
,在⽅法内部定制触摸反馈算法- 是否消费事件取决于
ACTION_DOWN
事件是否返回true
MotionEvent
getActionMasked()
和getAction()
POINTER_DOWN
/POINTER_UP
和getActionIndex()
- 是否消费事件取决于
View.onTouchEvent()
- 当⽤户按下(
ACTION_DOWN
):- 如果不在滑动控件中,切换⾄按下状态,并注册⻓按计时器
- 如果在滑动控件中,切换⾄预按下状态,并注册按下计时器
- 当进⼊按下状态并移动(
ACTION_MOVE
):- 重绘
Ripple Effect
- 如果移动出自⼰的范围,自我标记本次事件失效,忽略后续事件
- 重绘
- 当⽤户抬起(
ACTION_UP
):- 如果是按下状态并且未触发⻓按,切换⾄抬起状态并触发点击事件,并清除⼀切状态
- 如果已经触发⻓按,切换⾄抬起状态并清除⼀切状态
- 当事件意外结束(
ACTION_CANCEL
):- 切换⾄抬起状态,并清除⼀切状态
自定义 ViewGroup
的触摸反馈
除了重写
onTouchEvent()
,还需要重写onInterceptTouchEvent()
onInterceptTouchEvent()
不⽤在第⼀时间返回true
,⽽是在任意事件,需要拦截的时候返回true
就⾏
触摸反馈的流程
Activity.dispatchTouchEvent()
- 递归:
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
标记拦截处理
如果不拦截并且不是
CANCEL
事件,并且是DOWN
或者POINTER_DOWN
,尝试把pointer
(手指)通过TouchTarget
分配给子View
;并且如果分配给了新的子View
,调⽤child.dispatchTouchEvent()
把事件传给子 View看有没有
TouchTarget
- 如果没有,调⽤自⼰的
super.dispatchTouchEvent()
- 如果有,调⽤
child.dispatchTouchEvent()
把事件传给对应的子View
(如果有的话)
- 如果没有,调⽤自⼰的
如果是
POINTER_UP
,从TouchTargets
中清除POINTER
信息;如果是UP
或CANCEL
,重置状态
TouchTarget
- 作用:记录每个子
View
是被哪些pointer
(手指)按下的 - 结构:单向链表
拦截处理
- 如果不是初次按下,并且没有
TouchTarget
,直接拦截 - 如果是初次按下,或者有
TouchTarget
- 如果设置了
disallow intercept
,不拦截 - 否则,调⽤
onInterceptTouchEvent()
,如果返回true
则拦截,返回false
则不拦截
- 如果设置了