یک حرکت لمسی زمانی رخ میدهد که کاربر یک یا چند انگشت خود را روی صفحه لمسی قرار میدهد و برنامه شما این الگوی لمس را به عنوان یک حرکت تفسیر میکند. تشخیص حرکت دو مرحله دارد:
- جمعآوری دادههای رویداد لمسی
- تفسیر دادهها برای تعیین اینکه آیا معیارهای مربوط به حرکاتی که برنامه شما پشتیبانی میکند را برآورده میکنند یا خیر.
کلاسهای AndroidX
مثالهای این سند از کلاسهای GestureDetectorCompat و MotionEventCompat استفاده میکنند. این کلاسها در کتابخانه AndroidX قرار دارند. در صورت امکان از کلاسهای AndroidX برای سازگاری با دستگاههای قدیمیتر استفاده کنید. MotionEventCompat جایگزینی برای کلاس MotionEvent نیست . در عوض، متدهای کاربردی استاتیکی ارائه میدهد که شما شیء MotionEvent خود را برای دریافت اکشن مرتبط با آن رویداد به آنها ارسال میکنید.
جمعآوری دادهها
وقتی کاربر یک یا چند انگشت خود را روی صفحه نمایش قرار میدهد، این کار باعث فراخوانی تابع onTouchEvent() در view میشود که رویدادهای لمسی را دریافت میکند. برای هر دنباله از رویدادهای لمسی - مانند موقعیت، فشار، اندازه و اضافه کردن انگشت دیگر - که به عنوان یک حرکت شناسایی میشود، onTouchEvent() چندین بار اجرا میشود.
این حرکت از زمانی شروع میشود که کاربر برای اولین بار صفحه را لمس میکند، با ردیابی موقعیت انگشت یا انگشتان کاربر توسط سیستم ادامه مییابد و با ثبت رویداد نهایی خروج آخرین انگشت کاربر از صفحه نمایش به پایان میرسد. در طول این تعامل، MotionEvent که به onTouchEvent() تحویل داده میشود، جزئیات هر تعامل را ارائه میدهد. برنامه شما میتواند از دادههای ارائه شده توسط MotionEvent برای تعیین اینکه آیا حرکتی که برایش مهم است اتفاق میافتد یا خیر، استفاده کند.
ثبت رویدادهای لمسی برای یک فعالیت یا نمایش
برای رهگیری رویدادهای لمسی در یک Activity یا View ، تابع فراخوانی onTouchEvent() را بازنویسی کنید.
قطعه کد زیر از getAction() برای استخراج عملی که کاربر انجام میدهد از پارامتر event استفاده میکند. این به شما دادههای خامی را میدهد که برای تعیین اینکه آیا حرکتی که برایتان مهم است رخ میدهد یا خیر، نیاز دارید.
کاتلین
class MainActivity : Activity() { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { return when (event.action) { MotionEvent.ACTION_DOWN -> { Log.d(DEBUG_TAG, "Action was DOWN") true } MotionEvent.ACTION_MOVE -> { Log.d(DEBUG_TAG, "Action was MOVE") true } MotionEvent.ACTION_UP -> { Log.d(DEBUG_TAG, "Action was UP") true } MotionEvent.ACTION_CANCEL -> { Log.d(DEBUG_TAG, "Action was CANCEL") true } MotionEvent.ACTION_OUTSIDE -> { Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element") true } else -> super.onTouchEvent(event) } } }
جاوا
public class MainActivity extends Activity { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()) { case (MotionEvent.ACTION_DOWN) : Log.d(DEBUG_TAG,"Action was DOWN"); return true; case (MotionEvent.ACTION_MOVE) : Log.d(DEBUG_TAG,"Action was MOVE"); return true; case (MotionEvent.ACTION_UP) : Log.d(DEBUG_TAG,"Action was UP"); return true; case (MotionEvent.ACTION_CANCEL) : Log.d(DEBUG_TAG,"Action was CANCEL"); return true; case (MotionEvent.ACTION_OUTSIDE) : Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element"); return true; default : return super.onTouchEvent(event); } }
این کد با ضربه زدن، لمس کردن و نگه داشتن و کشیدن کاربر، پیامهایی مانند پیامهای زیر را در Logcat تولید میکند:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
برای حرکات سفارشی، میتوانید پردازش خودتان را روی این رویدادها انجام دهید تا مشخص شود که آیا آنها نمایانگر حرکتی هستند که باید مدیریت کنید یا خیر. با این حال، اگر برنامه شما از حرکات رایج مانند دوبار ضربه زدن، لمس و نگه داشتن، پرتاب کردن و غیره استفاده میکند، میتوانید از کلاس GestureDetector بهره ببرید. GestureDetector تشخیص حرکات رایج را بدون پردازش رویدادهای لمسی جداگانه برای شما آسانتر میکند. این موضوع در بخش تشخیص حرکات بیشتر مورد بحث قرار گرفته است.
رویدادهای لمسی را برای یک نمای واحد ضبط کنید
به عنوان جایگزینی برای onTouchEvent() ، میتوانید با استفاده از متد setOnTouchListener() یک شیء View.OnTouchListener را به هر شیء View متصل کنید. این کار امکان گوش دادن به رویدادهای لمسی را بدون زیرکلاسسازی یک View موجود فراهم میکند، همانطور که در مثال زیر نشان داده شده است:
کاتلین
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // Respond to touch events. true }
جاوا
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // Respond to touch events. return true; } });
مراقب ایجاد شنوندهای باشید که برای رویداد ACTION_DOWN false برمیگرداند. اگر این کار را انجام دهید، شنونده برای توالی رویدادهای ACTION_MOVE و ACTION_UP بعدی فراخوانی نمیشود. دلیل این امر این است که ACTION_DOWN نقطه شروع همه رویدادهای لمسی است.
اگر در حال ایجاد یک نمای سفارشی هستید، میتوانید onTouchEvent() همانطور که قبلاً توضیح داده شد، بازنویسی کنید.
تشخیص حرکات
اندروید کلاس GestureDetector را برای تشخیص حرکات رایج ارائه میدهد. برخی از حرکاتی که پشتیبانی میکند عبارتند از onDown() ، onLongPress() و onFling() . میتوانید GestureDetector همراه با متد onTouchEvent() که قبلاً توضیح داده شد، استفاده کنید.
تشخیص تمام حرکات پشتیبانی شده
وقتی یک شیء GestureDetectorCompat نمونهسازی میکنید، یکی از پارامترهایی که میگیرد، کلاسی است که رابط GestureDetector.OnGestureListener را پیادهسازی میکند. GestureDetector.OnGestureListener هنگام وقوع یک رویداد لمسی خاص به کاربران اطلاع میدهد. برای اینکه شیء GestureDetector شما بتواند رویدادها را دریافت کند، متد onTouchEvent() مربوط به view یا activity را بازنویسی کنید و تمام رویدادهای مشاهده شده را به نمونه detector ارسال کنید.
در قطعه کد زیر، مقدار بازگشتی true از متدهای on <TouchEvent> نشان میدهد که رویداد لمس مدیریت شده است. مقدار بازگشتی false رویدادها را تا زمانی که لمس با موفقیت مدیریت شود، از طریق پشته نما (view stack) به پایین منتقل میکند.
اگر قطعه کد زیر را در یک برنامه آزمایشی اجرا کنید، میتوانید نحوهی اجرای اکشنها هنگام تعامل با صفحه لمسی و محتوای MotionEvent برای هر رویداد لمسی را درک کنید. سپس خواهید دید که برای تعاملات ساده چه مقدار داده تولید میشود.
کاتلین
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private lateinit var mDetector: GestureDetectorCompat // Called when the activity is first created. public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = GestureDetectorCompat(this, this) // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this) } override fun onTouchEvent(event: MotionEvent): Boolean { return if (mDetector.onTouchEvent(event)) { true } else { super.onTouchEvent(event) } } override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } override fun onLongPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onLongPress: $event") } override fun onScroll( event1: MotionEvent, event2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { Log.d(DEBUG_TAG, "onScroll: $event1 $event2") return true } override fun onShowPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onShowPress: $event") } override fun onSingleTapUp(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapUp: $event") return true } override fun onDoubleTap(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTap: $event") return true } override fun onDoubleTapEvent(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTapEvent: $event") return true } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event") return true } }
جاوا
public class MainActivity extends Activity implements GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener{ private static final String DEBUG_TAG = "Gestures"; private GestureDetectorCompat mDetector; // Called when the activity is first created. @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = new GestureDetectorCompat(this,this); // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } @Override public void onLongPress(MotionEvent event) { Log.d(DEBUG_TAG, "onLongPress: " + event.toString()); } @Override public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) { Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString()); return true; } @Override public void onShowPress(MotionEvent event) { Log.d(DEBUG_TAG, "onShowPress: " + event.toString()); } @Override public boolean onSingleTapUp(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString()); return true; } @Override public boolean onDoubleTap(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString()); return true; } @Override public boolean onDoubleTapEvent(MotionEvent event) { Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString()); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent event) { Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString()); return true; } }
تشخیص زیرمجموعهای از حرکات پشتیبانیشده
اگر فقط میخواهید چند حرکت را پردازش کنید، میتوانید به جای پیادهسازی رابط GestureDetector.SimpleOnGestureListener ، از GestureDetector.OnGestureListener ارثبری کنید.
GestureDetector.SimpleOnGestureListener با برگرداندن false برای همه متدهای on <TouchEvent> ، پیادهسازیای برای همه آنها فراهم میکند. این به شما امکان میدهد فقط متدهایی را که برایتان مهم هستند، بازنویسی کنید. برای مثال، قطعه کد زیر کلاسی ایجاد میکند که GestureDetector.SimpleOnGestureListener را ارثبری میکند و onFling() و onDown() را بازنویسی میکند.
چه از GestureDetector.OnGestureListener استفاده کنید و چه GestureDetector.SimpleOnGestureListener ، بهترین روش این است که یک متد onDown() پیادهسازی کنید که true را برمیگرداند. دلیل این امر آن است که همه حرکات با یک پیام onDown() شروع میشوند. اگر از onDown() false را برگردانید، همانطور که GestureDetector.SimpleOnGestureListener به طور پیشفرض انجام میدهد، سیستم فرض میکند که شما میخواهید بقیه حرکت را نادیده بگیرید و سایر متدهای GestureDetector.OnGestureListener فراخوانی نمیشوند. این ممکن است باعث مشکلات غیرمنتظرهای در برنامه شما شود. فقط در صورتی از onDown() false را برگردانید که واقعاً میخواهید کل یک حرکت را نادیده بگیرید.
کاتلین
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity() { private lateinit var mDetector: GestureDetectorCompat public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mDetector = GestureDetectorCompat(this, MyGestureListener()) } override fun onTouchEvent(event: MotionEvent): Boolean { mDetector.onTouchEvent(event) return super.onTouchEvent(event) } private class MyGestureListener : GestureDetector.SimpleOnGestureListener() { override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } } }
جاوا
public class MainActivity extends Activity { private GestureDetectorCompat mDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDetector = new GestureDetectorCompat(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } class MyGestureListener extends GestureDetector.SimpleOnGestureListener { private static final String DEBUG_TAG = "Gestures"; @Override public boolean onDown(MotionEvent event) { Log.d(DEBUG_TAG,"onDown: " + event.toString()); return true; } @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString()); return true; } } }
