试用 Compose 方式
Jetpack Compose 是推荐在 Android 设备上使用的界面工具包。了解如何在 Compose 中使用触控和输入功能。
手势 →
本课介绍了如何跟踪触摸事件中的移动操作。
一种新的
onTouchEvent()
触发
ACTION_MOVE事件
只要当前触摸接触位置、压力或大小发生变化。如
检测常用手势中的说明,所有
这些事件都会记录在
MotionEvent 参数(属于
onTouchEvent()。
因为基于手指的触摸并不总是最精确的互动形式,
触摸事件的检测通常更基于移动,而非简单接触。
帮助应用区分基于移动的手势(例如滑动)和
非移动手势(例如点按一次),Android 包含
Touch slop。Touch slop 是指用户可以触摸的距离(以像素为单位)
在手势被解释为基于移动的手势之前四处移动。有关
请参阅管理
ViewGroup。
您可以通过多种方式跟踪手势的移动情况,具体取决于
应用的需求示例如下:
指针的起始位置和结束位置,例如在屏幕上移动某个对象
将对象从 A 点指向 B 点。
指针的行进方向,由 X 和 Y 确定
坐标。
历史事件。可通过调用
MotionEvent 种方式
getHistorySize()。
然后,您可以获取每个对象的位置、大小、时间和压力
使用动作事件的
getHistorical
方法。当呈现用户手指的轨迹时,历史记录会非常有用,
和触摸绘图一样如需了解详情,请参阅 MotionEvent 参考文档。
指针在触摸屏上移动时的速度。
请参阅以下相关资源:
输入事件概览
传感器概览
将自定义视图设为互动式
跟踪速度
您可以执行基于距离或方向的移动手势
指针移动不过,速度通常是跟踪
或决定手势是否发生。为了让
速度计算变得更简单,Android 提供了
VelocityTracker 类。
VelocityTracker 可帮助您跟踪触摸事件的速度。有用
表示速度是手势标准的一部分的手势,例如
快速滑动
以下示例说明了
VelocityTracker API:
Kotlin
private const val DEBUG_TAG = "Velocity"
class MainActivity : Activity() {
private var mVelocityTracker: VelocityTracker? = null
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
// Reset the velocity tracker back to its initial state.
mVelocityTracker?.clear()
// If necessary, retrieve a new VelocityTracker object to watch
// the velocity of a motion.
mVelocityTracker = mVelocityTracker ?: VelocityTracker.obtain()
// Add a user's movement to the tracker.
mVelocityTracker?.addMovement(event)
}
MotionEvent.ACTION_MOVE -> {
mVelocityTracker?.apply {
val pointerId: Int = event.getPointerId(event.actionIndex)
addMovement(event)
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then, call getXVelocity() and
// getYVelocity() to retrieve the velocity for each pointer
// ID.
computeCurrentVelocity(1000)
// Log velocity of pixels per second. It's best practice to
// use VelocityTrackerCompat where possible.
Log.d("", "X velocity: ${getXVelocity(pointerId)}")
Log.d("", "Y velocity: ${getYVelocity(pointerId)}")
}
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker?.recycle()
mVelocityTracker = null
}
}
return true
}
}
Java
public class MainActivity extends Activity {
private static final String DEBUG_TAG = "Velocity";
...
private VelocityTracker mVelocityTracker = null;
@Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
int pointerId = event.getPointerId(index);
switch(action) {
case MotionEvent.ACTION_DOWN:
if(mVelocityTracker == null) {
// Retrieve a new VelocityTracker object to watch the
// velocity of a motion.
mVelocityTracker = VelocityTracker.obtain();
}
else {
// Reset the velocity tracker back to its initial state.
mVelocityTracker.clear();
}
// Add a user's movement to the tracker.
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.addMovement(event);
// When you want to determine the velocity, call
// computeCurrentVelocity(). Then call getXVelocity() and
// getYVelocity() to retrieve the velocity for each pointer ID.
mVelocityTracker.computeCurrentVelocity(1000);
// Log velocity of pixels per second. It's best practice to use
// VelocityTrackerCompat where possible.
Log.d("", "X velocity: " + mVelocityTracker.getXVelocity(pointerId));
Log.d("", "Y velocity: " + mVelocityTracker.getYVelocity(pointerId));
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
// Return a VelocityTracker object back to be re-used by others.
mVelocityTracker.recycle();
break;
}
return true;
}
}
注意 :计算 ACTION_MOVE 事件发生后(而不是在发生后)的速度
ACTION_UP。在
ACTION_UP 时,X 速度和 Y 速度为 0。
使用指针捕获
有些应用(例如游戏、远程桌面和虚拟化客户端)会受益
对鼠标指针的控制指针捕获是一项功能
Android 8.0(API 级别 26)及更高版本中提供的功能,它通过
将所有鼠标事件传递到应用中的聚焦视图。
请求指针捕获
应用中的视图只有在
包含它的焦点因此,当存在
用户执行特定操作时,例如在
onClick()
或
onWindowFocusChanged()
事件处理脚本。
要请求指针捕获,请调用
requestPointerCapture()
方法。以下代码示例展示了如何请求指针
用户点击视图时触发的事件:
Kotlin
fun onClick(view: View) {
view.requestPointerCapture()
}
Java
@Override
public void onClick(View view) {
view.requestPointerCapture();
}
在捕获指针的请求成功后,Android 会调用
onPointerCaptureChange(true)。
只要满足以下条件,系统就会将鼠标事件传递到应用中聚焦的视图:
它与请求捕获的视图位于同一视图层次结构中。其他
应用会停止接收鼠标事件,直到捕获释放为止,这些事件包括
ACTION_OUTSIDE
事件。Android 将鼠标以外来源的指针事件作为
正常,但鼠标指针不再可见。
处理捕获的指针事件
在视图成功获取指针捕获后,Android 会提供
鼠标事件。聚焦的视图可以通过执行以下其中一项操作来处理事件:
以下任务:
如果您使用的是自定义视图,请替换
onCapturedPointerEvent(MotionEvent)。
否则,请注册
OnCapturedPointerListener。
以下代码示例展示了如何实现
onCapturedPointerEvent(MotionEvent):
Kotlin
override fun onCapturedPointerEvent(motionEvent: MotionEvent): Boolean {
// Get the coordinates required by your app.
val verticalOffset: Float = motionEvent.y
// Use the coordinates to update your view and return true if the event is
// successfully processed.
return true
}
Java
@Override
public boolean onCapturedPointerEvent(MotionEvent motionEvent) {
// Get the coordinates required by your app.
float verticalOffset = motionEvent.getY();
// Use the coordinates to update your view and return true if the event is
// successfully processed.
return true;
}
以下代码示例展示了如何注册
OnCapturedPointerListener:
Kotlin
myView.setOnCapturedPointerListener { view, motionEvent ->
// Get the coordinates required by your app.
val horizontalOffset: Float = motionEvent.x
// Use the coordinates to update your view and return true if the event is
// successfully processed.
true
}
Java
myView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
@Override
public boolean onCapturedPointer (View view, MotionEvent motionEvent) {
// Get the coordinates required by your app.
float horizontalOffset = motionEvent.getX();
// Use the coordinates to update your view and return true if the event is
// successfully processed.
return true;
}
});
无论您是使用自定义视图还是注册监听器,您的视图都会收到
MotionEvent,使用指定相对移动的指针坐标,例如 X
或 Y 增量,类似于轨迹球设备传递的坐标。您可以
使用
getX()和
getY()。
释放捕获的指针
应用中的视图可以通过调用
releasePointerCapture()、
如以下代码示例所示:
Kotlin
override fun onClick(view: View) {
view.releasePointerCapture()
}
Java
@Override
public void onClick(View view) {
view.releasePointerCapture();
}
在您未明确指明的情况下,系统可能会将捕获的画面从视图中移除
调用 releasePointerCapture(),这通常是因为视图层次结构
包含请求捕获的视图会失去焦点。