VelocityTracker和Scroller实践

VelocityTracker

使用注意事项

VelocityTracker 资源在使用完毕后需要释放,通常在 MotionEvent.ACTION_CANCEL && MotionEvent.ACTION_UP 后释放,释放调用如下方法:

1
2
velocityTracker.clear();
velocityTracker.recycle();

但是需要注意的是,掉用后会发生异常(回收任务已在线程池中),所以需要在调用前加!= null判断,在调用后将velocityTracker置空,因为每次使用后都会置空回收,因此在每次启用时都要初始化,而不是只初始化一次(例如:放在onCreate中进行初始化)。

还有如果要追踪MotionEvent.ACTION_MOVE,则需要在每次MotionEvent.ACTION_DOWNMotionEvent.ACTION_MOVE时调用addMovement(event)

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Override
public boolean onTouchEvent(MotionEvent event) {
if(tracker == null){
tracker = VelocityTracker.obtain();
}
tracker.addMovement(event);
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPointId = event.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
tracker.computeCurrentVelocity(1000, ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
final float xVelocity = tracker.getXVelocity(mPointId);
final float yVelocity = tracker.getYVelocity(mPointId);
Log.i(TAG, String.format("xVelocity = %f, yVelocity = %f", xVelocity, yVelocity));
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
if(tracker != null){
tracker.clear();
tracker.recycle();
tracker = null;
}
break;
default:
break;
}
return super.onTouchEvent(event);
}

Scroller

注意事项

Scroller所做的事情只是将View中的内容移动位置移动,而非View本身,如果需要移动View则应该考虑在其所在的ViewGroup中使用Scroller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
scroller = new Scroller(context);
....
public void smoothScrollTo(int destX, int destY){
int scrollX = getScrollX();//先推导一个方向,再同理推导
int scrollY = getScrollY();
int deltaX = destX + scrollX;
int deltaY = destY + scrollY;
scroller.startScroll(scrollX, scrollY, -deltaX, -deltaY, 1000);
// Log.i(TAG, String.format("scrollX = %d, scrollY = %d, deltaX = %d, deltaY = %d", scrollX, scrollY, deltaX, deltaY));
invalidate();
}
...
@Override
public void computeScroll() {
if(scroller.computeScrollOffset()){
scrollTo(scroller.getCurrX(), scroller.getCurrY());
invalidate();
}
}

注意事项,在View.invalidate()中又注释到:

1
2
3
4
5
6
7
8
9
10
11
/**
* Invalidate the whole view. If the view is visible,
* {@link #onDraw(android.graphics.Canvas)} will be called at some point in
* the future.
* <p>
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*/
public void invalidate() {
invalidate(true);
}

onDraw(Canvas canvas)只在draw(Canvas canvas)里有调用,所以实际上draw(Canvas canvas),而draw(Canvas canvas)会调用computeScroll()方法,所以就会一直scrollTo(scroller.getCurrX(), scroller.getCurrY());,直到条件返回为false

至于deltaXdeltaY的计算,建议先从单个坐标(另外一个置零最好)分析。