تحدث إيماءة اللمس عندما يضع المستخدم إصبعًا واحدًا أو أكثر على الشاشة التي تعمل باللمس ويفسر تطبيقك نمط اللمسات هذه على أنّها إيماءة. هناك مرحلتان لاكتشاف الإيماءات:
- جارٍ جمع بيانات أحداث اللمس.
- تفسير البيانات لتحديد ما إذا كانت تستوفي معايير الإيماءات التي يتيحها تطبيقك
صفوف AndroidX
تستخدم الأمثلة في هذا المستند
الفئتَين GestureDetectorCompat
وMotionEventCompat
. تتوفر هذه الفئات في مكتبة
AndroidX. استخدِم فئات AndroidX متى أمكن ذلك لضمان التوافق مع الأجهزة السابقة.
MotionEventCompat
هو ليس بديل للفئة
MotionEvent
. بل توفّر بدلاً من ذلك طرق الأداة المساعدة الثابتة التي يمكنك من خلالها تمرير كائن MotionEvent
لتلقّي الإجراء المرتبط بذلك الحدث.
جمع البيانات
عندما يضع المستخدم إصبعًا واحدًا أو أكثر على الشاشة، يؤدي ذلك إلى إعادة الاتصال
onTouchEvent()
في طريقة العرض التي تتلقّى أحداث اللمس. يتم تنشيط onTouchEvent()
عدة مرات لكل تسلسل من أحداث اللمس، مثل الموضع والضغط والحجم وإضافة إصبع آخر.
تبدأ الإيماءة عندما يلمس المستخدم الشاشة لأول مرة، وتستمر بينما
يتتبع النظام موضع إصبع المستخدم أو أصابعه، وتنتهي بالتقاط الحدث الأخير الذي يخرج من إصبع المستخدم من الشاشة.
خلال عملية التواصل هذه، توفّر لك MotionEvent
التي يتم إرسالها إلى
"onTouchEvent()
" تفاصيل كل تفاعل. يمكن لتطبيقك
استخدام البيانات التي تقدّمها MotionEvent
لتحديد ما إذا كانت إيماءة يهتم بها أم لا.
تسجيل أحداث اللمس لنشاط أو عرض
لاعتراض أحداث اللمس في Activity
أو View
، يمكنك إلغاء معاودة الاتصال onTouchEvent()
.
يستخدم مقتطف الرمز التالي getAction()
لاستخراج الإجراء الذي ينفّذه المستخدم من المعلَمة event
.
يمنحك هذا البيانات الأولية التي تحتاجها لتحديد ما إذا كانت الإيماءة التي
تهتم بها ستحدث أم لا.
Kotlin
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) } } }
Java
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()
، يمكنك إرفاق كائن
View.OnTouchListener
بأي كائن View
باستخدام الطريقة
setOnTouchListener()
. ويتيح ذلك الاستماع إلى أحداث اللمس بدون تصنيف View
حالي، كما هو موضّح في المثال التالي:
Kotlin
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // Respond to touch events. true }
Java
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // Respond to touch events. return true; } });
يجب توخّي الحذر من إنشاء أداة معالجة تعليقات تعرض رمز الاستجابة false
لحدث ACTION_DOWN
.
إذا فعلت ذلك، لن يتم استدعاء المستمع للتسلسل اللاحق
لأحداث ACTION_MOVE
وACTION_UP
. ويرجع ذلك إلى أنّ ACTION_DOWN
هي نقطة البداية لجميع
أحداث اللمس.
وإذا كنت تنشئ طريقة عرض مخصّصة، يمكنك إلغاء onTouchEvent()
كما هو موضّح سابقًا.
رصد الإيماءات
يوفّر Android الفئة GestureDetector
لرصد الإيماءات
الشائعة. وتتضمّن بعض الإيماءات المتوافقة
onDown()
وonLongPress()
وonFling()
.
يمكنك استخدام GestureDetector
مع طريقة
onTouchEvent()
الموضّحة سابقًا.
رصد كل الإيماءات المتوافقة
عند إنشاء مثيل كائن GestureDetectorCompat
، تتمثل إحدى المعلمات التي يتم استخدامها في فئة تنفذ واجهة GestureDetector.OnGestureListener
. يرسل GestureDetector.OnGestureListener
إشعارًا إلى المستخدمين عند
حدوث حدث لمس معيّن. لتمكين كائن GestureDetector
من تلقّي الأحداث، عليك إلغاء طريقة onTouchEvent()
الخاصة بالملف الشخصي أو النشاط، وتمرير جميع الأحداث المرصودة إلى مثيل أداة الرصد.
في المقتطف التالي، تشير القيمة المعروضة true
من طرق on<TouchEvent>
الفردية إلى أنّه تم التعامل مع حدث اللمس. وتمرر القيمة التي تعرضها false
الأحداث لأسفل
عبر حزمة طرق العرض إلى أن يتم التعامل مع اللمسة بنجاح.
إذا شغّلت المقتطف التالي في تطبيق اختباري، يمكنك التعرّف على كيفية
تنفيذ الإجراءات عند التفاعل مع الشاشة التي تعمل باللمس
ومحتوى MotionEvent
لكل حدث لمس. سترى بعد ذلك مقدار البيانات التي
يتم إنشاؤها للتفاعلات البسيطة.
Kotlin
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 } }
Java
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
عملية تنفيذ لجميع طُرق on<TouchEvent>
من خلال عرض false
لها جميعًا. يتيح لك هذا إلغاء الأساليب
التي تهمك فقط. على سبيل المثال، ينشئ مقتطف الرمز التالي فئة تمتد إلى GestureDetector.SimpleOnGestureListener
وتلغي الترميزَين onFling()
وonDown()
.
سواء كنت تستخدم GestureDetector.OnGestureListener
أو
GestureDetector.SimpleOnGestureListener
، من أفضل الممارسات
تنفيذ طريقة onDown()
التي تعرض true
. ويعود السبب في ذلك إلى أنّ كل الإيماءات تبدأ برسالة onDown()
. إذا أعدت
false
من onDown()
، كما تفعل
GestureDetector.SimpleOnGestureListener
تلقائيًا، يفترض النظام
أنك تريد تجاهل بقية الإيماءة، وأن طرق
GestureDetector.OnGestureListener
الأخرى لا يتم استدعاءها. قد يتسبب ذلك في حدوث
مشاكل غير متوقَّعة في تطبيقك. يُرجى عدم عرض الرمز false
من
onDown()
إلا إذا كنت تريد تجاهل إيماءة كاملة.
Kotlin
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 } } }
Java
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; } } }