זיהוי תנועות נפוצות

רוצה לנסות את שיטת הכתיבה?
'Jetpack פיתוח נייטיב' היא ערכת הכלים המומלצת לממשק המשתמש ל-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 גרפי. 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;
        }
    }
}

מקורות מידע נוספים