You can use the view animation system to perform tweened animation on Views.看看官网的说明,View Animation 后面跟这一个 system ,说明视图动画是一套完整的动画系统。视图动画(View Animation)也经常被称为 Tween Animation,国内大多翻译为补间动画。
先说说什么是 tween animation,跟帧动画不一样,帧动画是一帧一帧完整的渲染资源图片,而补间动画呢,通过给定的两个关键帧的参数,系统按照一定的算法计算出每一帧的图像。
先说说帧,在一个时刻,你看见的图像是一帧,你看到的屏幕内容的变化,移动,都是通过一帧一帧不停的渲染到屏幕上的。假设屏幕的刷新频率是 60hz,那么相当于 16.67 ms 刷新一次屏幕。在帧动画里,咱们一般把 item 称为 帧,但是它跟屏幕渲染的帧明显不是等同的。假设咱们设置每item 的时长为 200 ms,也就是说,这个 item 将大概在屏幕保留 200 ms,而屏幕可能已经刷新了好几帧了。注意:这里不考虑丢帧等乱七八糟的情况。
在回到补间动画,补间动画通过给对象设置关键帧的参数,让系统自动去计算每帧具体渲染的图片。补间动画可以设置的参数有:位置 position,大小 size,旋转 rotation 和透明度 transparency。
一般在项目里,咱们有两种方式使用补间动画,一种是 xml 定义,一种是 Java Code。
补间动画有个专门的目录 res/anim/ 。在这个目录下的每个文件的根元素可以是 <alpha>,<scale>,<translate>和
上一个官网的例子:
<set android:shareInterpolator="false">
<scale
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromXScale="1.0"
android:toXScale="1.4"
android:fromYScale="1.0"
android:toYScale="0.6"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false"
android:duration="700" />
<set android:interpolator="@android:anim/decelerate_interpolator">
<scale
android:fromXScale="1.4"
android:toXScale="0.0"
android:fromYScale="0.6"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400"
android:fillBefore="false" />
<rotate
android:fromDegrees="0"
android:toDegrees="-45"
android:toYScale="0.0"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="700"
android:duration="400" />
</set>
</set>
对应的 Java 代码:
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);
刚才上面的 Java 代码不是补间动画的 Java 实现,这里是配合 xml 文件,执行动画。咱们刚才可以看到,xml 文件中包含了 <set>,<alpha> 等元素,这些元素其实都是有其对应的 Java 类的,<set> 对应的就是 AnimationSet,AnimationSet 是 Animation 的子类,除了 AnimationSet 之外,Animation 还有这么几个子类:AlphaAnimation,RotateAnimation,ScaleAnimation,TranslateAnimation 。补间动画的 Java 代码实现说的是,你可以通过创建 Animation 对象,然后调用 View 的 startAnimation 方法来实现动画。
具体不同的动画 xml 元素属性和 Animation 子类的属性基本都是对应的,这里不一一描述了。原理知道了,剩下的就是查文档的事了。
咱们可以看到,不管是是 XML 实现,还是 Java 实现,无非就是构造出一个 Animation 对象,然后调用 View.startAnimation() 方法。
咱们前面说了,Animation 是一个抽象类,AnimationSet 和 AlphaAnimation 等都是它的子类。startAnimation() 方法调用了 setAnimation() 方法,调完后还调用了 invalidate() 方法。咱们这里先不讨论 invalidate() 方法怎么执行的,咱们回到 Animation 这个类里,咱们看到下面这个方法,这个方法就是动画的一个关键方法。
/**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
* otherwise.
*
* @param currentTime Where we are in the animation. This is wall clock time.
* @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @return True if the animation is still running
*/
public boolean getTransformation(long currentTime, Transformation outTransformation) {
if (mStartTime == -1) {
mStartTime = currentTime;
}
final long startOffset = getStartOffset();
final long duration = mDuration;
float normalizedTime;
if (duration != 0) {
normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
(float) duration;
} else {
// time is a step-change with a zero duration
normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
}
final boolean expired = normalizedTime >= 1.0f;
mMore = !expired;
if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
if (!mStarted) {
fireAnimationStart();
mStarted = true;
if (USE_CLOSEGUARD) {
guard.open("cancel or detach or getTransformation");
}
}
if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
if (mCycleFlip) {
normalizedTime = 1.0f - normalizedTime;
}
final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
applyTransformation(interpolatedTime, outTransformation);
}
if (expired) {
if (mRepeatCount == mRepeated) {
if (!mEnded) {
mEnded = true;
guard.close();
fireAnimationEnd();
}
} else {
if (mRepeatCount > 0) {
mRepeated++;
}
if (mRepeatMode == REVERSE) {
mCycleFlip = !mCycleFlip;
}
mStartTime = -1;
mMore = true;
fireAnimationRepeat();
}
}
if (!mMore && mOneMoreTime) {
mOneMoreTime = false;
return true;
}
return mMore;
}
其中调用了 applyTransformation() 方法,而这个方法在 Animation 里是一个空的方法,于是咱们去找它的子类看看,我找了 AplhaAnimation 这个类,方法的实现如下:
/**
* Changes the alpha property of the supplied {@link Transformation}
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float alpha = mFromAlpha;
t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
}
通过一个计算,给 Transformation 更改了以下属性。在 View 的 draw(Canvas canvas) 方法里,在往 Canvas 上渲染的时候,会根据 Animation 对象和 Transformation 对象的相关的值,把当前的 View 渲染到 Canvas 上。所以,咱们可以看出,视图动画,View 本身的属性没有变化,变化的是渲染的时候。
- EOF -
本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处,尊重他人劳动。
转载请注明:文章转载自 Binkery 技术博客 [https://binkery.com]
本文标题: Andoid 动画之二 视图动画 View Animation
本文地址: https://binkery.com/archives/522.html