阅读 258

解码自定义View-触摸反馈

  触摸反馈应该是自定义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_DOWNViewGroup#dispatchTouchEvent 收到ACTION_DOWN 后,

  开始询问 ViewGroup#onInterceptTouchEvent 是否需要拦截,

  默认情况下 ViewGroup#onInterceptTouchEvent 返回false 不拦截,开始向下传递ACTION_DOWN 事件,

  Buttton#dispatchTouchEvent 收到ACTION_DOWN 询问onTouchEvent 是否处理,

  Button 默认处理,此后的所有事件序列都直接跨过 ViewGroup#onInterceptTouchEvent 的判断直接传递给Button

  但 ViewGroup#dispatchTouchEvent 会收到所有事件。随着手指的滑动Button 的坐标发生了改变,当手指抬起时触发 Button#onClick 事件。

八.参考资料