多点触控手势是指多个指针(手指)同时轻触屏幕。这节课介绍了如何检测涉及多个指针的手势。
跟踪多个指针
当多个指针同时轻触屏幕时,系统会生成以下触摸事件:
ACTION_DOWN
- 针对轻触屏幕的第一个指针。这是手势的起点。此指针的指针数据始终位于MotionEvent
中的索引 0 处。ACTION_POINTER_DOWN
- 针对除第一个指针以外进入屏幕的其他指针。此指针的指针数据位于getActionIndex()
返回的索引处。ACTION_MOVE
- 在执行按下手势的过程中发生了变化。ACTION_POINTER_UP
- 当非主要指针抬起时发送。ACTION_UP
- 当最后一个指针离开屏幕时发送。
您可以通过每个指针的索引和 ID 跟踪 MotionEvent
中的各个指针:
- 索引:
MotionEvent
会有效地将关于每个指针的信息存储在数组中。指针的索引就是指针在此数组中的位置。用于与指针进行交互的大多数MotionEvent
方法都使用触控点索引(而非指针 ID)作为参数。 - ID:每个指针还有一个 ID 映射,该映射在触摸事件之间保持不变,让您能够在整个手势中跟踪单个指针。
单个指针在动作事件中的显示顺序是未定义的。因此,不同事件中的触控点索引可能会不一样,但只要指针保持活动状态,其指针 ID 就会保持不变。您可以使用 getPointerId()
方法获取指针的 ID,并在手势中的所有后续动作事件中跟踪指针。然后,对于连续动作事件,可以使用 findPointerIndex()
方法获取给定指针 ID 在相应动作事件中的触控点索引。例如:
Kotlin
private var mActivePointerId: Int = 0 override fun onTouchEvent(event: MotionEvent): Boolean { ... // Get the pointer ID mActivePointerId = event.getPointerId(0) // ... Many touch events later... // Use the pointer ID to find the index of the active pointer // and fetch its position val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex -> // Get the pointer's current position event.getX(pointerIndex) to event.getY(pointerIndex) } ... }
Java
private int mActivePointerId; public boolean onTouchEvent(MotionEvent event) { ... // Get the pointer ID mActivePointerId = event.getPointerId(0); // ... Many touch events later... // Use the pointer ID to find the index of the active pointer // and fetch its position int pointerIndex = event.findPointerIndex(mActivePointerId); // Get the pointer's current position float x = event.getX(pointerIndex); float y = event.getY(pointerIndex); ... }
获取 MotionEvent 的操作
您应该始终使用方法 getActionMasked()
(最好使用兼容版本 MotionEventCompat.getActionMasked()
)来检索 MotionEvent
的操作。与旧版 getAction()
方法不同,getActionMasked()
能够与多个指针一起使用。它可以返回正在执行的已遮蔽操作,而不包含触控点索引位。然后,您可以使用 getActionIndex()
返回与此操作相关联的指针的索引。以下代码段对此进行了说明。
注意:此示例使用的是 MotionEventCompat
类。此类包含在支持库中。您应该使用 MotionEventCompat
为各种平台提供最佳支持。请注意,MotionEventCompat
不能替代 MotionEvent
类。相反,它可以提供静态实用程序方法,您可以向这些方法传递 MotionEvent
对象,以便接收与相应事件相关的预期操作。
Kotlin
val (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action -> Log.d(DEBUG_TAG, "The action is ${actionToString(action)}") // Get the index of the pointer associated with the action. MotionEventCompat.getActionIndex(event).let { index -> // The coordinates of the current screen contact, relative to // the responding View or Activity. MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt() } } if (event.pointerCount > 1) { Log.d(DEBUG_TAG, "Multitouch event") } else { // Single touch event Log.d(DEBUG_TAG, "Single touch event") } ... // Given an action int, returns a string description fun actionToString(action: Int): String { return when (action) { MotionEvent.ACTION_DOWN -> "Down" MotionEvent.ACTION_MOVE -> "Move" MotionEvent.ACTION_POINTER_DOWN -> "Pointer Down" MotionEvent.ACTION_UP -> "Up" MotionEvent.ACTION_POINTER_UP -> "Pointer Up" MotionEvent.ACTION_OUTSIDE -> "Outside" MotionEvent.ACTION_CANCEL -> "Cancel" else -> "" } }
Java
int action = MotionEventCompat.getActionMasked(event); // Get the index of the pointer associated with the action. int index = MotionEventCompat.getActionIndex(event); int xPos = -1; int yPos = -1; Log.d(DEBUG_TAG,"The action is " + actionToString(action)); if (event.getPointerCount() > 1) { Log.d(DEBUG_TAG,"Multitouch event"); // The coordinates of the current screen contact, relative to // the responding View or Activity. xPos = (int)MotionEventCompat.getX(event, index); yPos = (int)MotionEventCompat.getY(event, index); } else { // Single touch event Log.d(DEBUG_TAG,"Single touch event"); xPos = (int)MotionEventCompat.getX(event, index); yPos = (int)MotionEventCompat.getY(event, index); } ... // Given an action int, returns a string description public static String actionToString(int action) { switch (action) { case MotionEvent.ACTION_DOWN: return "Down"; case MotionEvent.ACTION_MOVE: return "Move"; case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down"; case MotionEvent.ACTION_UP: return "Up"; case MotionEvent.ACTION_POINTER_UP: return "Pointer Up"; case MotionEvent.ACTION_OUTSIDE: return "Outside"; case MotionEvent.ACTION_CANCEL: return "Cancel"; } return ""; }
如需详细了解多点触控和一些示例,请参阅拖动和缩放课程。