示例代码、思维导图可在此下载
https://github.com/jzj1993/AndroidAnimation
Animation(补间动画 / Tween Animation)
Animation通过对View的显示矩阵进行变换,实现了整个View的动画效果。Android提供的几种Animation,都有完整的XML支持,使用简单方便,功能较为全面。
Android提供的Animation包括:
- RotateAnimation,旋转动画
- TranslateAnimation,平移动画
- ScaleAnimation,缩放动画
- AlphaAnimation,透明度动画
下面是旋转动画示例:
res/anim/animation.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:interpolator="@android:interpolator/linear"
-
android:pivotX="50%"
-
android:pivotY="50%"
-
android:repeatCount="infinite"
-
android:repeatMode="restart"
-
android:toDegrees="360" />
-
Animation anim = AnimationUtils.loadAnimation(this, R.anim.animation);
-
mTextView.startAnimation(anim);
-
常用XML定义动画,用Java代码加载,并设置给View
-
也可以在Java代码中创建,参考工程源码
属性相关
-
from-to属性:动画起始、终止的位置(或角度、透明度等)
-
duration 动画持续时间,单位是ms
-
pivotX, pivotY 指定旋转的中心点。
pivotX="50%"
:50%
表示元素自身尺寸的50%
,50%p
表示父元素尺寸的50%
。注意:这里指定的中心点坐标是相对坐标原点而言的,而坐标原点在View的左上角。
假设想让View沿着父视图的中心旋转,由于坐标原点在View左上角,而不是父视图左上角,所以写成
50%p
是不能正常工作的(除非两者左上角重合)。可以在Java代码中进行计算。 -
repeatMode : reverse 反转; restart 重新开始
-
repeatCount : 5 循环五次;infinite 无限循环
Interpolator 插值器
用于控制动画播放速度,Android系统提供的常用Interpolator有:
- LinearInterpolator 匀速线性动画
- AccelerateInterpolator 加速动画
- DecelerateInterpolator 减速动画
- AccelerateDecelerateInterpolator,先加速再减速
- BounceInterpolator,弹跳形式
AnimationSet
AnimationSet继承自Animation,用于实现多个Animation叠加的效果。通常也在XML中定义。
res/anim/animation_set.xml
-
<?xml version="1.0" encoding="utf-8"?>
-
<set xmlns:android="http://schemas.android.com/apk/res/android"
-
android:duration="2000"
-
android:fillAfter="true">
-
<translate
-
android:fromXDelta="0"
-
android:fromYDelta="-50%p"
-
android:interpolator="@android:interpolator/accelerate_decelerate"
-
android:toXDelta="0"
-
android:toYDelta="0" />
-
<alpha
-
android:fromAlpha="0.5"
-
android:startOffset="2000"
-
android:toAlpha="1" />
-
<scale
-
android:fromXScale="1"
-
android:fromYScale="1"
-
android:pivotX="50%"
-
android:pivotY="50%"
-
android:startOffset="4000"
-
android:toXScale="2"
-
android:toYScale="2" />
-
</set>
-
// 返回的是AnimationSet实例,AnimationSet继承自Animation
-
Animation anim = AnimationUtils.loadAnimation(this, R.anim.animation_set);
-
mTextView.startAnimation(anim);
-
startOffset:默认set中所有动画是同时播放的,如果有设置startOffset,那一项就会延时播放。这样就可以实现一系列的动画效果了。
-
fillAfter:设置为true时,动画播放完成后停留在最后一帧,而不是复位到初始位置
-
android:fromYDelta="-50%p"
坐标原点还是左上角,但由于TranslateAnimation是平移运动,这里就可以直接用这种方式实现从父视图的上方中心位置开始移动。 -
注意:
android:fromAlpha="0.5"
虽然Alpha动画在2s时才开始执行,但是在0s的时候,View的Alpha值就已经被设置成fromAlpha的数值了。类似的,如果设置了Scale的初始值不为1,整个View的坐标比例也会发生变化。例如初始Scale为0.5,则50%p实际上只有父视图的25%。 -
AnimationSet继承自Animation,但它继承的一些属性比较特别。其中的一些属性,会直接被设置给它包含的每个Animation;而另外一些属性会被忽略;还有一些属性会被应用到AnimationSet本身。可参考AnimationSet中的JavaDoc:
- duration, repeatMode, fillBefore, fillAfter: These properties, when set on an AnimationSet object, will be pushed down to all child animations.
- repeatCount, fillEnabled: These properties are ignored for AnimationSet.
- startOffset, shareInterpolator: These properties apply to the AnimationSet itself.
使用Java代码实例化Animation
Animation anim = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, -0.2f, Animation.RELATIVE_TO_PARENT, -0.2f,
Animation.RELATIVE_TO_PARENT, -0.5f, Animation.RELATIVE_TO_PARENT, 0
);
anim.setFillAfter(true);
anim.setDuration(2000);
mTextView.setAnimation(anim);
CustomAnimation CustomIntepolator
自定义Animation
-
public class MyAnimation extends Animation {
-
private float mFromXValue = 0.0f;
-
private float mFromYValue = 0.0f;
-
private float mFromXDelta;
-
private float mFromYDelta;
-
public MyAnimation(float fromXDelta, float fromYDelta) {
-
mFromXValue = fromXDelta;
-
mFromYValue = fromYDelta;
-
}
-
@Override
-
public void initialize(int width, int height, int parentWidth, int parentHeight) {
-
super.initialize(width, height, parentWidth, parentHeight);
-
mFromXDelta = parentWidth * mFromXValue;
-
mFromYDelta = parentHeight * mFromYValue;
-
}
-
/**
-
* @param interpolatedTime 0~1f
-
* @param t
-
*/
-
@Override
-
protected void applyTransformation(float interpolatedTime, Transformation t) {
-
float dx = mFromXDelta + ((0 - mFromXDelta) * interpolatedTime);
-
float dy = mFromYDelta + ((0 - mFromYDelta) * interpolatedTime);
-
final Matrix matrix = t.getMatrix();
-
// pre-方法向前生长,post-方法向后生长,set-方法先清空已有变换,再进行变换
-
matrix.setTranslate(dx, dy);
-
}
-
}
-
initialize
方法中,对坐标尺寸相关内容进行初始化 -
applyTransformation
方法中,对矩阵Matrix
进行变换实现动画- interpolatedTime参数取值为0~1f,代表当前动画执行到的位置。
-
matrix.setTranslate(dx, dy)
,matrix.postRotate(180)
,matrix.preScale(scaleX, scaleY)
。post-方法向后生长(在先调用变换的基础上在做后调用的变换),pre-方法向前生长,set-方法先清空已有变换,再进行变换。如果要进行多个变换,最多使用一个set方法。 -
上面的代码实现了一个类似TranslateAnimation的自定义动画。
-
也可以在applyTransformation中执行其他操作,而不是调用Matrix的相关方法变换View。例如设置View的LayoutParam,实现View宽高的渐变动画;设置View的Margin,实现View在其Parent中位置的平移。
自定义Interpolator
-
public class MyInterpolator implements Interpolator {
-
float[] rawStep; // 1f, 0.5f, 0.5f, 0.25f, 0.25f, 0.125f, 0.125f, ...
-
float[] normalizedStep; // 1k, 0.5k, 0.5k, ...
-
float[] normalizedStepSum; // 0, 1k, (1+0.5)k, (1+0.5+0.5)k, ..., 1
-
float sum;
-
public MyInterpolator() {
-
final int k = 10;
-
rawStep = new float[2 * k + 1];
-
normalizedStep = new float[rawStep.length];
-
normalizedStepSum = new float[rawStep.length + 1];
-
rawStep[0] = 1f;
-
float f = 1f;
-
for (int i = 0; i < k; ++i) {
-
rawStep[2 * i] = f;
-
rawStep[2 * i + 1] = f;
-
f /= 2f;
-
}
-
sum = 0;
-
for (float f1 : rawStep) {
-
sum += f1;
-
}
-
normalizedStepSum[0] = 0;
-
for (int i = 0; i < rawStep.length; ++i) {
-
normalizedStep[i] = rawStep[i] / sum;
-
normalizedStepSum[i + 1] = normalizedStepSum[i] + normalizedStep[i];
-
}
-
}
-
public float getInterpolation(float input) {
-
float r = 1;
-
for (int i = 0; i < normalizedStep.length; ++i) {
-
if (input < normalizedStepSum[i + 1]) {
-
float x = (input - normalizedStepSum[i]) / normalizedStep[i]; // x = 0~1
-
if (i % 2 == 0) { // down
-
return accelerate(x, r);
-
} else { // up
-
return decelerate(x, r);
-
}
-
}
-
if (i % 2 == 0) {
-
r /= 2;
-
}
-
}
-
return input;
-
}
-
/**
-
* @param x
-
* @param range
-
* @return 1-range ~ 1
-
*/
-
float accelerate(float x, float range) {
-
return x * x * range + 1 - range;
-
}
-
/**
-
* @param x
-
* @param range
-
* @return 1-range ~ 1
-
*/
-
float decelerate(float x, float range) {
-
return (1 - x) * (1 - x) * range + 1 - range;
-
}
-
}
-
自定义Interpolator,需要实现Interpolator接口的getInterpolation方法。这个方法相当于一个数学函数,输入值、输出值范围都是0~1。
-
动画播放的时候,每刷新一帧就会调用一次getInterpolation方法,输入的参数从0到1线性递增;而返回值作为interpolatedTime参数传入到Animation的applyTransformation方法中。
-
LinearInterpolator的getInterpolation方法是一次函数,返回值等于输入值,所以返回值也会线性递增。
-
AccelerateInterpolator,则是对输入值求N次方(例如平方),所以就会有加速的效果。
-
-
上面的代码中,将整个动画播放过程进行了分段,每段加速或者减速,最后配合自定义的TranslateAnimation,实现了View下落弹跳的效果。实际效果参考工程源码。
AnimationListener 监听器
-
Animation anim = new MyAnimation(0f, -0.5f);
-
anim.setInterpolator(new MyInterpolator());
-
anim.setDuration(8000);
-
anim.setFillAfter(true);
-
anim.setAnimationListener(new Animation.AnimationListener() {
-
@Override
-
public void onAnimationStart(Animation animation) {
-
mTextView.setText("Animation");
-
}
-
@Override
-
public void onAnimationEnd(Animation animation) {
-
mTextView.setText("Hello World");
-
}
-
@Override
-
public void onAnimationRepeat(Animation animation) {
-
}
-
});
-
mTextView.startAnimation(anim);
LayoutAnimation,用于ViewGroup
XML方式实现
定义每个ChildView的动画
-
<?xml version="1.0" encoding="utf-8"?>
-
<set xmlns:android="http://schemas.android.com/apk/res/android"
-
android:duration="500">
-
<scale
-
android:fromXScale="0"
-
android:fromYScale="0"
-
android:pivotX="50%"
-
android:pivotY="50%"
-
android:toXScale="1"
-
android:toYScale="1" />
-
<alpha
-
android:fromAlpha="0"
-
android:toAlpha="1" />
-
</set>
定义LayoutAnimation
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/layout_animation_item"
android:animationOrder="normal"
android:delay="0.15" />
在ViewGroup中应用LayoutAnimation
<LinearLayout android:layoutAnimation="@anim/layout_animation" >
</LinearLayout>
ViewGroup加载时,按照animationOrder属性指定的顺序(顺序、倒序、随机),delay属性指定的时延,让每个ChildView依次显示动画。
Java创建LayoutAnimation
-
Animation anim = AnimationUtils.loadAnimation(this, R.anim.layout_animation_item);
-
LayoutAnimationController lac = new LayoutAnimationController(anim);
-
lac.setOrder(LayoutAnimationController.ORDER_REVERSE);
-
lac.setDelay(1);
-
mListView.setLayoutAnimation(lac);