多点触控手势是指多个指针(手指)同时轻触屏幕。本文档介绍如何检测涉及多个指针的手势。
跟踪多个指针
当多个指针同时轻触屏幕时,系统会生成以下触摸事件
ACTION_DOWN
:第一个指针轻触屏幕时发送。这将启动手势。此指针的指针数据始终位于MotionEvent
中的索引0
处。ACTION_POINTER_DOWN
:在第一个指针之后,其他指针进入屏幕时发送。您可以使用getActionIndex()
获取刚刚按下指针的索引。ACTION_MOVE
:在手势发生变化时发送,涉及任意数量的指针。ACTION_POINTER_UP
:非主要指针抬起时发送。您可以使用getActionIndex()
获取刚刚抬起的指针的索引。ACTION_UP
:最后一个指针离开屏幕时发送。ACTION_CANCEL
:指示整个手势(包括所有指针)都被取消。
开始和结束手势
手势是一系列事件,从 ACTION_DOWN
事件开始,以 ACTION_UP
或 ACTION_CANCEL
事件结束。一次只有一个活动手势。操作 DOWN、MOVE、UP 和 CANCEL 应用于整个手势。例如,具有 ACTION_MOVE
的事件可以指示此时所有按下指针的移动。
跟踪指针
使用指针的索引和 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); ... }
要支持多个触摸指针,您可以在其各个 ACTION_POINTER_DOWN
和 ACTION_DOWN
事件时间缓存所有活动指针及其 ID。在其 ACTION_POINTER_UP
和 ACTION_UP
事件中从缓存中删除指针。您可能会发现这些缓存的 ID 有助于正确处理其他操作事件。例如,在处理 ACTION_MOVE
事件时,查找每个缓存的活动指针 ID 的索引,使用 getX()
和 getY()
函数检索指针的坐标,然后将这些坐标与缓存的坐标进行比较以发现哪些指针移动了。
仅对 ACTION_POINTER_UP
和 ACTION_POINTER_DOWN
事件使用 getActionIndex()
函数。不要对 ACTION_MOVE
事件使用此函数,因为这始终返回 0
。
检索 MotionEvent
操作
使用 getActionMasked()
方法或兼容版本 MotionEventCompat.getActionMasked()
来检索 MotionEvent
的操作。与早期的 getAction()
方法不同,getActionMasked()
旨在与多个指针一起使用。它返回不带指针索引的操作。对于具有有效指针索引的操作,使用 getActionIndex()
返回与操作关联的指针的索引,如以下代码片段所示
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 ""; }
其他资源
有关输入事件的更多信息,请参阅以下参考