رصد الإيماءات الشائعة

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

تحدث إيماءة اللمس عندما يضع المستخدم إصبعًا واحدًا أو أكثر على شاشة تعمل باللمس ويفسر تطبيقك نمط اللمس هذا كإيماءة. هناك مرحلتان لاكتشاف الإيماءات:

  1. جارٍ جمع بيانات أحداث اللمس.
  2. يعني تفسير البيانات ما إذا كانت تفي بمعايير الإيماءات التي يدعمها تطبيقك.

صفوف 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 من واجهة pyplot. يُرسِل "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" من واجهة pyplot.

يوفّر 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;
        }
    }
}

مصادر إضافية