MotionEvent.getActionMasked()
常⻅值:
- ACTION_DOWN 第⼀个⼿指按下(之前没有任何⼿指触摸到 View)
- ACTION_UP 最后⼀个⼿指抬起(抬起之后没有任何⼿指触摸到 View,这个⼿指未必是 ACTION_DOWN 的那个⼿指)
- ACTION_MOVE 有⼿指发⽣移动
- ACTION_POINTER_DOWN 额外⼿指按下(按下之前已经有别的⼿指触摸到 View)
- ACTION_POINTER_UP 有⼿指抬起,但不是最后⼀个(抬起之后,仍然还有别的⼿指在触
摸着 View)
event.getActionIndex()
获取抬起或者落下手指的 index 。如果现在屏幕上已经有一个手指,此时又按下一个手指,该方法返回1(index 从0开始计数);如果现在将第一个手指抬起,该方法会返回0,如果是第二个手指抬起,该方法会返回1。
ACTION_DOWN 和 ACTION_UP 中,event.getActionIndex() 是没有意义的,而 ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 中才有意义,得知是哪个 index 的手指按下或者抬起了。
触摸事件的结构
触摸事件是按序列来分组的,每⼀组事件必然以 ACTION_DOWN 开头,以 ACTION_UP 或 ACTION_CANCEL 结束。
ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 和 ACTION_MOVE ⼀样,只是事件序列中的组成部分,并不会单独分出新的事件序列。
触摸事件序列是针对 View 的,⽽不是针对 pointer 的。“某个 pointer 的事件”这种说法是不正确的。getX()/getY() 获取的是第0个 pointer 的横/纵坐标,getX()/getY() 与 getX(0)/getY(0) 是相等的。ACTION_MOVE 并不是某个点在移动,而 getX()/getY() 也不是获得移动的点的坐标。ACTION_MOVE 只会告诉有点移动了,而具体是那个点不清楚,也没有相关 API。
同⼀时刻,⼀个 View 要么没有事件序列,要么只有⼀个事件序列。
对于 ACTION_POINTER_DOWN ,手指刚刚触摸 View ,这个点便会被记录,比如原来有1个触摸点,此时便会变成2个,在后续的 ACTION_MOVE 会有2个点;在某一时刻抬起一个手指,(原来屏幕上有两个手指),ACTION_POINTER_UP 中,触摸点的数量还是2,而下一个 ACTION_MOVE 中,触摸点的数量才会变成1。
pointer 的 index 与 id
当 pointerA 触摸到屏幕时,它的 index 为0,id 为0;此时 pointerB 触摸到屏幕, pointerA 依然是 index 为0,id 为0,而 pointerB 的 index 是1,id 为1;此时 pointerA 松开, pointerB 的 index 变为0,而 id 还是1。index 是用来遍历 pointer 的,追踪某个 pointer 使用 id 。
id 是可以复用的,接着之前的场景,现在 pointerA 松开,pointerB 的 index 为0,id 为1,此时不存在 id 为0的 pointer 了。当有新的 pointer 按下时,此时并不会将新的 pointer 的 id 赋值为 2 ,而是将之前为0的 id 赋给新的 pointer ,而这个新的 pointer 可以是 pointerA 也可以不是。API会假设之前抬起的手指(pointerA)此时落下了,这只是一种简单的假设。由于 id 的复用,此时将之前的 index 也重新还给新的 pointer ,新 pointer 的 id 为0,index 为0,pointerB 的 index 为1,id 为1。
下面看三个 pointer 的情况:pointerA、pointerB、pointerC 依次触摸屏幕,pointerA 的 id 和 index 分别是 0、0,pointerB 的 id 和 index 分别是 1、1,pointerC 的 id 和 index 分别是 2、2;此时抬起 pointerB ,pointerA 的 id 和 index 分别是 0、0,而 pointerC 的 id 和 index 分别是 2、1;此时新的 pointer 落下(可以是 pointerB 也可以不是),pointerA 的 id 和 index 分别是 0、0,pointerC 的 id 和 index 分别是 2、2,新的 pointer 的 id 和 index 分别是 1、1。
id 的复用会导致 index 的重新调整,id 不会变化,而 index 在 pointer 抬起和落下时会变化,并且它永远是连续的。
多点触控的三种类型
接力型
同⼀时刻只有⼀个 pointer 起作⽤,即最新的 pointer。
- 典型: ListView、RecyclerView。
- 实现方式:在 ACTION_POINTER_DOWN 和 ACTION_POINTER_UP 时记录下最新的 pointer,在之后的 ACTION_MOVE 事件中使⽤这个 pointer 来判断位置。
- 代码详见 MultiTouchView1
配合型 / 协作型
所有触摸到 View 的 pointer 共同起作⽤。
- 典型: ScaleGestureDetector,以及 GestureDetector 的 onScroll() ⽅法判断。
- 实现方式:在每个 DOWN、 POINTER_DOWN、 POINTER_UP、 UP 事件中使⽤所有 pointer 的坐标来共同更新焦点坐标,并在 MOVE 事件中使⽤所有 pointer 的坐标来判断位置。
- 代码详见 MultiTouchView2
各⾃为战型
各个 pointer 做不同的事,互不影响。
- 典型:⽀持多画笔的画板应⽤。
- 实现⽅式:在每个 DOWN、 POINTER_DOWN 事件中记录下每个 pointer 的 id,在 MOVE 事件中使⽤ id 对它们进⾏跟踪。
- 代码详见 MultiTouchView3
参考资料:
腾讯课堂 HenCoder