マルチタップ操作の処理

マルチタップ操作とは、複数のポインタ(指)で同時に画面をタップする場合のことです。このレッスンでは、複数のポインタが含まれる操作を検出する方法について説明します。

以下の関連リソースもご覧ください。

複数のポインタのトラッキング

複数のポインタで同時に画面をタップすると、システムは次のタッチイベントを生成します。

  • 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 のアクションの取得

MotionEvent のアクションを取得するには、常に getActionMasked() メソッド(できれば MotionEventCompat.getActionMasked() の互換バージョン)を使用する必要があります。過去の getAction() メソッドと異なり、getActionMasked() は複数のポインタで動作するように設計されています。ポインタのインデックス ビットを含めずに、実行中のマスクされたアクションを返します。その後、getActionIndex() を使用して、アクションに関連付けられたポインタのインデックスを返すことができます。これを次のスニペットで示します。

注: この例では、MotionEventCompat クラスを使用しています。このクラスはサポート ライブラリにあります。幅広いプラットフォームに最適なサポートを提供するために、MotionEventCompat を使用する必要があります。MotionEventCompatMotionEvent クラスの代替ではありません。むしろ、そのイベントに関連付けられた目的のアクションを受け取るために、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 "";
    }
    

マルチタップの詳細といくつかの例については、ドラッグしてサイズ変更のレッスンをご覧ください。