איך מטפלים בתנועות מולטי-טאץ'

רוצה לנסות את שיטת הכתיבה?
'Jetpack פיתוח נייטיב' היא ערכת הכלים המומלצת לממשק המשתמש ל-Android. מידע נוסף על שימוש בנגיעה ובקלט במהלך 'כתיבה'.

תנועה לכמה נקודות מגע מתרחשת כשכמה מצביעים (אצבעות) מקישים על המסך כמעט באותה עת. במסמך הזה מוסבר איך לזהות תנועות שכוללות כמה מצביעים.

מעקב אחרי כמה מצביעים

כשכמה מצביעים מקישים על המסך בו-זמנית, המערכת יוצרת אירועי המגע הבאים:

  • ACTION_DOWN: נשלחת כשהסמן הראשון יקיש על המסך. הפעולה הזו תתחיל את התנועה. נתוני מצביע עבור מצביע זה תמיד נמצאים באינדקס 0 ב- MotionEvent.
  • ACTION_POINTER_DOWN: נשלחת כשסמנים נוספים נכנסים למסך אחרי הראשון. אפשר לקבל האינדקס של הסמן שירד באמצעות getActionIndex()
  • ACTION_MOVE: נשלחים כששינוי מתרחש בתנועה, שכוללת כל מספר את המצביעים.
  • ACTION_POINTER_UP: נשלח כאשר מצביע שאינו עיקרי עולה. אפשר לקבל את האינדקס של מצביע בדיוק עלה באמצעות getActionIndex().
  • ACTION_UP: נשלח כשהסמן האחרון יוצא מהמסך.
  • ACTION_CANCEL: מציין שהתנועה כולה, כולל כל המצביעים, בוטלה.

תנועות של התחלה וסיום

תנועה היא סדרה של אירועים המתחילים ב-ACTION_DOWN אירוע ומסתיים בספרות ACTION_UP או אירוע ACTION_CANCEL. יש תנועה פעילה אחת בכל פעם. פעולות למטה, העברה, העלאה וביטול חלות על כל התנועה. לדוגמה, אירוע עם ACTION_MOVE יכול לציין תנועה בכל הסמן למטה באותו רגע.

מעקב אחר המצביעים

שימוש באינדקס ובמזהה של הסמן כדי לעקוב אחרי כל מצביע בנפרד מיקומים בטווח MotionEvent.

  • אינדקס: מצביע של MotionEvent לחנות מידע במערך. האינדקס של מצביע הוא המיקום שלו בתוך מערך. ברוב השיטות של MotionEvent, אינדקס המצביע מוגדר כ פרמטר ולא מזהה מצביע.
  • מזהה: לכל מצביע יש גם מיפוי מזהה שנשאר קבוע בכל אירועי המגע כדי לאפשר מעקב אחר מצביע ספציפי לכל התנועה.

מצביעים בודדים מופיעים בתוך אירוע תנועה בסדר לא מוגדר. כך, האינדקס של מצביע יכול להשתנות מאירוע אחד לאירוע הבא, אבל מזהה המצביע מובטח שיישאר קבוע כל עוד הוא נשאר פעיל. משתמשים ב getPointerId() לקבלת מזהה של מצביע אירועי תנועה בתנועה. לאחר מכן, לאירועי תנועה עוקבים, משתמשים findPointerIndex() לקבלת אינדקס מצביע עבור מזהה מצביע נתון באירוע התנועה הזה. לדוגמה:

Kotlin

private var mActivePointerId: Int = 0

override fun onTouchEvent(event: MotionEvent): Boolean {
    ...
    // Get the pointer ID.
    mActivePointerId = event.getPointerId(0)

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position.
    val (x: Float, y: Float) = event.findPointerIndex(mActivePointerId).let { pointerIndex ->
        // Get the pointer's current position.
        event.getX(pointerIndex) to event.getY(pointerIndex)
    }
    ...
}

Java

private int mActivePointerId;

public boolean onTouchEvent(MotionEvent event) {
    ...
    // Get the pointer ID.
    mActivePointerId = event.getPointerId(0);

    // ... Many touch events later...

    // Use the pointer ID to find the index of the active pointer
    // and fetch its position.
    int pointerIndex = event.findPointerIndex(mActivePointerId);
    // Get the pointer's current position.
    float x = event.getX(pointerIndex);
    float y = event.getY(pointerIndex);
    ...
}

