Android动画详解
Android框架提供了两个动画系统,属性动画(property animation )和视图动画(view animation)。在一般情况下我们大多使用属性动画,因为它更灵活,并提供更多的功能。另外,还有一种帧动画,它通过加载图片资源,将多张图片一帧一帧显示形成动画效果。
1.视图动画(View Animation)
视图动画在很早的Android系统中就已提供,它只能用来设置View的动画。视图动画也叫补间动画,通过xml文件或java代码定义。
1-1.属性详解
在补间动画中主要可以实现视图的位移(translate)、缩放(scale)、旋转(rotate)、淡入淡出(alpha)四种效果。其各的主要属性如下:
Animation属性详解
xml属性 | java方法 | 解释 |
---|---|---|
android:detachWallpaper | setDetachWallpaper(boolean) | 是否在壁纸上运行 |
android:duration | setDuration(long) | 动画持续时间,毫秒为单位 |
android:fillAfter | setFillAfter(boolean) | 控件动画结束时是否保持动画最后的状态 |
android:fillBefore | setFillBefore(boolean) | 控件动画结束时是否还原到开始动画前的状态 |
android:fillEnabled | setFillEnabled(boolean) | 与android:fillBefore效果相同 |
android:interpolator | setInterpolator(Interpolator) | 设定插值器(指定的动画效果,譬如回弹等) |
android:repeatCount | setRepeatCount(int) | 重复次数 |
android:repeatMode | setRepeatMode(int) | 重复类型有两个值,reverse表示倒序回放,restart表示从头播放 |
android:startOffset | setStartOffset(long) | 调用start函数之后等待开始运行的时间,单位为毫秒 |
android:zAdjustment | setZAdjustment(int) | 表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal |
Alpha属性详解
xml属性 | java方法 | 解释 |
---|---|---|
android:fromAlpha | AlphaAnimation(float fromAlpha, …) | 动画开始的透明度(0.0到1.0,0.0是全透明,1.0是不透明) |
android:toAlpha | AlphaAnimation(…, float toAlpha) | 动画结束的透明度,同上 |
Rotate属性详解
xml属性 | java方法 | 解释 |
---|---|---|
android:fromDegrees | RotateAnimation(float fromDegrees, …) | 旋转开始角度,正代表顺时针度数,负代表逆时针度数 |
android:toDegrees | RotateAnimation(…, float toDegrees, …) | 旋转结束角度,正代表顺时针度数,负代表逆时针度数 |
android:pivotX | RotateAnimation(…, float pivotX, …) | 缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) |
android:pivotY | RotateAnimation(…, float pivotY) | 缩放起点Y坐标,同上规律 |
Translate属性详解
xml属性 | java方法 | 解释 |
---|---|---|
android:fromXDelta | TranslateAnimation(float fromXDelta, …) | 起始点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) |
android:fromYDelta | TranslateAnimation(…, float fromYDelta, …) | 起始点Y轴从标,同上规律 |
android:toXDelta | TranslateAnimation(…, float toXDelta, …) | 结束点X轴坐标,同上规律 |
android:toYDelta | TranslateAnimation(…, float toYDelta) | 结束点Y轴坐标,同上规律 |
Scale属性详解
xml属性 | java方法 | 解释 |
---|---|---|
android:fromXScale | ScaleAnimation(float fromX, …) | 初始X轴缩放比例,1.0表示无变化 |
android:toXScale | ScaleAnimation(…, float toX, …) | 结束X轴缩放比例 |
android:fromYScale | ScaleAnimation(…, float fromY, …) | 初始Y轴缩放比例 |
android:toYScale | ScaleAnimation(…, float toY, …) | 结束Y轴缩放比例 |
android:pivotX | ScaleAnimation(…, float pivotX, …) | 缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) |
android:pivotY | ScaleAnimation(…, float pivotY) | 缩放起点Y坐标,同上规律 |
1-2.xml文件中定义
1.xml定义动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
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>
2.在java中使用动画资源
Animation animation = AnimationUtils.loadAnimation(context,R.anim.view_anin);
image.startAnimation(animation);
1-3.java文件中定义
RotateAnimation rotateAnimation = new RotateAnimation(0,180);
rotateAnimation.setDuration(400);
image.startAnimation(rotateAnimation);
2.帧动画(Drawable Animation)
它通过加载图片资源,将多张图片一帧一帧显示形成动画效果。在Android中提供了两种方式为AnimationDrawable添加帧:XML定义的资源文件和Java代码创建。
2-1.AnimationDrawable的常用方法:
-
void start()
:开始播放逐帧动画。 -
void stop()
:停止播放逐帧动画。 -
void addFrame(Drawable frame,int duration)
:为AnimationDrawable添加一帧,并设置持续时间。 -
int getDuration(int i)
:得到指定index的帧的持续时间。 -
Drawable getFrame(int index)
:得到指定index的帧Drawable。 -
int getNumberOfFrames()
:得到当前AnimationDrawable的所有帧数量。 -
boolean isOneShot()
:当前AnimationDrawable是否执行一次,返回true执行一次,false循环播放。 -
boolean isRunning()
:当前AnimationDrawable是否正在播放。 -
void setOneShot(boolean oneShot)
:设置AnimationDrawable是否执行一次,true执行一次,false循环播放。
2-2.在xml文件中定义资源文件
1.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/loading_1" android:duration="100"/>
<item android:drawable="@drawable/loading_2" android:duration="100"/>
<item android:drawable="@drawable/loading_3" android:duration="100"/>
<item android:drawable="@drawable/loading_4" android:duration="100"/>
<item android:drawable="@drawable/loading_5" android:duration="100"/>
<item android:drawable="@drawable/loading_6" android:duration="100"/>
<item android:drawable="@drawable/loading_7" android:duration="100"/>
<item android:drawable="@drawable/loading_8" android:duration="100"/>
<item android:drawable="@drawable/loading_9" android:duration="100"/>
<item android:drawable="@drawable/loading_10" android:duration="100"/>
<item android:drawable="@drawable/loading_11" android:duration="100"/>
<item android:drawable="@drawable/loading_12" android:duration="100"/>
</animation-list>
2.在java中调用
// 通过逐帧动画的资源文件获得AnimationDrawable示例
AnimationDrawable animationDrawable =(AnimationDrawable) getResources().getDrawable(R.drawable.animation_loading);
image.setImageDrawable(animationDrawable);
animationDrawable.start();
2-3.使用java定义资源文件
AnimationDrawable drawable = new AnimationDrawable();
drawable.addFrame(getResources().getDrawable(R.drawable.loading_1),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_2),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_3),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_4),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_5),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_6),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_7),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_8),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_9),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_10),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_11),100);
drawable.addFrame(getResources().getDrawable(R.drawable.loading_12),100);
drawable.setOneShot(true);
image.setImageDrawable(drawable);
drawable.start();
3.属性动画(Property Animation)
属性动画只对Android 3.0(API 11)以上版本的Android系统才有效,这种动画可以设置给任何Object,包括那些还没有渲染到屏幕上的对象。这种动画是可扩展的,可以让你自定义任何类型和属性的动画。
3-1.属性动画允许我们定义以下的动画特点:
- Duration:可以指定动画的持续时间。默认长度为300毫秒;
- Time interpolation:可以指定该属性的值如何作为动画的当前的运行时间的函数来计算;
- Repeat count and behavior:指定动画的播放次数,还可定义是否反向播放动画;
- Animator sets:动画集合,即可以同时对一个对象应用多个动画,这些动画可以同时播放也可以对不同动画设置不同的延迟;
- Frame refresh dela:多少时间刷新一次动画,即每隔多少时间计算一次属性值,默认10ms。最终取决于系统整体多么繁忙,系统如何能够快速的服务基础计时器。
3-1.属性动画的工作原理:
3-1-1.实例描述
图1. Example of a linear animation如图1所示的就是一个线性动画实例,描述了一个Object的x属性随时间匀速增长,也就是x方向的坐标每10ms就移动10个像素。
图2.Example of a non-linear animation
如图2所示的是一个非线性动画实例,Object的x属性同样是没10ms刷新一次,但在开始的时候,这个动画加速到中间点,然后从中途减速,直至动画结束。
3-1-2.属性动画主类之间的相互合作
图3.How animations are calculatedValueAnimator:是动画的执行类,跟踪动画的时间,并且它是动画的属性的当前值。
-
TimeInterpolator
:ValueAnimator
封装了动画的TimeInterpolator
时间插值器,定义动画的插入。 -
TypeEvaluator
:用于设置动画属性的值
要启动动画,需要创建一个ValueAnimator,并且指定目标对象属性的开始、结束值和持续时间。在调用start后,整个动画过程中, ValueAnimator会根据已经完成的动画时间计算得到一个0到1之间的分数,代表该动画的已完成动画百分比。0表示0%,1表示100%。
当ValueAnimator计算完已完成动画分数后,它会调用当前设置的TimeInterpolator,去计算得到一个interpolated(插值)分数,在计算过程中,已完成动画百分比会被加入到新的插值计算中。
当插值分数计算完成后,ValueAnimator
会根据插值分数调用合适的 TypeEvaluator
去计算运动中的属性值。
3-2.属性动画的具体使用:
3-2-1.ValueAnimator
ValueAnimator
是属性动画的核心类,但平时用的时候好像不常接触。使用的时候需要添加监听AnimatorUpdateListener
监听,在监听方法中对需要执行动画的对象设置当前的属性值。
ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();
animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator updatedAnimation) {
// You can use the animated value in a property that uses the
// same type as the animation. In this case, you can use the
// float value in the translationX property.
float animatedValue = (float)updatedAnimation.getAnimatedValue();
textView.setTranslationX(animatedValue);
}
});
3-2-2.ObjectAnimator
ObjectAnimato
r是ValueAnimator
的一个子类,相对于ValueAnimator
,ObjectAnimator
我们会用得更多一点。
使用:
ObjectAnimator.ofFloat(textview,"rotationX",0.0f,180f)
.setDuration(100)
.start();
对于ObjectAnimator..ofFloat(Object target,String propertyName,float... values)
,其中第一个参数代表需要作用到的View身上,第二个参数代表需要执行的动画的属性名,第三个可变参数代表属性值。
需要注意参数名不要写错,不过可以写一个该动画没有的属性,然后自己在AnimatorUpdateListener
的onAnimationUpdate
方法中设置多种属性值,一次性实现淡入淡出、翻转等多种效果,自己手动调用,如:
ObjectAnimator anim = ObjectAnimator//
.ofFloat(view, "zhy", 1.0F, 0.0F)//
.setDuration(500);//
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float cVal = (Float) animation.getAnimatedValue();
view.setAlpha(cVal);
view.setScaleX(cVal);
view.setScaleY(cVal);
}
});
3-2-3.AnimatorSet组合动画
在很多情况下,我们不可能只对对象执行一种动画,可能需要移动的时候还要翻转,或者淡入淡出。Android系统就为我们提供了AnimatorSet这个类,并且会提供了以下四个方法帮助我们进行动画的组合:
- after(Animator anim) 将现有动画插入到传入的动画之后执行
- after(long delay) 将现有动画延迟指定毫秒后执行
- before(Animator anim) 将现有动画插入到传入的动画之前执行
- with(Animator anim) 将现有动画和传入的动画同时执行
使用
ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);
ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);
AnimatorSet animSet = new AnimatorSet();
animSet.play(rotate).with(fadeInOut).after(moveIn);
//三个动画同时执行
//animSet.playTogether(moveIn,rotate,fadeInOut);
animSet.setDuration(5000);
animSet.start();
3-2-4.使用xml声明动画
通过自定义的xml进行声明动画,可以进行动画的重用,更轻松地编辑动画序列。
为了区分使用来自那些使用传统的新属性动画的API动画文件(视图动画)框架,从Android 3.1开始,属性动画的xml文件应保存在res/animator/
目录下。
使用:
1.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together" >
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="0.5" >
</objectAnimator>
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="0.5" >
</objectAnimator>
</set>
2.java:中调用
// 加载动画
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scale);
mMv.setPivotX(0);
mMv.setPivotY(0);
//显示的调用invalidate
mMv.invalidate();
anim.setTarget(mMv);
anim.start();
3-3.Animator监听器
Animator
提供了一个addListener()
的方法,这个方法接收一个AnimatorListener
,我们只需要去实现这个AnimatorListener
就可以监听动画的各种事件了。
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
//动画启动时调用
}
@Override
public void onAnimationEnd(Animator animation) {
//当动画结束时调用
}
@Override
public void onAnimationCancel(Animator animation) {
//当动画被取消调用,被取消的动画还会调用onAnimationEnd方法
}
@Override
public void onAnimationRepeat(Animator animation) {
//当动画重演调用
}
});
最后再了解一下插值器和估值器。
4.插值器(Interpolator)与估值器(TypeEvaluator)
插值器和估值器都是实现复杂动画的关键。其中插值器主要用来设置 属性值 从初始值过渡到结束值 的变化规律 ,如加速,回弹等效果。估值器设置 属性值 从初始值过渡到结束值 的变化具体数值 。插值器(Interpolator)决定 值 的变化规律(匀速、加速blabla),即决定的是变化趋势;而接下来的具体变化数值则交给
而估值器 。
插值器(Interpolator)
Android系统为我们提供了九种内置的插值器,如下表:
xml资源ID | java类 | 描述 |
---|---|---|
@android:anim/accelerate_interpolator | AccelerateInterpolator | 动画加速进行 |
@android:anim/overshoot_interpolator | OvershootInterpolator | 快速完成动画,超出再回到结束样式 |
@android:anim/accelerate_decelerate_interpolator | AccelerateDecelerateInterpolator | 先加速再减速 |
@android:anim/anticipate_interpolator | AnticipateInterpolator | 先退后再加速前进 |
@android:anim/anticipate_overshoot_interpolator | AnticipateOvershootInterpolator | 先退后再加速前进,超出终点后再回终点 |
@android:anim/bounce_interpolator | BounceInterpolator | 最后阶段弹球效果 |
@android:anim/cycle_interpolator | CycleInterpolator | 周期运动 |
@android:anim/decelerate_interpolator | DecelerateInterpolator | 减速 |
@android:anim/linear_interpolator | LinearInterpolator | 匀速 |
插值器在动画的使用有xml和java两种方式。
在xml中使用:
1.设置插值器 android:interpolator属性
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<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>
2.在java中调用
Animation animation = AnimationUtils.loadAnimation(context,R.anim.view_anin);
image.startAnimation(animation);
其实就是在上面的补间动画里加了个android:interpolator属性就是。
在java中使用
TranslateAnimation rotateAnimation = new TranslateAnimation(0,200,0,200);
rotateAnimation.setDuration(400);
//创建对于的插值器
OvershootInterpolator interpolator = new OvershootInterpolator();
//设置插值器
rotateAnimation.setInterpolator(interpolator);
image.startAnimation(rotateAnimation);
自定义插值器
本质:根据动画的进度(0%-100%)计算出当前属性值改变的百分比。
补间动画实现Interpolator
接口。
属性动画实现TimeInterpolator
接口。
实现Interpolator
接口
public class MyInterpolator implements Interpolator {
@Override
public float getInterpolation(float input) {
Log.d("print", "getInterpolation: " + input);
return input;
}
}
实现TimeInterpolator
接口
public class MyInterpolator implements TimeInterpolator {
@Override
public float getInterpolation(float input) {
Log.d("print", "getInterpolation: " + input);
return input;
}
}
其中input为0~1.0之前的数字,表示当前属性变化的百分比。返回的值就是用于估值器继续计算的fraction值,可以大于1。如:当你return为0时,动画将静止在初始位置。当你return为1时,动画开始后将跳到你设置的终止位置停留知道动画时间停止后回到初始位置。
估值器(TypeEvaluator)
系统内置了3个估值器,分别是:
- IntEvaluator:整数属性值。
- FloatEvaluator:浮点型属性值。
- ArgbEvaluator:十六进制color属性值。
使用:
ObjectAnimator objectAnimator = ObjectAnimator.ofObject(image,"alpha",new IntEvaluator(),1,0,1);
objectAnimator.setDuration(500);
objectAnimator.start();
自定义估值器:
需要实现TypeEvaluator
接口:
public class MyTypeEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
// fraction:插值器getInterpolation()的返回值
// startValue:动画的初始值
// endValue:动画的结束值
Log.d("print", "fraction: " + fraction + "\nstartValue "+ startValue + " \nendValue" + endValue);
return ((Integer)startValue + ((Integer)endValue - (Integer)startValue) * fraction);
}
}
本文参考文章:
Android应用开发之所有动画使用详解
Android属性动画详解
Android插值器与估值器分析
官网文档:android developer