处理多点触控手势

处理多点触控手势

试试 Compose 方式

Jetpack Compose 是推荐用于 Android 的界面工具包。了解如何在 Compose 中使用触控和输入操作。

多点触控手势 →

多点触控手势是指多个指针(手指)同时点按屏幕。本文档介绍了如何检测涉及多个指针的手势。

跟踪多个指针

当多个指针同时点按屏幕时,系统会生成以下触摸事件:

ACTION_DOWN:当第一个指针点按屏幕时发送。这是手势的起点。此指针的指针数据始终位于 MotionEvent 中的索引 0 处。

ACTION_POINTER_DOWN:当第一个指针之后有其他指针进入屏幕时发送。您可以使用 getActionIndex() 获取刚刚向下移动的指针的索引。

ACTION_MOVE:当手势发生变化时发送,涉及任意数量的指针。

ACTION_POINTER_UP:当非主要指针抬起时发送。您可以使用 getActionIndex() 获取刚刚向上移动的指针的索引。

ACTION_UP:当最后一个指针离开屏幕时发送。

ACTION_CANCEL:表示取消整个手势,包括所有指针。

开始和结束手势

手势是一系列事件,以 ACTION_DOWN 事件开头,以 ACTION_UP 或 ACTION_CANCEL 事件结尾。一次只能有一个手势处于有效状态。向下、移动、向上和取消操作会应用于整个手势。例如,包含 ACTION_MOVE 的事件可以指示此时所有按下指针的移动。

跟踪指针

使用指针的索引和 ID 跟踪 MotionEvent 中的各个指针位置。

索引:MotionEvent 会在数组中存储指针信息。指针的索引就是指针在此数组中的位置。大多数 MotionEvent 方法都使用触控点索引(而非指针 ID)作为参数。

ID:每个指针还有一个 ID 映射,该映射在触摸事件之间保持不变,让您能够在整个手势中跟踪单个指针。

单个指针在动作事件中的显示顺序是未定义的。因此,不同事件中的触控点索引可能会不一样,但只要指针保持活动状态,其指针 ID 就会保持不变。您可以使用 getPointerId() 方法获取指针的 ID,并在手势中的所有后续动作事件中跟踪指针。然后,对于连续动作事件,可以使用 findPointerIndex() 方法获取给定指针 ID 在相应动作事件中的指针索引。例如:

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 事件时间缓存所有处于活动状态的指针及其 ID。在 ACTION_POINTER_UP 和 ACTION_UP 事件中从缓存中移除指针。这些缓存的 ID 可能有助于您正确处理其他操作事件。例如,在处理 ACTION_MOVE 事件时,查找每个缓存的活动指针 ID 的索引,使用 getX() 和 getY() 函数检索指针的坐标,然后将这些坐标与缓存的坐标进行比较,以确定哪些指针移动了。

请仅将 getActionIndex() 函数与 ACTION_POINTER_UP 和 ACTION_POINTER_DOWN 事件搭配使用。请勿将此函数与 ACTION_MOVE 事件搭配使用,因为它始终会返回 0。

检索 MotionEvent 操作

使用 getActionMasked() 方法或兼容版本 MotionEventCompat.getActionMasked() 检索 MotionEvent 的操作。与早期的 getAction() 方法不同,getActionMasked() 能够与多个指针一起使用。它会返回不含指针索引的操作。对于具有有效指针索引的操作,请使用 getActionIndex() 返回与操作相关联的指针的索引,如以下代码段所示:

注意:此示例使用的是 支持库中的 MotionEventCompat 类。使用 MotionEventCompat 为各种平台提供最佳支持。MotionEventCompat 不能替代 MotionEvent 类。相反,它可以提供静态实用程序方法,您可以向这些方法传递 MotionEvent 对象,以便接收与相应事件相关的预期操作。

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. 多点触控绘制模式。

其他资源

如需详细了解输入事件,请参阅以下参考文档:

输入事件概览

传感器概览

将自定义视图设为互动式

拖动和缩放

相关画作

LOL黯晶凤凰艾尼维亚皮肤价格及特效详解 稀有度、绝版情况
365bet手机网址是多少

LOL黯晶凤凰艾尼维亚皮肤价格及特效详解 稀有度、绝版情况

📅 07-01 👁️ 4916
微博手机如何悄悄关注他人?教你一个小妙招!
365bet手机网址是多少

微博手机如何悄悄关注他人?教你一个小妙招!

📅 10-09 👁️ 4030
华为荣耀电视盒子哪个最好?
365足球

华为荣耀电视盒子哪个最好?

📅 07-04 👁️ 6244