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

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

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

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

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

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

מחוות התחלה וסיום

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

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

כדי לעקוב אחרי המיקומים של הסמנים בתוך MotionEvent, משתמשים באינדקס ובמזהה של הסמן.

  • אינדקס: מצביע MotionEvent stores למידע במערך. האינדקס של מצביע הוא המיקום שלו במערך הזה. רוב השיטות של 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() או בגרסת התאימות 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. שרטוט דפוסים באמצעות מולטי-טאץ'.

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

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