本课介绍了如何跟踪触摸事件中的移动操作。
由于基于手指的轻触并不总是最精确的互动形式,因此检测触摸事件时通常更注重移动操作,而不仅仅是接触。为了帮助应用区分移动类手势(例如滑动)和非移动手势(例如点按一次),Android 引入了“touch slop”的概念。Touch slop 是指在系统将手势解读为基于移动的手势之前,用户的轻触手势可以滑动的距离(以像素为单位)。有关此内容的详细讨论,请参阅在 ViewGroup 中管理轻触事件。
根据应用的需求,您可以通过多种不同的方式跟踪手势中的移动操作。例如:
指针的起始位置和结束位置(例如,将屏幕中的对象从 A 点移到 B 点)。
指针行进的方向(由 x 和 y 坐标确定)。
历史事件。您可以通过调用 getHistorical 方法获取每个历史事件的位置、大小、时间和压力。历史事件在呈现用户手指的轨迹(例如,针对轻触绘制)时非常有用。如需了解详情,请参阅
指针在触摸屏上移动时的速度。
请参阅以下相关资源:
跟踪速度
您可以设置单纯基于指针行进距离和/或方向的移动类手势。但是,速度通常是跟踪手势特征,甚至决定手势是否发生的决定因素。为了更方便地计算速度,Android 提供了
下面是一个简单的示例,说明了
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
// 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
// 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)及更高版本中提供的一项功能,可通过将所有鼠标事件传递到应用中聚焦的视图来提供此类控制功能。
请求指针捕获
应用中的视图只有在包含它的视图层次结构获得焦点时才能请求指针捕获。因此,您应该在视图中发生特定用户操作时请求指针捕获,例如在
如需请求指针捕获,请在视图中调用
Kotlin
fun onClick(view: View) {
view.requestPointerCapture()
}Java
@Override
public void onClick(View view) {
view.requestPointerCapture();
}
在捕获指针的请求成功后,Android 会调用
处理捕获的指针事件
在视图成功获取指针捕获后,Android 便会开始传递鼠标事件。聚焦的视图可以通过执行以下其中一项任务来处理事件:
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 was
// 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 was
// successfully processed
return true;
}
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 was
// 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 was
// successfully processed
return true;
}
});
无论您是使用自定义视图还是注册监听器,您的视图都会收到包含指针坐标的
释放捕获的指针
应用中的视图可以通过调用
Kotlin
override fun onClick(view: View) {
view.releasePointerCapture()
}Java
@Override
public void onClick(View view) {
view.releasePointerCapture();
}
系统可以在您未显式调用