Menangani gestur multi-sentuh

Gestur multi-sentuh adalah saat beberapa pointer (jari) menyentuh layar secara bersamaan. Tutorial ini menjelaskan cara mendeteksi gestur yang melibatkan beberapa pointer.

Lihat referensi terkait berikut:

Melacak beberapa pointer

Ketika beberapa pointer menyentuh layar secara bersamaan, sistem akan menghasilkan peristiwa sentuhan berikut:

  • ACTION_DOWN— Untuk pointer pertama yang menyentuh layar. Langkah ini akan memulai gestur. Data pointer untuk pointer ini selalu ada pada indeks 0 dalam MotionEvent.
  • ACTION_POINTER_DOWN—Untuk pointer tambahan yang masuk ke layar selain yang pertama. Data pointer untuk pointer ini ada pada indeks yang ditampilkan oleh getActionIndex().
  • ACTION_MOVE— Suatu perubahan telah terjadi selama gestur penekanan.
  • ACTION_POINTER_UP—Dikirim ketika pointer non-primer naik.
  • ACTION_UP—Dikirim ketika pointer terakhir meninggalkan layar.

Anda melacak setiap pointer dalam MotionEvent melalui indeks dan ID setiap pointer:

  • Indeks: MotionEvent secara efektif menyimpan informasi tentang setiap pointer dalam array. Indeks pointer adalah posisinya di dalam array ini. Sebagian besar metode MotionEvent yang Anda gunakan untuk berinteraksi dengan pointer mengambil indeks pointer sebagai parameter, bukan ID pointer.
  • ID: Setiap pointer juga memiliki pemetaan ID yang tetap ada di semua peristiwa sentuhan untuk memungkinkan pelacakan kursor individual di seluruh gestur.

Urutan di mana setiap pointer muncul dalam suatu peristiwa motion tidak ditentukan. Sehingga, indeks suatu pointer dapat berubah dari satu peristiwa ke peristiwa berikutnya, tetapi ID pointer dari suatu pointer dipastikan tetap konstan selama pointer tetap aktif. Gunakan metode getPointerId() untuk mendapatkan ID pointer untuk melacak pointer di semua peristiwa gerakan berikutnya dalam gestur. Kemudian untuk peristiwa gerakan berturut-turut, gunakan metode findPointerIndex() untuk memperoleh indeks pointer untuk ID pointer yang diberikan dalam peristiwa gerakan tersebut. Contoh:

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);
        ...
    }
    

Mendapatkan tindakan MotionEvent

Anda harus selalu menggunakan metode getActionMasked() (atau lebih baik lagi, versi kompatibilitas MotionEventCompat.getActionMasked()) untuk mengambil tindakan MotionEvent. Tidak seperti metode getAction() yang lama, getActionMasked() didesain untuk berfungsi dengan beberapa pointer. Hal ini akan mengembalikan tindakan samaran yang dilakukan, tanpa menyertakan bit indeks pointer. Selanjutnya, Anda dapat menggunakan getActionIndex() untuk mengembalikan indeks pointer yang terkait dengan tindakan. Hal ini ditunjukkan dalam cuplikan di bawah.

Catatan: Contoh ini menggunakan class MotionEventCompat. Class ini ada di Support Library. Anda harus menggunakan MotionEventCompat untuk memberikan dukungan terbaik bagi berbagai platform. Perhatikan bahwa MotionEventCompat bukan pengganti untuk class MotionEvent. Sebaliknya, metode ini menyediakan metode utilitas statis yang Anda teruskan objek MotionEvent untuk menerima tindakan yang diinginkan terkait dengan peristiwa tersebut.

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 "";
    }
    

Untuk diskusi selengkapnya tentang multi-sentuh dan beberapa contoh yang tersedia, lihat tutorial Tarik dan Skalakan.