Xử lý cử chỉ nhiều điểm nhấn

Thử cách Compose
Jetpack Compose là bộ công cụ giao diện người dùng được đề xuất cho Android. Tìm hiểu cách sử dụng thao tác chạm và nhập trong Compose.

Cử chỉ nhiều điểm chạm là khi nhiều con trỏ (ngón tay) nhấn vào màn hình cùng một lúc. Tài liệu này mô tả cách phát hiện các cử chỉ liên quan đến nhiều con trỏ.

Theo dõi nhiều con trỏ

Khi nhiều con trỏ chạm vào màn hình cùng lúc, hệ thống sẽ tạo ra các sự kiện chạm sau:

  • ACTION_DOWN: được gửi khi con trỏ đầu tiên chạm vào màn hình. Thao tác này sẽ bắt đầu cử chỉ. Dữ liệu con trỏ cho con trỏ này luôn ở chỉ mục 0 trong MotionEvent.
  • ACTION_POINTER_DOWN: được gửi khi các con trỏ bổ sung xuất hiện trên màn hình sau con trỏ đầu tiên. Bạn có thể lấy chỉ mục của con trỏ vừa đi xuống bằng cách sử dụng getActionIndex().
  • ACTION_MOVE: được gửi khi có thay đổi xảy ra trong một cử chỉ, liên quan đến bất kỳ số lượng con trỏ nào.
  • ACTION_POINTER_UP: được gửi khi một con trỏ không phải là con trỏ chính di chuyển lên. Bạn có thể lấy chỉ mục của con trỏ vừa di chuyển lên bằng cách dùng getActionIndex().
  • ACTION_UP: được gửi khi con trỏ cuối cùng rời khỏi màn hình.
  • ACTION_CANCEL: cho biết toàn bộ cử chỉ, bao gồm cả tất cả con trỏ, đều bị huỷ.

Cử chỉ bắt đầu và kết thúc

Cử chỉ là một chuỗi các sự kiện bắt đầu bằng sự kiện ACTION_DOWN và kết thúc bằng sự kiện ACTION_UP hoặc ACTION_CANCEL. Mỗi lần chỉ có một cử chỉ hoạt động. Các thao tác XUỐNG, DI CHUYỂN, LÊN và HUỶ áp dụng cho toàn bộ cử chỉ. Ví dụ: một sự kiện có ACTION_MOVE có thể cho biết một chuyển động cho tất cả các con trỏ xuống tại thời điểm đó.

Theo dõi các con trỏ

Sử dụng chỉ mục và mã nhận dạng của con trỏ để theo dõi vị trí của từng con trỏ trong một MotionEvent.

  • Index: MotionEvent lưu trữ thông tin con trỏ trong một mảng. Chỉ mục của con trỏ là vị trí của con trỏ trong mảng này. Hầu hết các phương thức MotionEvent đều lấy chỉ số con trỏ làm tham số, thay vì mã nhận dạng con trỏ.
  • Mã nhận dạng: mỗi con trỏ cũng có một ánh xạ mã nhận dạng duy trì liên tục trên các sự kiện chạm để cho phép theo dõi một con trỏ riêng lẻ trên toàn bộ cử chỉ.

Các con trỏ riêng lẻ xuất hiện trong một sự kiện chuyển động theo thứ tự không xác định. Do đó, chỉ mục của một con trỏ có thể thay đổi từ sự kiện này sang sự kiện tiếp theo, nhưng mã nhận dạng con trỏ của một con trỏ được đảm bảo vẫn không đổi miễn là con trỏ vẫn hoạt động. Sử dụng phương thức getPointerId() để lấy mã nhận dạng của con trỏ nhằm theo dõi con trỏ trong tất cả các sự kiện chuyển động tiếp theo trong một cử chỉ. Sau đó, đối với các sự kiện chuyển động liên tiếp, hãy sử dụng phương thức findPointerIndex() để lấy chỉ số con trỏ cho một mã nhận dạng con trỏ nhất định trong sự kiện chuyển động đó. Ví dụ:

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);
    ...
}

Để hỗ trợ nhiều con trỏ cảm ứng, bạn có thể lưu vào bộ nhớ đệm tất cả các con trỏ đang hoạt động cùng với mã nhận dạng của chúng tại thời gian diễn ra sự kiện ACTION_POINTER_DOWNACTION_DOWN riêng lẻ. Xoá con trỏ khỏi bộ nhớ đệm tại các sự kiện ACTION_POINTER_UPACTION_UP. Bạn có thể thấy những mã nhận dạng được lưu vào bộ nhớ đệm này hữu ích để xử lý chính xác các sự kiện hành động khác. Ví dụ: khi xử lý một sự kiện ACTION_MOVE, hãy tìm chỉ mục cho từng mã nhận dạng con trỏ đang hoạt động được lưu vào bộ nhớ đệm, truy xuất toạ độ của con trỏ bằng các hàm getX()getY(), sau đó so sánh các toạ độ này với toạ độ được lưu vào bộ nhớ đệm để biết con trỏ nào đã di chuyển.

Chỉ sử dụng hàm getActionIndex() với các sự kiện ACTION_POINTER_UPACTION_POINTER_DOWN. Không dùng hàm này với các sự kiện ACTION_MOVE, vì hàm này luôn trả về 0.

Truy xuất MotionEvent hành động

Sử dụng phương thức getActionMasked() hoặc phiên bản tương thích MotionEventCompat.getActionMasked() để truy xuất thao tác của một MotionEvent. Không giống như phương thức getAction() trước đó, getActionMasked() được thiết kế để hoạt động với nhiều con trỏ. Hàm này trả về thao tác mà không có chỉ mục con trỏ. Đối với các thao tác có chỉ số con trỏ hợp lệ, hãy dùng getActionIndex() để trả về chỉ số của các con trỏ được liên kết với thao tác như minh hoạ trong đoạn mã sau:

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 "";
}
Hình 1. Các mẫu vẽ đa điểm chạm.

Tài nguyên khác

Để biết thêm thông tin liên quan đến sự kiện đầu vào, hãy xem các tài liệu tham khảo sau: