Android 要做出炫的显示效果来就离不开动画。何为动画?当一个View 随时间变化而移动、放大、透明度变化等都能产生动画,同样连续的帧变化也能形成动画。
本文结合一动画实例来详细介绍Android的动画机制及Scroller类。如图1所示,在T1时刻,View的中心位置为X1,T2时刻中心位置为X2,从T1到T2的 delta =T2-T1的时间段中View 从X1位置移动到X2位置,同时逐步放大到原来大小的1.5倍,中间的虚线框表示某一时刻时View的位置和大小。很显然这是一个动画过程,在一定时间内实现了View 的移动和放大,当然也可以去逐步修改View的透明度。
图1, 动画移动实例
图1, 动画移动实例
图1 过程的位置移动可以通过Android Scroller 来实现,在Android 控件中,Gallery、ViewPager都使用了Scroller来实现滚动动画,理解Scroller对实现好的动画效果十分有用。下面介绍一下Scroller 类,Scroller 支持滚动模式(SCROLL_MODE)和Fling 模式(FLING_MODE),这里重点介绍SCROLL_MODE,fling模式也是类似的,无非是插值函数的差异,后面也有介绍。
一: Scroller 关键API 分析
1. public void startScroll(int startX, int startY, int dx, int dy, int duration){
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration; // 滚动时长
mStartTime = AnimationUtils.currentAnimationTimeMillis(); //当前滚动时间
mStartX = startX; // 滚动起始位置
mStartY = startY;
mFinalX = startX + dx; // 滚动结束位置
mFinalY = startY + dy;
mDeltaX = dx; // 滚动距离
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration; //滚动时长的倒数
}
Scroller 并不是真正实现View 的滚动,它只是用来记录及记录View 滚动位置的工具,如图1中的示例中,移动的duration 是T2-T1,X方向移动的dx 是X2-X1,Y方向无移动。因此在控制View 绘制的线程创建一个Scroller实例mScroller,通过mScroller.startScroll(0,0,X2-X1,0,T2-T1) 便实现了滚动的初始化。
2.
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
// 计算已经消耗掉的时间
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
// 消耗的时间未达到动画时长
switch (mMode) {
case SCROLL_MODE:
// 计算消耗掉的时间占动画总时间的比例
float x = timePassed * mDurationReciprocal;
// 根据插值函数,计算出当前时刻,View 需要滚动到的位置,插值函数在后面介绍
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
... ...
break;
}
}
else {
// 如果消耗的时间已经达到动画时长了,那么直接移动到终点位置
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
3.
public final int getCurrX() {
return mCurrX;
// 获取当前滚动位置
}
从Scroller中的上述两个方法可以看出,实现View的移动只需要通过一个线程不停的调用computeScrollOffset来判断是否移动完成,并获取到当前移动的位置,将View移动到对应位置即可。
图1的动画,代码段示例如下:
图2 fling 模式下速度与时间的关系曲线
图3:fling模式下位移与时间的关系曲线
Interpolator用于动画中的时间插值,不同的插值曲线能做出不同的动画效果,如匀速移动,加速移动,先加速后减速等等,这里不一一举例。只要理解了Android动画的原理,可以根据自己的需求来设计插值函数来实现更好的动画效果。
private AnimationRunnable mAnimationRunnable = new FlingRunnable();
mAnimationRunnable.mScroller.startScroll(0,0,X2-X1,0,T2-T1);
private class AnimationRunnable implements Runnable{
public AnimationRunnable () {
mScroller = new Scroller(getContext());
}
@Override
public void run() {
final Scroller scroller = mScroller;
boolean more = scroller.computeScrollOffset();
final int x = scroller.getCurrX();
int delta = mLastScrollX - x;
if (delta > 0) {
mView.offsetLeftAndRight(delta); //移动view
mView.setScaleX(); // 这里设置放大比例,其他放大比例可以根据实际情况计算,此处略去放大参数。
mView.setScaleY();
invalidate();
if (more) {
// 继续滚动
mLastScrollX = x;
post(this);
} else {
mScroller.forceFinished(true); //滚动结束
}
}
}
假如系统性能高,每次绘图的时间短,那么根据上面的分析可知从X1位置移动到X2位置绘制的帧数会越多,动画效果会越平滑,反之动画将没有那么流程。
二:插值函数Interpolator
画面移动过程要呈现不同的效果,可以通过不同的插值函数来实现。在构造Scroller时可以设置不同的插值函数来达到不同的动画效果,如我们经常看到的滑屏fling效果是移动速度逐渐变小最后停止下来,
图2和图3分别是Scroller中FLING_MODE下速度与时间、位移与时间的关系图。
图2 fling 模式下速度与时间的关系曲线
图3:fling模式下位移与时间的关系曲线
Interpolator用于动画中的时间插值,不同的插值曲线能做出不同的动画效果,如匀速移动,加速移动,先加速后减速等等,这里不一一举例。只要理解了Android动画的原理,可以根据自己的需求来设计插值函数来实现更好的动画效果。