توضّح هذه الدرس كيفية تتبُّع الحركة في أحداث اللمس.
يتم تشغيل حدث
onTouchEvent()
جديد
مع حدث
ACTION_MOVE
عندما يتغيّر موضع أو ضغط أو حجم جهة اتصال اللمس الحالية. كما هو موضّح في رصد الإيماءات الشائعة، يتم تسجيل كل هذه الأحداث في المَعلمة MotionEvent الخاصة بالحدث onTouchEvent().
بما أنّ اللمس باستخدام الأصابع ليس دائمًا الشكل الأكثر دقة للتفاعل، فإنّ رصد أحداث اللمس يعتمد غالبًا على الحركة أكثر من مجرد الاتصال. لمساعدة التطبيقات في التمييز بين الإيماءات المستندة إلى الحركة (مثل التمرير السريع) والإيماءات غير المستندة إلى الحركة (مثل النقر مرة واحدة)، يتضمّن نظام التشغيل Android مفهوم ميل اللمس. يشير مصطلح "هامش الخطأ" إلى المسافة بالبكسل التي يمكن أن يتحرك فيها إصبع المستخدم قبل أن يتم تفسير الإيماءة على أنّها إيماءة مستندة إلى الحركة. لمزيد من المعلومات حول هذا الموضوع، يُرجى الاطّلاع على إدارة أحداث اللمس في ViewGroup.
تتوفّر عدة طرق لتتبُّع الحركة في الإيماءة، وذلك حسب احتياجات تطبيقك. في ما يلي بعض الأمثلة:
- موضعَي بداية ونهاية المؤشر، مثل تحريك عنصر على الشاشة من النقطة أ إلى النقطة ب
- تمثّل هذه السمة اتجاه حركة المؤشر، كما هو محدّد بواسطة الإحداثيات X وY.
- السجلّ. يمكنك العثور على حجم سجلّ الإيماءة من خلال استدعاء الطريقة
MotionEventgetHistorySize(). يمكنك بعد ذلك الحصول على مواضع وأحجام وأوقات وضغوط كل الأحداث السابقة باستخدام طرقgetHistorical<Value>حدث الحركة. يكون السجلّ مفيدًا عند عرض مسار إصبع المستخدم، مثل الرسم باللمس. يمكنك الاطّلاع علىMotionEventالمرجع للحصول على التفاصيل. - سرعة المؤشر أثناء تحرّكه على الشاشة التي تعمل باللمس
يُرجى الاطّلاع على المراجع ذات الصلة التالية:
سرعة التتبُّع
يمكنك استخدام إيماءة مستندة إلى الحركة تستند إلى المسافة أو الاتجاه الذي يتحرّك فيه المؤشر. ومع ذلك، غالبًا ما تكون السرعة عاملاً حاسمًا في تتبُّع خصائص الإيماءة أو تحديد ما إذا كانت قد حدثت. لتسهيل عملية حساب السرعة، يوفّر نظام التشغيل Android الفئة VelocityTracker.
تساعدك VelocityTracker في تتبُّع سرعة أحداث اللمس. ويكون ذلك مفيدًا
للإيماءات التي تشكّل السرعة جزءًا من معاييرها، مثل
الإيماءة السريعة.
في ما يلي مثال يوضّح الغرض من الطرق في واجهة برمجة التطبيقات VelocityTracker:
Kotlin
private const val DEBUG_TAG = "Velocity" class MainActivity : Activity() { private var mVelocityTracker: VelocityTracker? = null override fun onTouchEvent(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { // Reset the velocity tracker back to its initial state. mVelocityTracker?.clear() // If necessary, retrieve a new VelocityTracker object to watch // the velocity of a motion. mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain() // Add a user's movement to the tracker. mVelocityTracker?.addMovement(event) } MotionEvent.ACTION_MOVE -> { mVelocityTracker?.apply { val pointerId: Int = event.getPointerId(event.actionIndex) addMovement(event) // When you want to determine the velocity, call // computeCurrentVelocity(). Then, call getXVelocity() and // getYVelocity() to retrieve the velocity for each pointer // ID. computeCurrentVelocity(1000) // Log velocity of pixels per second. It's best practice to // use VelocityTrackerCompat where possible. Log.d("", "X velocity: ${getXVelocity(pointerId)}") Log.d("", "Y velocity: ${getYVelocity(pointerId)}") } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // Return a VelocityTracker object back to be re-used by others. mVelocityTracker?.recycle() mVelocityTracker = null } } return true } }
Java
public class MainActivity extends Activity { private static final String DEBUG_TAG = "Velocity"; ... private VelocityTracker mVelocityTracker = null; @Override public boolean onTouchEvent(MotionEvent event) { int index = event.getActionIndex(); int action = event.getActionMasked(); int pointerId = event.getPointerId(index); switch(action) { case MotionEvent.ACTION_DOWN: if(mVelocityTracker == null) { // Retrieve a new VelocityTracker object to watch the // velocity of a motion. mVelocityTracker = VelocityTracker.obtain(); } else { // Reset the velocity tracker back to its initial state. mVelocityTracker.clear(); } // Add a user's movement to the tracker. mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: mVelocityTracker.addMovement(event); // When you want to determine the velocity, call // computeCurrentVelocity(). Then call getXVelocity() and // getYVelocity() to retrieve the velocity for each pointer ID. mVelocityTracker.computeCurrentVelocity(1000); // Log velocity of pixels per second. It's best practice to use // VelocityTrackerCompat where possible. Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId)); Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: // Return a VelocityTracker object back to be re-used by others. mVelocityTracker.recycle(); break; } return true; } }
استخدام ميزة "التقاط المؤشر"
تستفيد بعض التطبيقات، مثل الألعاب وبرامج سطح المكتب البعيد وبرامج المحاكاة الافتراضية، من إمكانية التحكّم في مؤشر الماوس. ميزة "التقاط المؤشر" متوفّرة في نظام التشغيل Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات) والإصدارات الأحدث، وتوفّر هذا التحكّم من خلال إرسال جميع أحداث الماوس إلى طريقة عرض مركّزة في تطبيقك.
طلب التقاط المؤشر
لا يمكن لعنصر في تطبيقك طلب الحصول على إذن بالتقاط المؤشر إلا عندما تكون هيكلية طرق العرض التي تتضمّنه في وضع التركيز. لهذا السبب، اطلب التقاط المؤشر عند اتّخاذ المستخدم إجراءً معيّنًا على العرض، مثل أثناء حدث onClick() أو في معالج حدث onWindowFocusChanged() لنشاطك.
لطلب التقاط المؤشر، استدعِ طريقة
requestPointerCapture()
في العرض. يوضّح مثال الرمز البرمجي التالي كيفية طلب التقاط المؤشر عندما ينقر المستخدم على طريقة عرض:
Kotlin
fun onClick(view: View) { view.requestPointerCapture() }
Java
@Override public void onClick(View view) { view.requestPointerCapture(); }
بعد نجاح طلب تسجيل المؤشر، يستدعي نظام التشغيل Android الدالة
onPointerCaptureChange(true).
يقدّم النظام أحداث الماوس إلى العرض المركّز في تطبيقك طالما أنّه في هيكلية طرق العرض نفسها التي طلبت الالتقاط. تتوقف التطبيقات الأخرى عن تلقّي أحداث الماوس إلى أن يتم إيقاف عملية الالتقاط، بما في ذلك أحداث ACTION_OUTSIDE. يوفّر نظام التشغيل Android أحداث المؤشر من مصادر أخرى غير الماوس كالمعتاد، ولكن لم يعُد مؤشر الماوس مرئيًا.
التعامل مع أحداث المؤشر التي تم التقاطها
بعد أن يحصل العرض بنجاح على إذن استخدام مؤشر الماوس، يرسل نظام التشغيل Android أحداث الماوس. يمكن لطريقة العرض المركّزة التعامل مع الأحداث من خلال تنفيذ إحدى المهام التالية:
- إذا كنت تستخدم طريقة عرض مخصّصة، عليك تجاهل
onCapturedPointerEvent(MotionEvent). - في الحالات الأخرى، يمكنك تسجيل
OnCapturedPointerListener.
يوضّح مثال الرمز البرمجي التالي كيفية تنفيذ onCapturedPointerEvent(MotionEvent):
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean { // Get the coordinates required by your app. val verticalOffset: Float = motionEvent.y // Use the coordinates to update your view and return true if the event is // successfully processed. return true }
Java
@Override public boolean onCapturedPointerEvent(MotionEvent motionEvent) { // Get the coordinates required by your app. float verticalOffset = motionEvent.getY(); // Use the coordinates to update your view and return true if the event is // successfully processed. return true; }
يوضّح مثال الرمز البرمجي التالي كيفية تسجيل OnCapturedPointerListener:
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent -> // Get the coordinates required by your app. val horizontalOffset: Float = motionEvent.x // Use the coordinates to update your view and return true if the event is // successfully processed. true }
Java
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() { @Override public boolean onCapturedPointer (View view, MotionEvent motionEvent) { // Get the coordinates required by your app. float horizontalOffset = motionEvent.getX(); // Use the coordinates to update your view and return true if the event is // successfully processed. return true; } });
سواء كنت تستخدم طريقة عرض مخصّصة أو تسجّل أداة معالجة، ستتلقّى طريقة العرض MotionEvent مع إحداثيات المؤشر التي تحدّد الحركات النسبية، مثل دلتا X أو Y، على غرار الإحداثيات التي يقدّمها جهاز كرة التتبّع. يمكنك استرداد الإحداثيات باستخدام getX() وgetY().
إلغاء التقاط المؤشر
يمكن للعرض في تطبيقك إيقاف ميزة "التقاط المؤشر" من خلال استدعاء releasePointerCapture()، كما هو موضّح في مثال الرمز البرمجي التالي:
Kotlin
override fun onClick(view: View) { view.releasePointerCapture() }
Java
@Override public void onClick(View view) { view.releasePointerCapture(); }
يمكن للنظام إزالة اللقطة من طريقة العرض بدون أن تستدعي releasePointerCapture() بشكل صريح، ويحدث ذلك عادةً لأنّ هيكلية طرق العرض التي تتضمّن طريقة العرض التي تطلب اللقطة تفقد التركيز.