یک حرکت چند لمسی زمانی است که چندین اشاره گر (انگشت) به طور همزمان روی صفحه ضربه می زنند. این سند نحوه تشخیص حرکاتی که شامل چندین اشاره گر هستند را توضیح می دهد.
چندین اشاره گر را ردیابی کنید
هنگامی که چندین اشاره گر به طور همزمان روی صفحه ضربه می زنند، سیستم رویدادهای لمسی زیر را ایجاد می کند:
ACTION_DOWN : زمانی که اولین اشاره گر روی صفحه ضربه می زند ارسال می شود. این ژست را شروع می کند. داده های اشاره گر برای این اشاره گر همیشه در شاخص 0 در MotionEvent است.
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 میتواند حرکتی را برای همه نشانگرها به پایین در آن لحظه نشان دهد.
نشانگرها را پیگیری کنید
از نمایه و شناسه اشاره گر برای پیگیری موقعیت های تک تک نشانگرها در یک MotionEvent استفاده کنید.
Index : یک MotionEvent اطلاعات اشاره گر را در یک آرایه ذخیره می کند. شاخص یک اشاره گر موقعیت آن در این آرایه است. اکثر متدهای MotionEvent به جای شناسه اشاره گر، شاخص اشاره گر را به عنوان پارامتر در نظر می گیرند.
شناسه : هر اشاره گر همچنین دارای یک نقشه شناسه است که در رویدادهای لمسی پایدار می ماند تا امکان ردیابی یک اشاره گر جداگانه در کل ژست را فراهم کند.
نشانگرهای منفرد در یک رویداد حرکتی به ترتیب نامشخص ظاهر می شوند. بنابراین، شاخص یک اشاره گر می تواند از یک رویداد به رویداد بعدی تغییر کند، اما شناسه اشاره گر یک اشاره گر تضمین می شود که تا زمانی که اشاره گر فعال است، ثابت بماند. از متد getPointerId() برای به دست آوردن شناسه اشاره گر برای ردیابی اشاره گر در تمام رویدادهای حرکتی بعدی در یک حرکت استفاده کنید. سپس، برای رویدادهای متوالی حرکت، از متد findPointerIndex() برای به دست آوردن شاخص اشاره گر برای شناسه اشاره گر معین در آن رویداد حرکتی استفاده کنید. به عنوان مثال:
کاتلین
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)}...}
جاوا
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);...}
برای پشتیبانی از چند نشانگر لمسی، میتوانید همه نشانگرهای فعال را با شناسههایشان در زمان رویداد جداگانه ACTION_POINTER_DOWN و ACTION_DOWN در حافظه پنهان ذخیره کنید. در رویدادهای ACTION_POINTER_UP و ACTION_UP نشانگرها را از حافظه پنهان خود حذف کنید. ممکن است این شناسه های ذخیره شده در حافظه پنهان برای مدیریت صحیح سایر رویدادهای اقدام مفید باشند. به عنوان مثال، هنگام پردازش یک رویداد ACTION_MOVE ، شاخص هر شناسه اشاره گر فعال ذخیره شده را پیدا کنید، مختصات اشاره گر را با استفاده از توابع getX() و getY() بازیابی کنید، سپس این مختصات را با مختصات حافظه پنهان خود مقایسه کنید تا متوجه شوید کدام نشانگر جابجا شده است.
از تابع getActionIndex() فقط با رویدادهای ACTION_POINTER_UP و ACTION_POINTER_DOWN استفاده کنید. از این تابع با رویدادهای ACTION_MOVE استفاده نکنید، زیرا همیشه 0 برمی گرداند.
بازیابی اقدامات MotionEvent
از متد getActionMasked() یا نسخه سازگاری MotionEventCompat.getActionMasked() برای بازیابی عملکرد یک MotionEvent استفاده کنید. برخلاف متد getAction() قبلی، getActionMasked() برای کار با چندین اشاره گر طراحی شده است. این عمل را بدون شاخص های اشاره گر برمی گرداند. برای اقداماتی که دارای شاخص اشاره گر معتبر هستند، از getActionIndex() برای برگرداندن ایندکس اشاره گرهای مرتبط با عمل همانطور که در قطعه زیر نشان داده شده است استفاده کنید:
کاتلین
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->""}}
جاوا
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"";}
شکل 1. الگوهای طراحی چند لمسی.
منابع اضافی
برای اطلاعات بیشتر در رابطه با رویدادهای ورودی، به مراجع زیر مراجعه کنید:
محتوا و نمونه کدها در این صفحه مشمول پروانههای توصیفشده در پروانه محتوا هستند. جاوا و OpenJDK علامتهای تجاری یا علامتهای تجاری ثبتشده Oracle و/یا وابستههای آن هستند.
تاریخ آخرین بهروزرسانی 2025-07-29 بهوقت ساعت هماهنگ جهانی.
[[["درک آسان","easyToUnderstand","thumb-up"],["مشکلم را برطرف کرد","solvedMyProblem","thumb-up"],["غیره","otherUp","thumb-up"]],[["اطلاعاتی که نیاز دارم وجود ندارد","missingTheInformationINeed","thumb-down"],["بیشازحد پیچیده/ مراحل بسیار زیاد","tooComplicatedTooManySteps","thumb-down"],["قدیمی","outOfDate","thumb-down"],["مشکل ترجمه","translationIssue","thumb-down"],["مشکل کد / نمونهها","samplesCodeIssue","thumb-down"],["غیره","otherDown","thumb-down"]],["تاریخ آخرین بهروزرسانی 2025-07-29 بهوقت ساعت هماهنگ جهانی."],[],[],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)"]]