Gest wielodotykowy to gest wykonywany jednocześnie kilkoma wskaźnikami (palcami). Z tego dokumentu dowiesz się, jak wykrywać gesty, które wykorzystują wiele wskaźników.
Śledzenie wielu wskaźników
Gdy kilka wskaźników dotyka ekranu jednocześnie, system generuje te zdarzenia dotyku:
ACTION_DOWN:
wysyłany, gdy pierwszy wskaźnik dotyka ekranu. To uruchamia gest. Dane wskaźnika są zawsze w indeksie 0 w tablicy MotionEvent.
ACTION_POINTER_DOWN:
wysyłane, gdy dodatkowe wskaźniki pojawiają się na ekranie po pierwszym. Za pomocą parametru getActionIndex() możesz uzyskać indeks wskaźnika, który właśnie się przesunął w dół.
ACTION_MOVE:
wysyłane, gdy nastąpi zmiana w geście, która obejmuje dowolną liczbę
wskaźników.
ACTION_POINTER_UP:
wysyłany, gdy wskaźnik inny niż podstawowy przesunie się w górę. Za pomocą getActionIndex() możesz uzyskać indeks wskaźnika, który właśnie się podniósł.
ACTION_UP:
wysyłane, gdy ostatni wskaźnik opuści ekran.
ACTION_CANCEL:
oznacza, że całe gesty, w tym wszystkie wskaźniki, zostały anulowane.
Gesty rozpoczęcia i zakończenia
Gest to seria zdarzeń, która zaczyna się zdarzeniem ACTION_DOWN, a kończy zdarzeniem ACTION_UP lub ACTION_CANCEL. W danym momencie może być aktywny tylko jeden gest. Działania DOŁÓŻ, PRZESUŃ, W GÓRĘ i ANULUJ dotyczą całego gestu. Na przykład zdarzenie z wartością ACTION_MOVE może wskazywać, że w danym momencie wszystkie wskaźniki zostały przesunięte w dół.
Śledź wskaźniki
Aby śledzić poszczególne pozycje wskaźników w ramach MotionEvent, używaj ich indeksu i identyfikatora.
Indeks: MotionEvent przechowuje wskaźnik do informacji w tablicy. Indeks wskaźnika to jego pozycja w tej tablicy. Większość metod MotionEvent przyjmuje jako parametr indeks wskaźnika, a nie jego identyfikator.
Identyfikator: każdy wskaźnik ma też mapowanie identyfikatorów, które pozostaje niezmienne w przypadku zdarzeń dotykowych, aby umożliwić śledzenie pojedynczego wskaźnika przez cały czas trwania gestu.
Poszczególne wskaźniki pojawiają się w ramach zdarzenia ruchu w niezdefiniowanej kolejności. Wskaźnik może się zmieniać z jednego zdarzenia na inne, ale jego identyfikator pozostaje niezmienny, dopóki pozostaje aktywny. Użyj metody getPointerId(), aby uzyskać identyfikator wskaźnika i śledzić go w przypadku wszystkich kolejnych zdarzeń ruchu w ramach gestu. Następnie w przypadku kolejnych zdarzeń ruchu użyj metody findPointerIndex(), aby uzyskać indeks wskaźnika dla danego identyfikatora wskaźnika w tym zdarzeniu ruchu.
Przykład:
Kotlin
privatevarmActivePointerId:Int=0overridefunonTouchEvent(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)toevent.getY(pointerIndex)}...}
Java
privateintmActivePointerId;publicbooleanonTouchEvent(MotionEventevent){...// 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.intpointerIndex=event.findPointerIndex(mActivePointerId);// Get the pointer's current position.floatx=event.getX(pointerIndex);floaty=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 zapisane w pamięci podręcznej mogą okazać się przydatne do prawidłowego obsługiwania innych zdarzeń akcji. Na przykład podczas przetwarzania zdarzenia ACTION_MOVE znajdź indeks każdego zapisanego w pamięci podręcznej aktywnego identyfikatora wskaźnika, pobierz współrzędne wskaźnika za pomocą funkcji getX() i getY(), a następnie porównaj te współrzędne z zapisanymi w pamięci podręcznej, aby dowiedzieć się, które wskaźniki się poruszyły.
Funkcji getActionIndex() używaj 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 wartość 0.
Pobieranie działań (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 do pracy z wieloma wskaźnikami. Zwraca działanie bez indeksów wskaźnika. W przypadku działań z prawidłowym wskaźnikiem indeksu użyj funkcji getActionIndex(), aby zwrócić indeks wskaźników powiązanych z działaniem, jak pokazano w tym fragmencie kodu:
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()toMotionEventCompat.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.funactionToString(action:Int):String{returnwhen(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
intaction=MotionEventCompat.getActionMasked(event);// Get the index of the pointer associated with the action.intindex=MotionEventCompat.getActionIndex(event);intxPos=-1;intyPos=-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 descriptionpublicstaticStringactionToString(intaction){switch(action){caseMotionEvent.ACTION_DOWN:return"Down";caseMotionEvent.ACTION_MOVE:return"Move";caseMotionEvent.ACTION_POINTER_DOWN:return"Pointer Down";caseMotionEvent.ACTION_UP:return"Up";caseMotionEvent.ACTION_POINTER_UP:return"Pointer Up";caseMotionEvent.ACTION_OUTSIDE:return"Outside";caseMotionEvent.ACTION_CANCEL:return"Cancel";}return"";}
Rysunek 1. rysowanie za pomocą gestów wielodotykowych
wzory.
Dodatkowe materiały
Więcej informacji o zdarzeniach związanych z wejściami znajdziesz w tych materiałach:
Treść strony i umieszczone na niej fragmenty kodu podlegają licencjom opisanym w Licencji na treści. Java i OpenJDK są znakami towarowymi lub zastrzeżonymi znakami towarowymi należącymi do firmy Oracle lub jej podmiotów stowarzyszonych.
Ostatnia aktualizacja: 2025-07-26 UTC.
[[["Łatwo zrozumieć","easyToUnderstand","thumb-up"],["Rozwiązało to mój problem","solvedMyProblem","thumb-up"],["Inne","otherUp","thumb-up"]],[["Brak potrzebnych mi informacji","missingTheInformationINeed","thumb-down"],["Zbyt skomplikowane / zbyt wiele czynności do wykonania","tooComplicatedTooManySteps","thumb-down"],["Nieaktualne treści","outOfDate","thumb-down"],["Problem z tłumaczeniem","translationIssue","thumb-down"],["Problem z przykładami/kodem","samplesCodeIssue","thumb-down"],["Inne","otherDown","thumb-down"]],["Ostatnia aktualizacja: 2025-07-26 UTC."],[],[],null,["# Handle multi-touch gestures\n\nTry the Compose way \nJetpack Compose is the recommended UI toolkit for Android. Learn how to use touch and input in Compose. \n[Multi-touch gestures →](/develop/ui/compose/touch-input/pointer-input/multi-touch) \n\nA multi-touch gesture is when multiple pointers (fingers) tap the screen at\nthe same time. This document describes how to detect gestures that involve\nmultiple pointers.\n\nTrack multiple pointers\n-----------------------\n\nWhen multiple pointers tap the screen at the same time, the system generates\nthe following touch events:\n\n- [ACTION_DOWN](/reference/android/view/MotionEvent#ACTION_DOWN): sent when the first pointer taps the screen. This starts the gesture. The pointer data for this pointer is always at index `0` in the [MotionEvent](/reference/android/view/MotionEvent).\n- [ACTION_POINTER_DOWN](/reference/android/view/MotionEvent#ACTION_POINTER_DOWN): sent when extra pointers enter the screen after the first. You can obtain the index of the pointer that just went down using [getActionIndex()](/reference/android/view/MotionEvent#getActionIndex()).\n- [ACTION_MOVE](/reference/android/view/MotionEvent#ACTION_MOVE): sent when a change occurs in a gesture, involving any number of pointers.\n- [ACTION_POINTER_UP](/reference/android/view/MotionEvent#ACTION_POINTER_UP): sent when a non-primary pointer goes up. You can obtain the index of the pointer that just went up using `getActionIndex()`.\n- [ACTION_UP](/reference/android/view/MotionEvent#ACTION_UP): sent when the last pointer leaves the screen.\n- [ACTION_CANCEL](/reference/android/view/MotionEvent#ACTION_CANCEL): indicates that the entire gesture, including all pointers, is canceled.\n\n### Start and end gestures\n\nA gesture is a series of events starting with an `ACTION_DOWN`\nevent and ending with either an `ACTION_UP` or\n`ACTION_CANCEL` event. There is one active gesture at a time. The\nactions DOWN, MOVE, UP, and CANCEL apply to the entire gesture. For example, an\nevent with `ACTION_MOVE` can indicate a movement for all pointers\ndown at that moment.\n\n### Keep track of pointers\n\nUse the pointer's index and ID to keep track of the individual pointers\npositions within a `MotionEvent`.\n\n- **Index** : a `MotionEvent` stores pointer information in an array. The index of a pointer is its position within this array. Most of the `MotionEvent` methods take the pointer index as a parameter, rather than the pointer ID.\n- **ID**: each pointer also has an ID mapping that stays persistent across touch events to allow for tracking of an individual pointer across the entire gesture.\n\nIndividual pointers appear within a motion event in an undefined order. Thus,\nthe index of a pointer can change from one event to the next, but the pointer ID\nof a pointer is guaranteed to remain constant as long as the pointer remains\nactive. Use the\n[getPointerId()](/reference/android/view/MotionEvent#getPointerId(int))\nmethod to obtain a pointer's ID to track the pointer across all subsequent\nmotion events in a gesture. Then, for successive motion events, use the\n[findPointerIndex()](/reference/android/view/MotionEvent#findPointerIndex(int))\nmethod to obtain the pointer index for a given pointer ID in that motion event.\nFor example: \n\n### Kotlin\n\n```kotlin\nprivate var mActivePointerId: Int = 0\n\noverride fun onTouchEvent(event: MotionEvent): Boolean {\n ...\n // Get the pointer ID.\n mActivePointerId = event.getPointerId(0)\n\n // ... Many touch events later...\n\n // Use the pointer ID to find the index of the active pointer\n // and fetch its position.\n val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex -\u003e\n // Get the pointer's current position.\n event.getX(pointerIndex) to event.getY(pointerIndex)\n }\n ...\n}\n```\n\n### Java\n\n```java\nprivate int mActivePointerId;\n\npublic boolean onTouchEvent(MotionEvent event) {\n ...\n // Get the pointer ID.\n mActivePointerId = event.getPointerId(0);\n\n // ... Many touch events later...\n\n // Use the pointer ID to find the index of the active pointer\n // and fetch its position.\n int pointerIndex = event.findPointerIndex(mActivePointerId);\n // Get the pointer's current position.\n float x = event.getX(pointerIndex);\n float y = event.getY(pointerIndex);\n ...\n}\n```\n\nTo support multiple touch pointers, you can cache all active pointers with\ntheir IDs at their individual `ACTION_POINTER_DOWN` and\n`ACTION_DOWN` event time. Remove the pointers from your cache at\ntheir `ACTION_POINTER_UP` and `ACTION_UP`events. You might\nfind these cached IDs helpful to handle other action events correctly. For\nexample, when processing an `ACTION_MOVE` event, find the index for\neach cached active pointer ID, retrieve the pointer's coordinates using the\n[getX()](/reference/android/view/MotionEvent#getX(int))\nand\n[getY()](/reference/android/view/MotionEvent#getY(int))\nfunctions, then compare these coordinates with your cached coordinates to\ndiscover which pointers moved.\n\nUse the `getActionIndex()` function with\n`ACTION_POINTER_UP` and `ACTION_POINTER_DOWN` events\nonly. Don't use this function with `ACTION_MOVE` events, as this\nalways returns `0`.\n\nRetrieve `MotionEvent` actions\n------------------------------\n\nUse the\n[getActionMasked()](/reference/android/view/MotionEvent#getActionMasked())\nmethod or the compatibility version\n[MotionEventCompat.getActionMasked()](/reference/androidx/core/view/MotionEventCompat#getActionMasked(android.view.MotionEvent))\nto retrieve the action of a `MotionEvent`. Unlike the earlier\n[getAction()](/reference/android/view/MotionEvent#getAction())\nmethod, `getActionMasked()` is designed to work with multiple\npointers. It returns the action without the pointer indices. For actions with a\nvalid pointer index, use `getActionIndex()` to return the index of\nthe pointers associated with the action as shown in the following snippet:\n| **Note:** This example uses the [MotionEventCompat](/reference/androidx/core/view/MotionEventCompat) class, a class in the [Support\nLibrary](/tools/support-library). Use `MotionEventCompat` to provide the best support for a wide range of platforms. `MotionEventCompat` is *not* a replacement for the `MotionEvent` class. Rather, it provides static utility methods to which you pass your `MotionEvent` object to receive the desired action associated with that event. \n\n### Kotlin\n\n```kotlin\nval (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action -\u003e\n Log.d(DEBUG_TAG, \"The action is ${actionToString(action)}\")\n // Get the index of the pointer associated with the action.\n MotionEventCompat.getActionIndex(event).let { index -\u003e\n // The coordinates of the current screen contact, relative to\n // the responding View or Activity.\n MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt()\n }\n}\n\nif (event.pointerCount \u003e 1) {\n Log.d(DEBUG_TAG, \"Multitouch event\")\n\n} else {\n // Single touch event.\n Log.d(DEBUG_TAG, \"Single touch event\")\n}\n\n...\n\n// Given an action int, returns a string description.\nfun actionToString(action: Int): String {\n return when (action) {\n MotionEvent.ACTION_DOWN -\u003e \"Down\"\n MotionEvent.ACTION_MOVE -\u003e \"Move\"\n MotionEvent.ACTION_POINTER_DOWN -\u003e \"Pointer Down\"\n MotionEvent.ACTION_UP -\u003e \"Up\"\n MotionEvent.ACTION_POINTER_UP -\u003e \"Pointer Up\"\n MotionEvent.ACTION_OUTSIDE -\u003e \"Outside\"\n MotionEvent.ACTION_CANCEL -\u003e \"Cancel\"\n else -\u003e \"\"\n }\n}\n```\n\n### Java\n\n```java\nint action = MotionEventCompat.getActionMasked(event);\n// Get the index of the pointer associated with the action.\nint index = MotionEventCompat.getActionIndex(event);\nint xPos = -1;\nint yPos = -1;\n\nLog.d(DEBUG_TAG,\"The action is \" + actionToString(action));\n\nif (event.getPointerCount() \u003e 1) {\n Log.d(DEBUG_TAG,\"Multitouch event\");\n // The coordinates of the current screen contact, relative to\n // the responding View or Activity.\n xPos = (int)MotionEventCompat.getX(event, index);\n yPos = (int)MotionEventCompat.getY(event, index);\n\n} else {\n // Single touch event.\n Log.d(DEBUG_TAG,\"Single touch event\");\n xPos = (int)MotionEventCompat.getX(event, index);\n yPos = (int)MotionEventCompat.getY(event, index);\n}\n...\n\n// Given an action int, returns a string description\npublic static String actionToString(int action) {\n switch (action) {\n\n case MotionEvent.ACTION_DOWN: return \"Down\";\n\tcase MotionEvent.ACTION_MOVE: return \"Move\";\n\tcase MotionEvent.ACTION_POINTER_DOWN: return \"Pointer Down\";\n\tcase MotionEvent.ACTION_UP: return \"Up\";\n\tcase MotionEvent.ACTION_POINTER_UP: return \"Pointer Up\";\n\tcase MotionEvent.ACTION_OUTSIDE: return \"Outside\";\n\tcase MotionEvent.ACTION_CANCEL: return \"Cancel\";\n }\n return \"\";\n}\n```\n**Figure 1.** Multi-touch drawing patterns.\n\nAdditional resources\n--------------------\n\nFor more information related to input events, see the following\nreferences:\n\n- [Input events overview](/guide/topics/ui/ui-events)\n- [Sensors\n overview](/guide/topics/sensors/sensors_overview)\n- [Make a custom\n view interactive](/training/custom-views/making-interactive)\n- [Drag and scale](/develop/ui/views/touch-and-input/gestures/scale)"]]