多触控应用中如何用VelocityTracker计算特定指针的速度
解决多指针触摸下VelocityTracker计算特定指针速度的问题
嘿,这个问题我之前做多点触控应用时也踩过坑!默认情况下很多人会误以为VelocityTracker会把所有指针的运动平均计算,但其实它内部是单独跟踪每个指针的运动数据的——你觉得速度被静止指针拉低,大概率是因为没正确指定要查询的指针ID,或者事件处理逻辑有问题。
下面给你两种可靠的解决方案,根据你的场景选择:
方案一:直接获取特定指针的速度(最简单高效)
VelocityTracker本身就支持按指针ID查询单独速度,完全不需要过滤事件。你只需要两步:
- 记录你要跟踪的目标指针ID(比如第一个按下的指针,或者用户指定的指针)
- 在计算速度时,传入该指针ID即可
代码示例:
private VelocityTracker mVelocityTracker; private int mTargetPointerId = -1; @Override public boolean onTouchEvent(MotionEvent event) { int actionMasked = event.getActionMasked(); int actionIndex = event.getActionIndex(); // 初始化VelocityTracker if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } // 传入完整MotionEvent,VelocityTracker会自动跟踪每个指针的运动 mVelocityTracker.addMovement(event); switch (actionMasked) { case MotionEvent.ACTION_DOWN: // 记录第一个按下的指针作为跟踪目标 mTargetPointerId = event.getPointerId(0); break; case MotionEvent.ACTION_POINTER_DOWN: // 如果你需要切换跟踪新按下的指针,在这里更新ID // mTargetPointerId = event.getPointerId(actionIndex); break; case MotionEvent.ACTION_POINTER_UP: // 如果目标指针抬起,切换到其他活跃指针 int liftedPointerId = event.getPointerId(actionIndex); if (liftedPointerId == mTargetPointerId) { mTargetPointerId = event.getPointerId(0); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mTargetPointerId = -1; // 回收VelocityTracker,避免内存泄漏 if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; case MotionEvent.ACTION_MOVE: if (mTargetPointerId != -1) { // 计算速度,参数1000表示速度单位为像素/秒 mVelocityTracker.computeCurrentVelocity(1000); // 获取目标指针的独立速度,不受其他指针影响 float xVelocity = mVelocityTracker.getXVelocity(mTargetPointerId); float yVelocity = mVelocityTracker.getYVelocity(mTargetPointerId); // 在这里使用计算出的速度 Log.d("Velocity", "X: " + xVelocity + ", Y: " + yVelocity); } break; } return true; }
方案二:仅向VelocityTracker传入目标指针的事件(极端场景用)
如果你的场景非常特殊,需要完全忽略其他指针的所有事件,可以构造仅包含目标指针的MotionEvent传入VelocityTracker。这种方式更繁琐,但能确保VelocityTracker只处理目标指针的数据:
代码示例:
private VelocityTracker mVelocityTracker; private int mTargetPointerId = -1; @Override public boolean onTouchEvent(MotionEvent event) { int actionMasked = event.getActionMasked(); int actionIndex = event.getActionIndex(); if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } switch (actionMasked) { case MotionEvent.ACTION_DOWN: mTargetPointerId = event.getPointerId(0); break; case MotionEvent.ACTION_POINTER_DOWN: // 可选:切换跟踪新指针 // mTargetPointerId = event.getPointerId(actionIndex); break; case MotionEvent.ACTION_POINTER_UP: int liftedPointerId = event.getPointerId(actionIndex); if (liftedPointerId == mTargetPointerId) { mTargetPointerId = event.getPointerCount() > 1 ? event.getPointerId(0) : -1; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mTargetPointerId = -1; if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; case MotionEvent.ACTION_MOVE: if (mTargetPointerId != -1) { // 找到目标指针在当前事件中的索引 int targetIndex = event.findPointerIndex(mTargetPointerId); if (targetIndex != -1) { // 构造仅包含目标指针的MotionEvent MotionEvent targetEvent = MotionEvent.obtain(event); targetEvent.setPointerCount(1); targetEvent.setPointerCoords(0, event.getPointerCoords(targetIndex)); targetEvent.setPointerId(0, mTargetPointerId); // 仅将目标指针的事件传入VelocityTracker mVelocityTracker.addMovement(targetEvent); targetEvent.recycle(); // 计算并获取速度 mVelocityTracker.computeCurrentVelocity(1000); float xVel = mVelocityTracker.getXVelocity(mTargetPointerId); float yVel = mVelocityTracker.getYVelocity(mTargetPointerId); } } break; } return true; }
关键注意点:
- 务必在
ACTION_UP或ACTION_CANCEL时回收VelocityTracker,避免内存泄漏。 - 当目标指针抬起时,要及时更新目标指针ID(比如切换到其他活跃指针),避免后续速度计算出错。
computeCurrentVelocity的参数是时间单位,传入1000表示速度单位为像素/秒,传入1则是像素/毫秒,可根据需求调整。
内容的提问来源于stack exchange,提问作者jojois74
相关产品推荐
相关产品推荐

