Android 动画总结
项目中用到动画的地方也比较多,对于动画总的概念比较笼统。没有一个宏观体系上的认识。其实说实话,自己也没有对所有的动画都使用过一遍,但是还是有必要总结一下动画的知识。
动画分类
Android提供了丰富的动画API,总得来说可以分为三类:
- View Animation:视图动画,这种动画我们再熟悉不过了,用来设置View的一些缩放、旋转、平移动画
- Drawable Animation: 这种动画我们通常叫做帧动画(或者Frame动画),用来显示一帧一帧的Drawablw。类似于git图片一样
- Properties Animation:属性动画,在3.0以上API才有提供,这种动画可以设置到任何对象中,可以自定义任何类型和属性的动画,如果要交融低版本的,可以使用兼容动画库
View Animation 的使用方法
视图动画可以在一个视图容器内做一系列的简单变换,如平移、缩放、大小、透明度。这种动画可以通过两种方式来定义,分为JAVA代码方式和XML文件的方式
JAVA类名 | XML节点 | 描述 |
---|---|---|
AlphaAnimation | <alpha> (/res/anim/目录) | 透明度动画 |
RotateAnimation | <rotate> (/res/anim/目录) | 旋转动画 |
ScaleAnimation | <scale> (/res/anim/目录) | 缩放动画 |
TranslateAnimation | <translate> (/res/anim/目录) | 平移动画 |
各种动画的共有属性
动画通常都有一些属性可以设置,下面是java方式和xml方式对属性定义的对比:
JAVA方法 | XML属性 | 描述 |
---|---|---|
setDetachWallpaper(boolean) | android:detachWallpaper | 是否在壁纸上运行 |
setDuration(long) | android:duration | 动画持续的时间 |
setFillAfter(boolean) | andorid:fillAfter | 动画结束的时候是否保持动画的状态 |
setInterpolator(Interpolator) | android:interpolator | 设置动画的插值器 |
setRepeatCount(int) | android:repeatCount | 动画的重复次数 |
setRepeatMode(int) | andorid:repeatMode | 动画的重复类型,有2个值,reverse 表示倒叙回放,restart表示从头播放 |
setStartOffset(long) | android:startOffset | 调用start函数之后等待开始运行的时间,单位为毫秒 |
setZAdjustment(int) | android:zAdjustment | 表示被设置动画的内容运行时在Z轴上的位置(top/bottom/normal),默认为normal |
Alpha特有属性
public AlphaAnimation(float fromAlpha, float toAlpha)
JAVA构造函数参数 | XML属性 | 描述 |
---|---|---|
fromAlpha | android:fromAlpha | 动画开始的透明度(0.0-1.0,0.0是全透明,1.0是不透明) |
toAlpha | android:toAlpha | 动画结束的透明度 |
Rotate特有属性
public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
int pivotYType, float pivotYValue)
JAVA构造函数参数 | XML属性 | 描述 |
---|---|---|
fromDegrees | android:fromDegrees | 旋转开始的角度 |
toDegrees | android:toDegrees | 旋转的结束角度 |
pivotXType | 无 | 旋转起点X坐标的相对位置,有两个值Animation.RELATIVE_TO_PARENT(相对父控件) |
pivotXValue | android:pivotX | 缩放起点X坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) |
pivotYType | 无 | 和pivotXType一样 |
pivotYValue | android:pivotY | 和pivotXValue一样 |
Scale特有属性
public ScaleAnimation(float fromX, float toX, float fromY, float toY,
int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)
JAVA构造函数参数 | XML属性 | 描述 |
---|---|---|
fromX | android:fromX | 初始X轴缩放比例,1.0表示无变化 |
toX | android:toX | 结束时X轴的缩放比例 |
fromY | android:fromY | 初始Y轴缩放比例,1.0表示无变化 |
toY | android:toY | 结束时Y轴的缩放比例 |
privotXType 等属性和 Rotate的属性相同 |
Translate特有属性
public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta)
JAVA构造函数参数 | XML属性 | 描述 |
---|---|---|
fromXDelta | android:fromXDelta | 起始点X轴坐标(数值、百分数、百分数p,譬如50表示以当前View左上角坐标加50px为初始点、50%表示以当前View的左上角加上当前View宽高的50%做为初始点、50%p表示以当前View的左上角加上父控件宽高的50%做为初始点) |
toXDelta | android:toXDelta | 结束时X轴坐标,同上规律 |
fromYDelta | android:fromYDelta | 起始点Y轴坐标,同上规律 |
toYDelta | android:toYDelta | 结束时Y轴的坐标,同上规律 |
AnimationSet属性
AnimationSet其实是一个属性的管理集合,当对AnimationSet使用属性的时候会对所有的子动画都产生影响,它的xml标签为 这个标签包含该上面4中动画的标签
Animation使用方式
下面是一种xml的方式声明XML之后:
View view = someview;
Animation anim = new Alpha|Scale|Rotate|Translate| AnimationUtils.loadAnimtion(R.anim.xxx);
view.startAnimtion(anim);
其中Animation类还有一些方法供我们使用:
- reset() 重置动画
- cancel() 取消动画
- start() 开始动画
- setAnimationListener() 设置监听器
- hasStarted() 判断是否开始
- hasEnded() 判断是否结束
View类同样有2个处理Animation的方法:
- startAnimation() 开启动画
- clearAnimation() 取消动画
Animation使用注意事项
首先一个View如果开启的动画,他是不能从父View中移除的。我们需要先执行clearAnimation()之后才能移除。其次我们要明白一个道理:Animation改变的不是View的位置,而是View的内容,就是说View的布局边界是固定不变的。
Animation拓展之插值器(Interpolator)
在使用Animation的过程中可以通过setInterpolator或者声明android:interpolator来使用插值器,先看看SDK提供的一些插值器
JAVA类 | 描述 |
---|---|
AccelerateDecelerateInterpolator | 动画初始和末尾速度较慢,中间会加速 |
AccelerateInterpolator | 动画开始速率较慢,之后慢慢加速 |
AnticipateInterpolator | 开始的时候从后向前甩 |
AnticipateOvershootInterpolator | 类似上面AnticipateInterpolator |
BounceInterpolator | 动画结束时弹起 |
CycleInterpolator | 循环播放速率改变为正弦曲线 |
DecelerateInterpolator | 动画开始快然后慢 |
LinearInterpolator | OvershootInterpolator |
OvershootInterpolator | OvershootInterpolator |
PathInterpolator | 新增,定义路径坐标后按照路径坐标来跑 |
插值器宏观上来理解就是说一个动画在开始到结束的时候,中间这个执行过程根据什么规律来变化。插值器都实现了一个接口Interpolator
我们来看看LinearInterpolator的实现
public float getInterpolation(float input) {
return input;
}
这里尝试用一个例子解释,比如我们定义一个平移动画,从 x = 0 变化到 x = 100,时间为4秒,那么在不同的时间点的时候View应该移动多少就是通过插值器来计算的。
Interpolator的getInterpolation方法就是返回应该移动位置的百分比,input参数的变化值为0.0-1.0 假设我们现在动画执行了1秒 那么input = 1 / 4 = 0.25 ; 那么这个方法的返回值就是0.25,那么该移动的位置就是 (100 - 0) * 0.25 = 25 这个函数会不停的调用(默认动画是10ms一帧),就这样来实现动画的变化效果,假如我们要实现其他的一些效果,只需要改变getInterpolation的实现即可。
我们看一看加速的实现:
public float getInterpolation(float input) {
if (mFactor == 1.0f) {
return input * input;
} else {
return (float)Math.pow(input, 2);
}
}
其实返回的就是 input的平方,当input从 0-1变化的时候,返回值的变化曲线是加速的。
Drawable Animation
Drawable动画其实就是播放几帧动画,定义的时候需要在/res/drawable/目录创建,通常使用XML的方式创建
<animation-list>
必须是根节点,包含一个或者多个元素,属性有:- android:oneshot true代表只执行一次,false循环执行。
- 类似一帧的动画资源。
<item>
animation-list的子项,包含属性如下:- android:drawable 一个frame的Drawable资源。
- android:duration 一个frame显示多长时间。
帧动画只能作用在ImageView上:
ImageView iv = xx;
iv.setBackgroundResource(R.drawable.anim);
AnimationDrawable anim = (AnimationDrawable)iv.getBackground();
anim.start();
注意:start()方法不能执行在onCreate函数中,因为AnimationDrawable还没完全出现在Window上,最好的调用时机应该是在onWindowFocusChanged()方法中。
Properties Animation 属性动画
属性动画的介绍以及原理
属性动画包括几个对象ValueAnimator、TimeAnimator、ObjectAnimator,下面我们来看看属性动画的几个属性:
- duration 动画持续时间
- TimeInterpolator 同上面的Interpolator一样
- TypeEvaluator 属性值得计算方式,有int,color,float类型的。根据属性的起始值、结束值以及当前的插值(插值器返回),计算当前时间点的属性值
ValueAnimator是属性动画的执行类,它封装了一个时间插值器和一个TimeInterpolator类型估值器,用于计算最终的结果。
插值器上面已经说过了,我们可以通过插值器根据时间因子(当前已用时间占总时间的百分比,就是getInterpolation的input参数),来得到最终的值因子(也是0-1,表示当前值应该变化的比例),最后我们得出这个比例之后然后就使用估值器来计算出具体的属性值。
我们再举一个例子:
要将一个View的x从 0 移动到100,耗时为10秒,使用的是AccelerateDecelerateInterpolator和IntEvaluator,我们的插值器的运算结果是先加速后减速。
看一下他们的代码:
//AccelerateDecelerateInterpolator
public class AccelerateDecelerateInterpolator extends BaseInterpolator
implements NativeInterpolatorFactory {
public AccelerateDecelerateInterpolator() {
}
......
//这是我们关注重点,可以发现如下计算公式计算后(input即为时间因子)插值大约为0.15。
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
}
//IntEvaluator
public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
可以预知,差值器返回的值是的变化曲线是一个先加速后减速的过程。这样,再通过IntEvaluator来得到具体的属性值,假设在一个时间点插值器返回的值是0.5,那么估值器返回值就是:(int)(0 + 0.5 * (100 - 0)) = 50 ,这个50就是这个时间点的x的位置坐标。
属性动画的使用方式
这是只介绍使用JAVA代码的使用方式:
ObjectAnimator
ObjectAnimator继承自VauleAnimator,允许制定一个属性值做变化,提供了一系列的ofInt ofFloat onArgb 来方便我们的使用,他们分别会使用不同的估值器。
public static Animator ofInt(View target,Stirng properName,int... values);
- target 执行动画的View
- properName 对应的属性,View必须对该属性实现了getXXX,setXXX方法才生效。比如View默认的属性都可以:alpha translateX|Y rotateX等
- values 如果只有一个值,这个值为结束值,用getXXX作为起始值,如果有2个值,一个为起始值,一个为结束值
eg:
ObjectAnimator.ofFloat(view, "rotationY", 0.0f, 360.0f).setDuration(1000).start();
PropertyValuesHolder
多属性动画同时工作管理类。有时候我们需要同时修改多个属性,那就可以用到此类,具体如下:
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", scale);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", scale);
ObjectAnimator objectAnimator =
ObjectAnimator.ofPropertyValuesHolder(mFloatingActionButton, scaleX, scaleY)
.setDuration(500);
objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
ValueAnimator
属性动画中的时间驱动,只负责属性动画的起始结束时相应属性的计算,一些动画的基本属性,和上面两个属性动画不同的是,此类属性动画并没有改变View的动画,它只包含随着时间变化的属性值结果。需要使用UpdateListener手动设置属性才生效
ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight); //定义动画
animator.setTarget(view); //设置作用目标
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation){
float value = (float) animation.getAnimatedValue();
view.setXXX(value); //必须通过这里设置属性值才有效
view.mXXX = value; //不需要setXXX属性方法
}
});
属性动画拓展之容器布局动画(抄袭的^_^!)
Property动画系统还提供了对ViewGroup中View添加时的动画功能,我们可以用LayoutTransition对ViewGroup中的View进行动画设置显示。LayoutTransition的动画效果都是设置给ViewGroup,然后当被设置动画的ViewGroup中添加删除View时体现出来。该类用于当前布局容器中有View添加、删除、隐藏、显示等时候定义布局容器自身的动画和View的动画,也就是说当在一个LinerLayout中隐藏一个View的时候,我们可以自定义 整个由于LinerLayout隐藏View而改变的动画,同时还可以自定义被隐藏的View自己消失时候的动画等。
XML方式使用系统提供的默认LayoutTransition动画:
我们可以通过如下方式使用系统提供的默认ViewGroup的LayoutTransition动画:
android:animateLayoutChanges=”true”
在ViewGroup添加如上xml属性默认是没有任何动画效果的,因为前面说了,该动画针对于ViewGroup内部东东发生改变时才有效,所以当我们设置如上属性然后调运ViewGroup的addView、removeView方法时就能看见系统默认的动画效果了。
还有一种就是通过如下方式设置:
android:layoutAnimation=”@anim/customer_anim”
通过JAVA设置:
mTransitioner = new LayoutTransition();
mViewGroup.setLayoutTransition(mTransitioner);
总结
上面对于三大类动画的基本使用以及一些显浅的原理进行了分析,其实还有很多动画的工具类没有去分析。总得来说,做完一个动画的总结之后感觉真的很nice。