补间动画就是指开发者只需指定动画开始、动画结束等“关键帧”,而动画变化的“中间帧”由系统计算并补齐
TWeen 动画与 Interpolator
-
AlphaAnimation
透明度改变的动画。创建该动画时要指定动画开始时的透明度、结束时的透明度和动画持续时间。其中透明度可从0变化到1。 -
ScaleAnimation
大小缩放的动画。创建该动画时要指定动画开始时的缩放比(以X、Y轴的缩放参数来表示)、结束时的缩放比(以X、Y轴的缩放参数来表示),并指定动画持续时间。由于缩放时以不同点为中心的缩放效果并不相同,因此指定缩放动画时还需要通过pivotX
、pivotY
来指定"缩放中心"的坐标。 -
TranslateAnimation
位移变化的动画。创建该动画时只要指定动画开始时的位置(以X、Y坐标来表示)、结束时的位置(以X、Y坐标来表示),并指定动画持续时间即可。 -
RotateAnimation
旋转动画。创建该动画时只要指定动画开始时的旋转角度、结束时的旋转角度,并指定动画持续时间即可。由于旋转时以不同点为中心的旋转效果并不相同,因此指定旋转动画时还要通过pivotX
、pivotY
来指定"旋转轴心"的坐标。
Interpolator
根据特定算法计算出整个动画所需要动态插入帧的密度和位置。简单地说,Interpolator
负责控制动画的变化速度,这就使得基本的动画效果(Alpha
、Scale
、Translate
、Rotate
)能以匀速变化、加速、减速、抛物线速度等各种速度变化。
Interpolator
是一个接口,它定义了所有 Interpolator
都需要实现的方法:float getInterpolation(float input)
,开发者完全可以通过实现 Interpolator
来控制动画的变化速度。
Android 为 Interpolator
提供了如下几个实现类,分别用于实现不同的动画变化速度。
-
LinearInterpolator
:动画以均匀的速度改变。 -
AccelerateInterpolator
:在动画开始的地方改变速度较慢,然后开始加速。 -
AccelerateDecelerateInterpolator
: 在动画开始、结束的地方改变速度较慢,在中间的时候加速。 -
CycleInterpolator
:动画循环播放特定的次数,变化速度按正弦曲线改变。 -
DecelerateInterpolator
:在动画开始的地方改变速度较快,然后开始减速。
位置、大小、旋转度、透明度改变的补间动画
MainActivity
利用 AnimationUtils
工具类来加载指定的动画资源,加载成功后会返回一个 Animation
,该对象即可控制图片或视图播放动画。
public class MainActivity extends AppCompatActivity {
private ImageView flower;
private Animation reverse;
class MyHandler extends Handler {
private WeakReference<MainActivity> activity;
public MyHandler(WeakReference<MainActivity> activity) {
this.activity = activity;
}
@Override
public void handleMessage(Message msg) {
if (msg.what == 0x123) {
activity.get().flower.startAnimation(activity.get().reverse);
}
}
}
Handler handler = new MyHandler(new WeakReference<>(this));
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
flower = findViewById(R.id.flower);
// 加载第一份动画资源
Animation anim = AnimationUtils.loadAnimation(this, R.anim.anim);
// 设置动画结束后保留结束状态
anim.setFillAfter(true);
// 加载第二份动画资源
reverse = AnimationUtils.loadAnimation(this, R.anim.reverse);
// 设置动画结束后保留结束状态
reverse.setFillAfter(true);
Button bn = findViewById(R.id.bn);
bn.setOnClickListener(view -> {
flower.startAnimation(anim);
// 设置3.5秒后启动第二个动画
new Timer().schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0x123);
}
}, 3500);
});
}
}
anim/anim.xml
指定动画匀速变化,同时进行缩放、透明度、旋转三种改变,动画持续时间为 3 秒。
<?xml version="1.0" encoding="UTF-8"?><!-- 指定动画匀速改变 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator">
<!-- 定义缩放变换 -->
<scale
android:duration="3000"
android:fillAfter="true"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.01"
android:toYScale="0.01" />
<!-- 定义透明度的变化 -->
<alpha
android:duration="3000"
android:fromAlpha="1"
android:toAlpha="0.05" />
<!-- 定义旋转变换 -->
<rotate
android:duration="3000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="1800" />
</set>
anim/reverse.xml
控制图片以动画的方式恢复回来
<?xml version="1.0" encoding="UTF-8"?><!-- 指定动画匀速改变 -->
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator">
<!-- 定义缩放变换 -->
<scale
android:duration="3000"
android:fillAfter="true"
android:fromXScale="0.01"
android:fromYScale="0.01"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<!-- 定义透明度的变化 -->
<alpha
android:duration="3000"
android:fromAlpha="0.05"
android:toAlpha="1" />
<!-- 定义旋转变换 -->
<rotate
android:duration="3000"
android:fromDegrees="1800"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="0" />
</set>
image.gif
实例:蝴蝶飞舞
结合逐帧动画和补间动画。蝴蝶飞行时的振翅效果是逐帧动画,蝴蝶飞行时的位置改变时补间动画。
MainActivity
public class MainActivity extends AppCompatActivity {
// 记录蝴蝶ImageView当前的位置
private float curX;
private float curY = 30f;
// 记录蝴蝶ImageView下一个位置的坐标
private float nextX;
private float nextY;
private ImageView imageView;
private float screenWidth;
static class MyHandler extends Handler {
private WeakReference<MainActivity> activity;
MyHandler(WeakReference<MainActivity> activity) {
this.activity = activity;
}
@Override
public void handleMessage(Message msg) {
if (activity.get() != null && msg.what == 0x123) {
MainActivity act = activity.get();
// 横向上一直向右飞
if (act.nextX > act.screenWidth) {
act.nextX = 0f;
act.curX = act.nextX;
} else {
act.nextX += 8f;
}
// 纵向上可以随机上下
act.nextY = act.curY + (float) (Math.random() * 10 - 5);
// 设置显示蝴蝶的ImageView发生位移改变
TranslateAnimation anim = new TranslateAnimation(act.curX, act.nextX, act.curY, act.nextY);
act.curX = act.nextX;
act.curY = act.nextY;
anim.setDuration(200);
// 开始位移动画
act.imageView.startAnimation(anim); // ①
}
}
}
private Handler handler = new MyHandler(new WeakReference<>(this));
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Point p = new Point();
// 获取屏幕宽度
getWindowManager().getDefaultDisplay().getSize(p);
screenWidth = p.x;
// 获取显示蝴蝶的ImageView组件
imageView = findViewById(R.id.butterfly);
AnimationDrawable butterfly = (AnimationDrawable) imageView.getBackground();
imageView.setOnClickListener(view -> {
// 开始播放蝴蝶振翅的逐帧动画
butterfly.start(); // ②
// 通过定时器控制每0.2秒运行一次TranslateAnimation动画
new Timer().schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0x123);
}
}, 0, 200);
});
}
}
drawable/butterfly.xml
<?xml version="1.0" encoding="utf-8"?><!-- 定义动画循环播放 -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/butterfly_f01"
android:duration="120" />
<item
android:drawable="@drawable/butterfly_f02"
android:duration="120" />
<item
android:drawable="@drawable/butterfly_f03"
android:duration="120" />
<item
android:drawable="@drawable/butterfly_f04"
android:duration="120" />
<item
android:drawable="@drawable/butterfly_f05"
android:duration="120" />
<item
android:drawable="@drawable/butterfly_f06"
android:duration="120" />
</animation-list>
image.gif
自定义补间动画
自定义补间动画需要继承 ,继承
时重写该抽象基类的 applyTransformation(float interpolatedTime, Transformation t)
方法。
-
interpolatedTime
代表了动画的时间进行比。不管动画实际的持续时间如何,当动画播放时,该参数总是自动从 0 变化到 1. -
t
代表了补间动画在不同时刻对图形或组件的变形程度。
Transformation
代表了对图片或视图的变形程度,该对象里封装了一个 Matrix
对象,对它所包装的 Matrix
进行位移、倾斜、旋转等变换时,Transformation
将会控制对应的图片或视图进行相应的变换。
Camera
控制图片或视图进行三维空间的变换。常用方法:
getMatrix(Matrix matrix)
将 Camera
所做的变换应用到指定 matrix
上。
rotateX(float deg)
使目标组件沿X轴旋转。
rotateY(float deg)
使目标组件沿Y轴旋转。
rotateZ(float deg)
使目标组件沿Z轴旋转。
translate(float x, float y, float z)
使目标组件在三维空间里进行位移变换。
applyToCanvas(Canvas canvas)
把 Camera
所做的变换应用到 Canvas
上。
MainActivity
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取ListView组件
ListView list = findViewById(R.id.list);
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
DisplayMetrics metrice = new DisplayMetrics();
// 获取屏幕的宽和高
display.getMetrics(metrice);
// 设置对ListView组件应用动画
list.setAnimation(new MyAnimation(metrice.xdpi / 2,
metrice.ydpi / 2, 3500));
}
}
MyAnimation
public class MyAnimation extends Animation {
private float centerX;
private float centerY;
private int duration;
private Camera camera = new Camera();
public MyAnimation(float x, float y, int duration) {
this.centerX = x;
this.centerY = y;
this.duration = duration;
}
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
// 设置动画的持续时间
setDuration(duration);
// 设置动画结束后效果保留
setFillAfter(true);
setInterpolator(new LinearInterpolator());
}
/*
* 该方法的interpolatedTime代表了抽象的动画持续时间,不管动画实际持续时间多长
* interpolatedTime参数总是从0(动画开始时)变化到1(动画结束时)
* Transformation参数代表了对目标组件所做的改变
*/
@Override
public void applyTransformation(float interpolatedTime, Transformation t) {
camera.save();
// 根据interpolatedTime时间来控制X、Y、Z上的偏移
camera.translate(100.0f - 100.0f * interpolatedTime,
150.0f * interpolatedTime - 150,
80.0f - 80.0f * interpolatedTime);
// 设置根据interpolatedTime时间在Y轴上旋转不同角度
camera.rotateY(360 * interpolatedTime);
// 设置根据interpolatedTime时间在X轴上旋转不同角度
camera.rotateX(360 * interpolatedTime);
// 获取Transformation参数的Matrix对象
Matrix matrix = t.getMatrix();
camera.getMatrix(matrix);
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
camera.restore();
}
}
layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:entries="@array/bookArray" />
</LinearLayout>
values/arrays.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string-array name="bookArray">
<item>疯狂Java讲义</item>
<item>轻量级Java EE企业应用实战</item>
<item>疯狂Swift讲义</item>
<item>疯狂前端开发讲义</item>
<item>疯狂Android讲义</item>
</string-array>
</resources>
image.gif
摘抄至《疯狂Android讲义(第4版)》
网友评论