本课介绍了如何跟踪触摸事件中的移动操作。
一种新的
onTouchEvent()
触发
ACTION_MOVE
事件
只要当前触摸接触位置、压力或大小发生变化。如
检测常用手势中的说明,所有
这些事件都会记录在
MotionEvent
参数(属于
onTouchEvent()
。
因为基于手指的触摸并不总是最精确的互动形式, 触摸事件的检测通常更基于移动,而非简单接触。 帮助应用区分基于移动的手势(例如滑动)和 非移动手势(例如点按一次),Android 包含 Touch slop。Touch slop 是指用户可以触摸的距离(以像素为单位) 在手势被解释为基于移动的手势之前四处移动。有关 请参阅管理 ViewGroup。
您可以通过多种方式跟踪手势的移动情况,具体取决于 应用的需求示例如下:
- 指针的起始位置和结束位置,例如在屏幕上移动某个对象 将对象从 A 点指向 B 点。
- 指针的行进方向,由 X 和 Y 确定 坐标。
- 历史事件。可通过调用
MotionEvent
种方式getHistorySize()
。 然后,您可以获取每个对象的位置、大小、时间和压力 使用动作事件的getHistorical<Value>
方法。当呈现用户手指的轨迹时,历史记录会非常有用, 和触摸绘图一样如需了解详情,请参阅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; } }
使用指针捕获
有些应用(例如游戏、远程桌面和虚拟化客户端)会受益 对鼠标指针的控制指针捕获是一项功能 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()
,这通常是因为视图层次结构
包含请求捕获的视图会失去焦点。