示例代码、思维导图可在此下载
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);