ACTION_DOWN 第一个手指按下(之前没有任何手指触摸到View)
ACTION_UP 最后一个手指抬起(抬起后没有手指触摸View,这个手指未必是ACTION_DOWN)
ACTION_MOVE 有手指所发生移动
ACTION_POINTEER_DOWN 额外手指按下(按下之前已经有别的手指触摸到View)
ACTION_POINTER_UP 有手指抬起,但不是最后一个(抬起之后,仍然有别的手指在触摸着View)
触摸事件的结构
触摸事件是按序列来分组的,每一组事件必然以ACTION_DOWN开头,以ACTION_UP或ACTION_CANCEL结束
ACTION_POINTER_DOWN和ACTION_MOVE一样,只是事件序列组成部分,并不会单独分出新的事件序列
触摸事件序列是针对View的,而不是针对pointer的。
同一个时刻,一个View要么没有事件序列,要么只有一个事件序列
多点触控的三种类型
1. 接力型
同一时刻只有一个pointer起作用,即最新的pointer。如listview、recyclerview。实现方式:在ACTION_POINTER_DOWN和ACTION_POINTER_UP时记录下最新的pointer,在之后的ACTION_MOVE事件中使用这个pointer来判断位置。
case MotionEvent.ACTION_DOWN:
//拿到第0跟手指
trackID = event.getPointerId(0)
downX = event.getX()
downY = event.getY()
originalOffsetX = offsetX
originalOffsetY = offsetY
break
case MotionEvent.ACTION_MOVE:
int index = event.findPointerIndex(trackID)
offsetX = originalOffsetX + event.getX(index) - downX
offsetY = originalOffsetY + event.getY(index) - downY
invalidate()
break
case MotionEvent.ACTION_POINTER_DOWN:
int actionIndex = event.getActionIndex()
trackID = event.getPointerId(actionIndex)
downX = event.getX(actionIndex)
downY = event.getY(actionIndex)
originalOffsetX = offsetX
originalOffsetY = offsetY
break
case MotionEvent.ACTION_POINTER_UP:
//抬起来的这根手指的index
actionIndex = event.getActionIndex()
int pointerId = event.getPointerId(actionIndex)
if (pointerId == trackID) {
//将当前的点分配到这根手指
int newIndex
//如果抬起来的这根手指恰好是最后一根则
if (actionIndex == event.getPointerCount() - 1) {
newIndex = event.getPointerCount() - 2
} else {
//如果是其他手指则
newIndex = event.getPointerCount() - 1
trackID = event.getPointerId(newIndex)
downX = event.getX(newIndex)
downY = event.getY(newIndex)
originalOffsetX = offsetX
originalOffsetY = offsetY
break
复制代码
2. 配合型/协作型
所有触摸到View的pointer共同起作用。如:ScaleGestureDetector,以及GestureDetector的onScroll()方法判断。实现方式:在每个DOWN、POINTER_DOWN、POINTER_UP、UP事件中使用所有的pointer的坐标来共同更新焦点坐标,在MOVE事件中使用所有的pointer的坐标来判断位置。
float sumX = 0
float sumY = 0
int pointerCount = event.getPointerCount()
boolean isPointerUp = event.getActionMasked() == MotionEvent.ACTION_POINTER_UP
for (int i = 0
if (!(isPointerUp && i == event.getActionIndex())) {
sumX += event.getX(i)
sumY += event.getY(i)
if (isPointerUp) {
pointerCount -= 1
float focusX = sumX / pointerCount
float focusY = sumY / pointerCount
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_POINTER_UP:
downX = focusX
downY = focusY
originalOffsetX = offsetX
originalOffsetY = offsetY
break
case MotionEvent.ACTION_MOVE:
offsetX = originalOffsetX + focusX - downX
offsetY = originalOffsetY + focusY - downY
invalidate()
break
复制代码
3. 各自独立型
各个pointer做不同的事,互不影响。典型:支持多画笔的画板应用。实现方式:在每个DOWN、POINTER_DOWN事件中记录下每个pointer的id,在MOVE事件中使用id对他们进行追踪。
case ACTION_DOWN:
case ACTION_POINTER_DOWN:
int actionIndex = event.getActionIndex()
int pointerId = event.getPointerId(actionIndex)
Path path = new Path()
path.moveTo(event.getX(actionIndex), event.getY(actionIndex))
paths.append(pointerId, path)
invalidate()
break
case ACTION_MOVE:
for (int i = 0
pointerId = event.getPointerId(i)
path = paths.get(pointerId)
path.lineTo(event.getX(i), event.getY(i))
invalidate()
break
case ACTION_UP:
case ACTION_POINTER_UP:
pointerId = event.getPointerId(event.getActionIndex())
paths.remove(pointerId)
invalidate()
break
复制代码