تحدث إيماءة اللمس عندما يضع المستخدم إصبعًا واحدًا أو أكثر على الشاشة التي تعمل باللمس ويفسّر تطبيقك نمط اللمس هذا على أنّه إيماءة. هناك مرحلتان لاكتشاف الإيماءات:
- جارٍ جمع بيانات أحداث اللمس.
- تفسير البيانات لتحديد ما إذا كانت تستوفي معايير الإيماءات التي يتيحها تطبيقك
صفوف 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; } } }