VelocityTracker是android提供的用来记录滑动速度的一个类,可以监控手指移动的速度。
基本用法
如果我们想监控一个view内,手指移动的瞬时速度,该如何做?代码如下所示。主要是在onTouchEvent里记录各个MotionEvent,down事件是起点,此时需要初始化mVelocityTracker(obtain或者reset),第一次肯定是obtain。然后把当前的event记录起来(addMovement)。接着在move的时候获取速度,获取速度用mVelocityTracker.getXVelocity()或者mVelocityTracker.getYVelocity()。在调用这个之前必须做一次计算,也就是mVelocityTracker.computeCurrentVelocity(1000);
最后在up的时候要对mVelocityTracker进行recycle。很简单吧。
public class XView extends View {
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();
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());
Log.d("", "Y velocity: " +
mVelocityTracker.getYVelocity());
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;
}
}
computeCurrentVelocity
我们看下computeCurrentVelocity这个函数,有2个重载
public void computeCurrentVelocity(int units) {
nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE);
}
public void computeCurrentVelocity(int units, float maxVelocity) {
nativeComputeCurrentVelocity(mPtr, units, maxVelocity);
}
我们刚才用的是第一种方法,传了个1000.这个表示计算过去1000ms(即1s)内的速度。这个速度其实就是 ((当前的位置)-(之前的位置))/时间.如果得到的值为200就表示这1000ms内,X方向移动了200像素。
速度是有正负的,右划就是正的,左划就是负的,上划为负,下划为正。
最大速度传的是Float的最大值,第二种方法可以指定最大速度。这个最大速度有什么用呢?
实际上是一个上限,比如我们当前速度300,但是上限为200,那用getXVelocity()得到的值就是200,不可能超过上限(无论正负),相关代码如下所示。
//android_view_VelocityTracker.cpp
void VelocityTrackerState::computeCurrentVelocity(int32_t units, float maxVelocity) {
BitSet32 idBits(mVelocityTracker.getCurrentPointerIdBits());
mCalculatedIdBits = idBits;
for (uint32_t index = 0; !idBits.isEmpty(); index++) {
uint32_t id = idBits.clearFirstMarkedBit();
float vx, vy;
mVelocityTracker.getVelocity(id, &vx, &vy);
vx = vx * units / 1000;
vy = vy * units / 1000;
if (vx > maxVelocity) {
vx = maxVelocity;
} else if (vx < -maxVelocity) {
vx = -maxVelocity;
}
if (vy > maxVelocity) {
vy = maxVelocity;
} else if (vy < -maxVelocity) {
vy = -maxVelocity;
}
Velocity& velocity = mCalculatedVelocity[index];
velocity.vx = vx;
velocity.vy = vy;
}
}
惯性滑动
还有个比较常见的需求,比如我们右划了一下,view往右移动,我们希望在手指抬起来之后,view能够按照惯性继续滑动一段距离然后停止,此时就需要手指抬起的时候的速度,可以在up的时候计算。
模板写法
我查了下网上的资料,发现不同的人有不同的写法,有点茫然,不知道该参考谁,本文的例子主要从android官方demo和源码内提取出来,应该没有坑,下次有需求,抄这段代码比较合适.这个写法和前文 基本用法里的有点区别,都是靠谱的,这里没有用到clear,每次都是obtain,然后recycle。基本用法里多了个clear,按道理提高了重用性,性能会好一些。但是我看了下ScrollView和ViewPager都是按照下边的写法来的,估计他们写的也比较随意。
public class XView extends View {
private static final String DEBUG_TAG = "Velocity";
private static final int V_CONSTANT=50;
private VelocityTracker mVelocityTracker = null;
@Override
public boolean onTouchEvent(MotionEvent event) {
int index = event.getActionIndex();
int action = event.getActionMasked();
initVelocityTrackerIfNotExists();
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);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000);
int initialVelocity = (int) velocityTracker.getYVelocity();
if ((Math.abs(initialVelocity) > V_CONSTANT)) {
//速度够大,就做什么事,比如翻页,some是个常数,大约在30-100之间
// ...
} else{
// ...
}
recycleVelocityTracker();
break;
}
return true;
}
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
}
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
}