多點觸控手勢是指多個指標 (手指) 同時輕觸螢幕。本文說明如何偵測涉及多個指標的手勢。
追蹤多個指標
如果多個指標同時輕觸螢幕,系統會產生下列觸控事件:
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 ""; }
其他資源
如要進一步瞭解輸入事件,請參閱下列參考資料: