**

* Helper for tracking the velocity of touch events, for implementing

* flinging and other such gestures. Use {@link #obtain} to retrieve a

* new instance of the class when you are going to begin tracking, put

* the motion events you receive into it with {@link #addMovement(MotionEvent)},

* and when you want to determine the velocity call

* {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}

* and {@link #getYVelocity()}.

*/

// Helper类用于跟踪触摸事件的速度,实现如抛掷等手势。使用{@link #obtain}在开始跟踪时检索类的新实例,将您接收到的motion事件放入其中,并在您想要确定速度时调用

// {@link #computeCurrentVelocity(int)},然后调用{@link #getXVelocity()}和{@link #getYVelocity()}。

// 在Android应用程序的开发过程中,可以实现更绚丽的效果,比如让界面切换平滑的滚动,系统提供的应用在特效这方面提供简单的动画接口。自定义控件开发需要对布局控件以及其子控件的尺寸进行精确控制,还需要根据手势情况实现惯性滑动的效果。为了实现滑动效果,我们需要使用以下工具:

// - android.view.VelocityTracer

// - android.view.Scroller

// - android.view.ViewConfiguration

// 从字面意思理解,VelocityTracker就是速度追踪器。在滑动效果的开发中,通常都需要使用该类来计算出当前手势的初始速度。对应的方法是:

// computeCurrentVelocity(int)

要实现一个可以进行惯性滑动的控件,我们需要遵循以下步骤:

1. 获取初始速度(initialVelocity)。

2. 创建一个Scroller对象。

3. 重写onTouchEvent方法,处理触摸事件。

4. 重写computeScroll方法,计算滚动位置。

5. 使用Scroller的fling方法控制界面滑动。

首先,我们需要通过VelocityTracker和ViewConfiguration类获取一些惯性滑动所需的变量,例如手势离开屏幕时的初始速度、允许进行手势操作的最小距离以及允许手势操作的速度边界值。这些值可以通过以下方法获取:

- configuration.getScaledTouchSlop():获得能够进行手势滑动的距离。

- configuration.getScaledMinimumFlingVelocity():获得允许执行一个fling手势动作的最小速度值。

- configuration.getScaledMaximumFlingVelocity():获得允许执行一个fling手势动作的最大速度值。

接下来,我们需要创建一个Scroller对象,并使用它的fling方法来控制界面滑动。这可以通过以下代码实现:

```java

// 创建VelocityTracker对象

VelocityTracker velocityTracker = VelocityTracker.obtain();

// 设置触摸事件监听器

view.setOnTouchListener(new View.OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// 更新速度轨迹

velocityTracker.addMovement(event);

// 判断是否需要进行惯性滑动

if (event.getAction() == MotionEvent.ACTION_MOVE && velocityTracker.getVelocity(event.getPointerId(0)) != 0) {

float initialVelocity = getInitialVelocity(v, event);

float minX = configuration.getScaledTouchSlop();

float maxX = v.getWidth() - minX;

float minY = configuration.getScaledTouchSlop();

float maxY = v.getHeight() - minY;

Scroller scroller = new Scroller(context);

fling(scroller, v, initialVelocity, minX, maxX, minY, maxY, v.getTop(), v.getBottom());

view.post(scroller::startScroll);

} else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_POINTER_UP) {

// 停止滚动动画

Scroller scroller = (Scroller) v.getTag();

if (scroller != null) {

scroller.abortAnimation();

}

} else if (velocityTracker.getVelocity(event.getPointerId(0)) == 0) {

// 如果手指离开屏幕,停止滚动动画

Scroller scroller = (Scroller) v.getTag();

if (scroller != null) {

scroller.abortAnimation();

}

}

return true;

}

});

```

最后,我们需要实现以下几个方法:

1. getInitialVelocity(View view, MotionEvent event):根据触摸事件计算初始速度。

2. fling(Scroller scroller, View view, float initialVelocityX, float minX, float maxX, float minY, float maxY, int top, int bottom):使用Scroller的fling方法控制视图在水平和垂直方向上的滚动。

第三,重写onTouchEvent方法。当我们用手指在屏幕上来回滑动时,此时执行的是scrollBy方法来刷新界面。当手指离开屏幕时,此时就要开始执行ACTION_UP后面的操作了。通过对手指离开屏幕时的速度进行判断,是否能够进行惯性滑动操作。如果能够执行,那么就使用Scroller类的fling方法启动滑动动画。这时需要调用一下invalidate()方法来间接地调用computeScroll方法。

在computeScroll方法中,对Scroller的动画是否执行完成做了判断。如果动画没有完成(mScroller.computeScrollOffset() == true),那么就使用scrollTo方法对mScrollX、mScrollY的值进行重新计算,刷新界面。然后调用postInvalidate()方法重新绘制界面。postInvalidate()方法会调用invalidate()方法,而invalidate()方法又会调用computeScroll方法。就这样周而复始地相互调用,直到mScroller.computeScrollOffset()返回false才会停止界面的重绘动作。

总结来说,滑动效果来看,它依然是在不停地计算控件的位置刷新屏幕,不停地绘制新的图片替换旧的图片。当然每次刷新的速度很快,从而给人一种快速滑动的感觉。写到这里我发现,现在所谓的动画总是逃脱不了电影的那种模式:每秒播放多少帧的图片来达到连续播放的效果欺骗人的眼睛。

而且,关于Android一些酷炫效果的开发,还是要自己多动手,熟悉View、ViewGroup中每个绘制方法、位置计算方法的调用方式以及顺序。这样至少是在2D动画开发中,也就是一种方式,逃脱不了不停重新绘制的这个圈。关于熟悉View、ViewGroup中每个绘制方法、位置计算方法的调用方式以及顺序的问题,我建议最好自己写一个简单的自定义View或ViewGroup的扩展类,重载那些绘制、位置计算的方法打个日志出来一看自然就明白了。虽然这个方法很笨,但是很容易出效果的。