تتبُّع حركات اللمس والمؤشر

تجربة طريقة الإنشاء
إنّ Jetpack Compose هي مجموعة أدوات واجهة المستخدم المقترَحة لنظام التشغيل Android. تعرَّف على كيفية استخدام اللمس والإدخال في ميزة "إنشاء".

يوضّح هذا الدرس كيفية تتبّع الحركة في أحداث اللمس.

يتم تشغيل onTouchEvent() جديد مع حدث ACTION_MOVE متى تغيّر موضع اللمس أو الضغط أو الحجم الحالي. كما هو موضّح في رصد الإيماءات الشائعة، يتم تسجيل كل هذه الأحداث في مَعلمة MotionEvent onTouchEvent().

نظرًا لأن اللمس بالإصبع ليس دائمًا الشكل الأكثر دقة للتفاعل، غالبًا ما يعتمد اكتشاف أحداث اللمس على الحركة أكثر من التلامس البسيط. لمساعدة التطبيقات في التفريق بين الإيماءات المستندة إلى الحركة (مثل التمرير السريع) والإيماءات غير المتحركة (مثل بنقرة واحدة)، يتضمّن Android مفهوم التمرير بدون تلامس الأجهزة. يشير انحدار اللمس إلى المسافة بالبكسل التي يمكن أن يتحرّكها المستخدم قبل أن يتم تفسير الإيماءة على أنها إيماءة تعتمد على الحركة. لمزيد من المعلومات حول هذا الموضوع، يُرجى الاطّلاع على إدارة أحداث اللمس في ViewGroup.

هناك عدة طرق لتتبع الحركة بإيماءة، حسب احتياجات تطبيقك. وفي ما يلي أمثلة على ذلك:

  • موضع بداية المؤشر ونهايته، مثل نقل كائن على الشاشة من النقطة "أ" إلى النقطة "ب".
  • الاتجاه الذي يتحرك فيه المؤشر، على النحو الذي يحدده الإحداثي "س" و"ص".
  • السجلّ. يمكنك معرفة حجم سجلّ الإيماءة من خلال استدعاء طريقة MotionEvent getHistorySize(). ويمكنك بعد ذلك الحصول على مواضع وأحجام ووقت وضغوط كل حدث من الأحداث السابقة باستخدام طُرق 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):

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() بشكل صريح، ويرجع ذلك عادةً إلى أنّ التسلسل الهرمي لطريقة العرض الذي يتضمّن طريقة العرض التي تطلب الالتقاط يفقد التركيز.