כדי לתמוך בכמה מצביעים עם מגע, אפשר לשמור במטמון את כל המצביעים הפעילים באמצעות את התעודה המזהה בACTION_POINTER_DOWN וגם זמן האירוע: ACTION_DOWN. הסרת המצביעים מהמטמון ב- את אירועי ACTION_POINTER_UP ו-ACTION_UP שלהם. ייתכן ש מזהים שנשמרו במטמון שימושיים לטיפול באירועי פעולות אחרים בצורה נכונה. עבור לדוגמה, כשמעבדים אירוע ACTION_MOVE, מחפשים את האינדקס של כל מזהה של מצביע פעיל שנשמר במטמון, מאחזר את הקואורדינטות של המצביע באמצעות getX() וגם getY() שלנו, השוו בין הקואורדינטות האלה עם הקואורדינטות השמורות לגלות אילו מצביעים עברו.

משתמשים בפונקציה getActionIndex() עם אירועים ACTION_POINTER_UP ו-ACTION_POINTER_DOWN בלבד. לא להשתמש בפונקציה הזו עם אירועי ACTION_MOVE, כי הפונקציה תמיד מחזירה 0.

אחזור MotionEvent פעולות

משתמשים ב getActionMasked() method או את גרסת התאימות MotionEventCompat.getActionMasked() כדי לאחזר את הפעולה של MotionEvent. בניגוד לקודמים getAction() השיטה getActionMasked() תוכננה לפעול עם את המצביעים. היא מחזירה את הפעולה ללא האינדקסים של המצביעים. עבור פעולות עם רכיב אינדקס מצביע חוקי, צריך להשתמש ב-getActionIndex() כדי להחזיר את האינדקס של את המצביעים המשויכים לפעולה, כפי שמוצג בקטע הקוד הבא:

Kotlin

val (xPos: Int, yPos: Int) = MotionEventCompat.getActionMasked(event).let { action ->
    Log.d(DEBUG_TAG, "The action is ${actionToString(action)}")
    // Get the index of the pointer associated with the action.
    MotionEventCompat.getActionIndex(event).let { index ->
        // The coordinates of the current screen contact, relative to
        // the responding View or Activity.
        MotionEventCompat.getX(event, index).toInt() to MotionEventCompat.getY(event, index).toInt()
    }
}

if (event.pointerCount > 1) {
    Log.d(DEBUG_TAG, "Multitouch event")

} else {
    // Single touch event.
    Log.d(DEBUG_TAG, "Single touch event")
}

...

// Given an action int, returns a string description.
fun actionToString(action: Int): String {
    return when (action) {
        MotionEvent.ACTION_DOWN -> "Down"
        MotionEvent.ACTION_MOVE -> "Move"
        MotionEvent.ACTION_POINTER_DOWN -> "Pointer Down"
        MotionEvent.ACTION_UP -> "Up"
        MotionEvent.ACTION_POINTER_UP -> "Pointer Up"
        MotionEvent.ACTION_OUTSIDE -> "Outside"
        MotionEvent.ACTION_CANCEL -> "Cancel"
        else -> ""
    }
}

Java

int action = MotionEventCompat.getActionMasked(event);
// Get the index of the pointer associated with the action.
int index = MotionEventCompat.getActionIndex(event);
int xPos = -1;
int yPos = -1;

Log.d(DEBUG_TAG,"The action is " + actionToString(action));

if (event.getPointerCount() > 1) {
    Log.d(DEBUG_TAG,"Multitouch event");
    // The coordinates of the current screen contact, relative to
    // the responding View or Activity.
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);

} else {
    // Single touch event.
    Log.d(DEBUG_TAG,"Single touch event");
    xPos = (int)MotionEventCompat.getX(event, index);
    yPos = (int)MotionEventCompat.getY(event, index);
}
...

// Given an action int, returns a string description
public static String actionToString(int action) {
    switch (action) {

        case MotionEvent.ACTION_DOWN: return "Down";
	case MotionEvent.ACTION_MOVE: return "Move";
	case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
	case MotionEvent.ACTION_UP: return "Up";
	case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
	case MotionEvent.ACTION_OUTSIDE: return "Outside";
	case MotionEvent.ACTION_CANCEL: return "Cancel";
    }
    return "";
}
איור 1. ציור מולטי-טאץ' דפוסים.

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

מידע נוסף בנוגע לאירועי קלט מופיע במאמרים הבאים: הפניות: