触摸反馈基础
自定义单 View 的触摸反馈
- 重写
onTouchEvent(),在⽅法内部定制触摸反馈算法- 是否消费事件取决于
ACTION_DOWN事件是否返回true MotionEventgetActionMasked()和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则不拦截
- 如果设置了