Android逐帧动画和补间动画
本篇博客来看一下Android中的逐帧动画和补间动画。
一、逐帧动画
逐帧动画也叫Drawable Animation。
在Android中实现逐帧动画,就是由设计师给出一系列状态不断变化的图片,
开发者可以指定动画中每一帧对应的图片和持续的时间,然后在合适的时候播发动画。
最常用定义逐帧动画的方式是:
在res/drawable目录下,放置动画对应的图片,并定义animation.xml文件。
animation.xml的定义类似于:
<?xml version="1.0" encoding="utf-8" ?>
<!--oneshot表示是否重复播放动画, true表示只播放一次, false表示重复播放 -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<!--drawable指定图片, duration表示图像的持续时间-->
<item
android:drawable= "@drawable/p_1"
android:duration="1200"/>
<item
android:drawable= "@drawable/p_2"
android:duration="1200"/>
<item
android:drawable= "@drawable/p_3"
android:duration="1200"/>
</animation-list>
负责播放动画的View,需要在xml中配置src属性为animation.xml,例如:
<ImageView
android:id="@+id/test_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/animation" />
然后就可以在代码中,启动动画了:
ImageView imageView = findViewById(R.id.test_view);
//获取AnimationDrawable
AnimationDrawable drawable = (AnimationDrawable)imageView.getDrawable();
//调用start接口开始播放
//AnimationDrawable还有其它接口,例如停止、增加帧等
drawable.start();
如果不定义animation.xml文件,也可以仅通过代码实现逐帧动画:
//在Activity中直接getDrawable,需要API >= 21
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AnimationDrawable animationDrawable = new AnimationDrawable();
int[] ids = new int[] {R.drawable.p_1, R.drawable.p_2, R.drawable.p_3};
for (int id : ids) {
Drawable tmp = getDrawable(id);
if (tmp != null) {
//利用addFrame接口, 增加帧
animationDrawable.addFrame(tmp, 1200);
}
}
animationDrawable.setOneShot(true);
ImageView testView = findViewById(R.id.another_test_view);
//设置animationDrawable
testView.setBackground(animationDrawable);
animationDrawable.start();
}
}
二、补间动画
补间动画是指开发者无需定义动画过程中的每一帧,
只需要定义动画的开发和结束这两个关键帧,并指定动画变化的时间和方式等,
然后交由Android系统进行计算,通过在两个关键帧之间插入渐变值来实现平滑过渡,
以实现对View的内容进行一系列的图形变换的动画效果。
补间动画主要包括四种基本效果:
透明度变化Alpha、大小变化Scale、位移变化Translate及旋转变化Rotate,
这四种效果可以动态组合,从而实现复杂灵活的动画。
Android中使用Animation来抽象动画类,对应上述四种补间动画的实现分别为:
AlphaAnimation:
改变透明度的动画,创建动画时需要指定动画开始和结束的透明度,
以及动画持续的时间,透明度取值范围是0到1。
ScaleAnimation:
缩放大小的动画,创建动画时需要指定动画开始和结束时在X轴和Y轴的缩放比,以及动画的持续时间;
同时由于缩放时以不同的点作为中心会产生不同的效果,因此也需要通过pivotX和pivotY指定缩放中心的坐标。
TranslateAnimation:
改变位置的动画,创建动画时需要指定动画开始和结束时的X、Y坐标,以及动画的持续时间。
RotateAnimation:
旋转动画,创建动画时需要指定动画开始和结束时的旋转角度,以及动画的持续时间;
同时由于旋转时以不同的点作为中心会产生不同的效果,因此也需要通过pivotX和pivotY指定旋转中心的坐标。
2.1、插值器Interpolator
在分析进一步补间动画前,我们有必要先了解一下插值器Interpolator。
Android系统会在补间动画的开始和结束帧之间插入渐变值,它的依据便是Interpolator。
Interpolator会根据类型,选择不同的算法,计算出在补间动画期间所需要动态插入帧的密度和位置。
简单来讲,Interpolator负责控制动画的变化速度,
使得动画效果能够以匀速、加速、减速、抛物线等多种速度进行变化。
Android中的Interpolator类是一个空接口,继承TimeInterpolator:
/**
* A time interpolator defines the rate of change of an animation. This allows animations
* to have non-linear motion, such as acceleration and deceleration.
*/
public interface TimeInterpolator {
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* @return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}
通过注释容易了解到,TimeInterpolator时间插值器允许动画进行非线性变换,如加速、减速变化等。
TimeInterpolator只有一个函数getInterpolation,
其输入参数表示当前帧在整个动画过程中对应的比例,取值范围在0~1之间;
输出表示输入值对应的映射值。
Android已经定义了一些Interpolator的实现类,
例如:AccelerateInterpolator、AccelerateDecelerateInterpolator等。
我们以AccelerateInterpolator的getInterpolation函数为例,看看具体的实现:
//AccelerateInterpolator的映射值为输入的平方或指数结果
//那么显然输入的值越大,输出就越大
//且随着输入的增大,输出增大速率越来越大
//这就是会导致一种加速的效果
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, mDoubleFactor);
}
}
2.2、实现示例
了解完Interpolator后,我们来看看各种补间动画如何实现。
2.2.1、AlphaAnimation的实现
用XML定义AlphaAnimation时,需要在res/anim目录下建立对应的xml文件,其内容类似于:
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1200"
android:fillAfter="true" //动画结束之后是否保持动画的最终状态; true, 表示保持动画的最终状态
android:fillBefore="false" //动画结束之后是否恢复到动画开始前的状态; true, 表示恢复到动画开始前的状态
android:fromAlpha="0.0" //初始透明度
android:interpolator="@android:anim/accelerate_interpolator" //指定插值器
android:toAlpha="1.0" //最终的透明度
android:repeatCount="-1" //指定动画重复播放的次数; 如果需要无限循环播放, 填写一个小于0的数
android:repeatMode="restart" //动画重复的Mode, 有reverse和restart两种
android:startOffset="200"/> //动画播放延迟时长
在代码中使用XML定义动画的方式类似于:
AlphaAnimation alphaAnimation = (AlphaAnimation) AnimationUtils.loadAnimation(
this, R.anim.alpha_animation);
ImageView imageView = findViewById(R.id.test_view);
imageView.startAnimation(alphaAnimation);
仅用Java来实现AlphaAnimation时,对应代码类似于:
//直接创建AlphaAnimation对象
//xml中定义的属性都有对应的接口可以调用,此处仅举例一下
AlphaAnimation alphaAnimation = new AlphaAnimation(0.0f, 1.0f);
alphaAnimation.setDuration(1000);
alphaAnimation.setInterpolator(new AccelerateInterpolator(2));
alphaAnimation.setRepeatCount(2);
ImageView imageView = findViewById(R.id.test_view);
imageView.startAnimation(alphaAnimation);
2.2.2、ScaleAnimation的实现
用XML定义ScaleAnimation时,需要在res/anim目录下建立对应的xml文件,其内容类似于:
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXScale="0.2"
android:fromYScale="0.2"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5"
android:repeatCount="-1"/>
在代码中使用XML定义动画的方式类似于:
ImageView imageView = findViewById(R.id.test_view);
ScaleAnimation scaleAnimation = (ScaleAnimation)
AnimationUtils.loadAnimation(this, R.anim.scale_animation);
imageView.startAnimation(scaleAnimation);
仅用Java来实现ScaleAnimation时,对应代码类似于:
ImageView imageView = findViewById(R.id.test_view);
ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f);
scaleAnimation.setDuration(2000);
imageView.startAnimation(scaleAnimation);
ScaleAnimation构造函数的参数较多,具体含义可以参考对应的doc文档。
2.2.3、TranslateAnimation的实现
用XML定义ScaleAnimation时,需要在res/anim目录下建立对应的xml文件,其内容类似于:
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="2000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="100" />
在代码中使用XML定义动画的方式类似于:
ImageView imageView = findViewById(R.id.test_view);
TranslateAnimation animation = (TranslateAnimation)
AnimationUtils.loadAnimation(this, R.anim.translate_animation);
imageView.startAnimation(animation);
仅用Java来实现TranslateAnimation时,对应代码类似于:
ImageView imageView = findViewById(R.id.test_view);
TranslateAnimation animation = new TranslateAnimation(0f, 100f, 0f, 100f);
animation.setDuration(3000);
animation.setFillAfter(true);
imageView.startAnimation(animation);
TranslateAnimation构造函数的参数较多,具体含义可以参考对应的doc文档。
2.2.4、RotateAnimation的实现
用XML定义ScaleAnimation时,需要在res/anim目录下建立对应的xml文件,其内容类似于:
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:toDegrees="360"/>
在代码中使用XML定义动画的方式类似于:
ImageView imageView = findViewById(R.id.test_view);
RotateAnimation animation = (RotateAnimation) AnimationUtils
.loadAnimation(this, R.anim.rotate_animation);
imageView.startAnimation(animation);
仅用Java来实现TranslateAnimation时,对应代码类似于:
ImageView imageView = findViewById(R.id.test_view);
RotateAnimation animation = new RotateAnimation(0, -720, 0.5f, 0.5f);
animation.setDuration(1000);
imageView.startAnimation(animation);
2.3、自定义补间动画
在实际的项目中,上述默认的基本动画可能无法满足需求,
此时就可能需要自定义补间动画了。
具体的做法就是集成Animation类,然后重写其中的applyTransformation方法:
protected void applyTransformation(float interpolatedTime, Transformation t) {
}
其中:
interpolatedTime代表了动画的时间进行比。
不管动画实际的持续时间如何,当动画播放时,该参数总是自动从0变化到1。
Transformation代表了补间动画在不同时刻对图形或组件的变形程度,
该对象里封装了一个Matrix对象。
Transformation实际上就是通过对Matrix对象进行旋转、位置、倾斜等,
实现对图片或者视图进行相应的变换。
从applyTransformation的参数可以看出,实现自定义补间动画的重点就在于:
重写applyTransformation方法时,根据interpolatedTime,动态的计算动画对图形的变形程度。
我们来看一个具体的例子:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = findViewById(R.id.test_view);
DemoAnimation animation = new DemoAnimation(150, 150, 5000);
imageView.startAnimation(animation);
}
//自定义的补间动画
private class DemoAnimation extends Animation {
private float mCenterX;
private float mCenterY;
private int mDuration;
//这里使用的是:android.graphics.Camera类
//用于空间变换的工具
private Camera mCamera = new Camera();
public DemoAnimation(float x, float y, int duration) {
mCenterX = x;
mCenterY = y;
mDuration = duration;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
setDuration(mDuration);
setFillAfter(true);
setInterpolator(new LinearInterpolator());
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
mCamera.save();
//根据interpolatedTime来控制x、y和z轴上的偏移
mCamera.translate(100.0f - 100.0f * interpolatedTime,
150.0f * interpolatedTime - 150, 80.0f - 80.0f * interpolatedTime);
//设置x和y轴的旋转角度
mCamera.rotateX(360 * interpolatedTime);
mCamera.rotateY(360 * interpolatedTime);
//获取Transformation中的matrix
Matrix matrix = t.getMatrix();
//将camera计算出的数据拷贝到matrix中,
//即应用到Transformation中
mCamera.getMatrix(matrix);
//在旋转前、后移动图片
//实际上就是更改图片的旋转中心
matrix.preTranslate(-mCenterX, -mCenterY);
matrix.postTranslate(mCenterX, mCenterY);
mCamera.restore();
}
}
}
总结
至此,我们已经基本了解了逐帧动画和补间动画的使用方式。
其中,逐帧动画的使用最为简单;
补间动画既可以利用XML定义,也可以仅用Java代码实现。
对于基本的补间动画,主要是要选择合适的插值器以及配置参数;
对于自定义的补间动画,则可以利用Camera类来简化Matrix类的计算。
上一篇: 夏日杂谈
下一篇: Android补间动画之平移动画
推荐阅读
-
Android 动画之补间动画实战(飞机起飞)
-
Android使用补间动画做一个朴实无华的启动页
-
Android 动画 属性动画 视图动画 补间动画 帧动画 详解 使用
-
flash油漆桶和补间动画制作小球的旋转动画
-
flash中制作遮罩层和传统补间动画有什么区别?
-
Android中四种补间动画的使用示例(附代码下载)
-
Android中实现一个简单的逐帧动画(附代码下载)
-
黑马Android76期学习笔记01基础--day07--广播,有、无序广播、特殊广播接受者、样式和主题,this与context的区别、普通对话框,进度条对话框、帧动画
-
Android动画之补间动画
-
Android开发触摸touch事件(补间动画和自定义view使用方法)