จัดการการแตะสัมผัสหลายแบบ

ลองใช้วิธีเขียน
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนําสําหรับ 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 จัดเก็บข้อมูลเคอร์เซอร์ในอาร์เรย์ ดัชนีของพอยน์เตอร์คือตําแหน่งของพอยน์เตอร์ภายในอาร์เรย์นี้ เมธอด 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 getActionMasked() ออกแบบมาเพื่อใช้กับเคอร์เซอร์หลายรายการ ซึ่งต่างจากเมธอด getAction() ก่อนหน้านี้ โดยจะแสดงผลการดำเนินการโดยไม่มีอินเด็กซ์เคอร์เซอร์ สําหรับการดําเนินการที่มีดัชนีเคอร์เซอร์ที่ถูกต้อง ให้ใช้ 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 รูปแบบการวาดแบบมัลติทัช

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับเหตุการณ์อินพุตได้ที่ข้อมูลอ้างอิงต่อไปนี้