Obsługa gestów wielodotykowych

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak w funkcji tworzenia wiadomości używać dotyku i wprowadzania tekstu.

Gest wielodotykowy polega na dotknięciu ekranu kilkoma wskaźnikami (palcami) jednocześnie. W tym artykule opisujemy, jak wykrywać gesty wymagające użycia wielu wskaźników.

Śledzenie wielu wskaźników

Gdy kilka wskaźników dotknie ekranu jednocześnie, system wygeneruje te zdarzenia dotyku:

  • ACTION_DOWN: wysyłane, gdy pierwszy wskaźnik dotknie ekranu. Spowoduje to uruchomienie gestu. Dane wskaźnika tego wskaźnika zawsze znajdują się w indeksie MotionEvent w indeksie 0.
  • ACTION_POINTER_DOWN: wysyłane, gdy na ekranie pojawią się dodatkowe wskaźniki po pierwszym. Indeks wskaźnika, który zniknął, możesz uzyskać za pomocą polecenia getActionIndex().
  • ACTION_MOVE: wysyłane, gdy zmiana zachodzi gestem i obejmuje dowolną liczbę wskaźników.
  • ACTION_POINTER_UP: wysyłane, gdy wskaźnik inny niż główny przesuwa się w górę. Indeks wskaźnika, który przed chwilą został zwiększony, możesz uzyskać za pomocą funkcji getActionIndex().
  • ACTION_UP: wysyłane, gdy ostatni wskaźnik opuści ekran.
  • ACTION_CANCEL: wskazuje, że cały gest, w tym wszystkie wskaźniki, został anulowany.

Gesty rozpoczęcia i zakończenia

Gest to seria zdarzeń rozpoczynających się od zdarzenia ACTION_DOWN i kończących zdarzeniem ACTION_UP lub ACTION_CANCEL. Jednocześnie jest wykonywany 1 aktywny gest. Działania W DÓŁ, PRZENIEŚ, W GÓRĘ i ANULUJ mają zastosowanie do całego gestu. Na przykład zdarzenie z elementem ACTION_MOVE może wskazywać, że w danym momencie wszystkie wskaźniki kursora w dół.

Śledzenie wskaźników

Indeks i identyfikator wskaźnika pozwalają śledzić poszczególne pozycje wskaźników w elemencie MotionEvent.

  • Indeks: MotionEvent przechowuje informacje o wskaźniku w tablicy. Indeks wskaźnika to jego pozycja w tej tablicy. Większość metod MotionEvent wykorzystuje indeks wskaźnika jako parametr, a nie identyfikator wskaźnika.
  • ID: każdy wskaźnik ma też mapowanie identyfikatorów, które pozostają trwałe w przypadku zdarzeń dotknięcia, umożliwiając śledzenie poszczególnych wskaźników podczas całego gestu.

Pojedyncze wskaźniki pojawiają się w zdarzeniu ruchu w nieokreślonej kolejności. Indeks może się więc zmieniać z jednego zdarzenia na drugie, ale identyfikator wskaźnika pozostanie bez zmian, dopóki ten wskaźnik pozostanie aktywny. Użyj metody getPointerId(), aby uzyskać identyfikator wskaźnika umożliwiający śledzenie wskaźnika we wszystkich kolejnych zdarzeniach ruchu. Następnie w przypadku kolejnych zdarzeń ruchu użyj metody findPointerIndex(), aby uzyskać indeks wskaźnika dla określonego identyfikatora wskaźnika w tym zdarzeniu. Na przykład:

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

Aby obsługiwać wiele wskaźników dotykowych, możesz zapisać w pamięci podręcznej wszystkie aktywne wskaźniki z ich identyfikatorami w czasie poszczególnych zdarzeń ACTION_POINTER_DOWN i ACTION_DOWN. Usuń wskaźniki z pamięci podręcznej w zdarzeniach ACTION_POINTER_UP i ACTION_UP. Te identyfikatory w pamięci podręcznej mogą być przydatne do prawidłowej obsługi innych zdarzeń działań. Na przykład podczas przetwarzania zdarzenia ACTION_MOVE znajdź indeks każdego identyfikatora aktywnego wskaźnika zapisanego w pamięci podręcznej, pobierz współrzędne wskaźnika za pomocą funkcji getX() i getY(), a następnie porównaj te współrzędne ze współrzędnymi w pamięci podręcznej, aby wykryć, które wskaźniki zostały przeniesione.

Funkcji getActionIndex() należy używać tylko w przypadku zdarzeń ACTION_POINTER_UP i ACTION_POINTER_DOWN. Nie używaj tej funkcji w przypadku zdarzeń ACTION_MOVE, ponieważ zawsze zwraca ona 0.

Pobierz działania (MotionEvent)

Aby pobrać działanie MotionEvent, użyj metody getActionMasked() lub wersji zgodności MotionEventCompat.getActionMasked(). W przeciwieństwie do wcześniejszej metody getAction() metoda getActionMasked() została zaprojektowana z myślą o pracy z wieloma wskaźnikami. Zwraca działanie bez indeksów wskaźnika. W przypadku działań z prawidłowym indeksem wskaźnika użyj getActionIndex(), aby zwrócić indeks wskaźników powiązanych z działaniem, jak pokazano w tym fragmencie:

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 "";
}
Rysunek 1. Wzory do rysowania przy użyciu wielodotyku.

Dodatkowe materiały

Więcej informacji o zdarzeniach wejściowych znajdziesz w tych materiałach: