当我们看到别人的APP里面或者写的Demo里面的一些很牛逼得动画效果的时候,是否会有那么一种冲动我要看看它的源代码到底是怎么写的,为毛我就是写不出这样的动画效果呢,为毛我只能做哪些简单的平移,缩放,旋转,透明度改变的动画效果呢。呵呵,当你这么想的时候,证明你应该存在两种主要的问题:第一,就是你的确基础知识不过关,别人用到的东西,你根本就没有接触过,但是这些东西的确是API里面确实存在的东西。第二,就是你的经验不够,逻辑思维能力还有待提升。任何一个很酷很炫的动画效果,都应该是由一系列的动画效果合成的,这个时候就看你怎么安排和处理每一个小的动画效果最后合成你看到的动画效果。下面先介绍Android中的几种动画,最后会对属性动画(Property
Animation)做详细的介绍:
补间动画(Tween Animation):
所谓的补间动画,个人理解其实就是定义了我们动画的起始点和终止点的状态,而动画的过程我们是不关心的,只需要达到我们想要的效果就行。
a. 渐变动画支持四种类型:平移(Translate)、旋转(Rotate)、缩放(Scale)、不透明度(Alpha)。
b. 只是显示的位置变动,View的实际位置未改变,表现为View移动到其他地方,点击事件仍在原处才能响应。
c. 组合使用步骤较复杂。
d. View Animation 也是指此动画。
帧动画(Frame Animation):
所谓的帧动画就是可以设置我们的动画的每一帧的效果,其实视频或者Gif的效果都是由许多张图片在很短的时间内播放,从而产生动画效果。
a. 用于生成连续的Gif效果图。
b.DrawableAnimation也是指此动画。
属性动画(Property Animation)
a. 支持对所有View能更新的属性的动画(需要属性的setXxx()和getXxx())。
b. 更改的是View实际的属性,所以不会影响其在动画执行后所在位置的正常使用。
c. Android3.0 (API11)及以后出现的功能,3.0之前的版本可使用github第三方开源库nineoldandroids.jar进行支持。
属性动画的相关的API:
ValueAnimator :值动画执行类,常配合AnimatorUpdateListener使用。
ObjectAnimator :对象动画执行类。
PropertyValuesHolder: 属性存储器,为两个执行类提供更新多个属性的功能。
AnimatorListener :动画执行监听,在动画开始、重复、结束、取消时进行回调。
Keyframe :为PropertyValuesHolder提供多个关键帧的操作值。
AnimatorSet :一组动画的执行集合类:设置执行的先后顺序,时间等。
TimeInterpolator :时间插值,用于控制动画执行过程。
AnimatorUpdateListener:动画更新监听。
TypeEvaluator :类型估值,用于设置复杂的动画操作属性的值。
ValueAnimator和ObjectAnimator是属性动画里面经常使用的对象类,ObjectAnimator是ValueAnimator的子类,看看ValueAnimator的类结构:
在ValueAnimator类中经常用到的几个方法:
public static ValueAnimator ofInt(int... values){ } //设置类型为int的属性的值,例如X,Y坐标,可变长的数组值,设置属性值在之间变化
public static ValueAnimator ofFloat(float... values) { } //设置类型为float的属性的值,例如alpha,translationX,translationY等等属性
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) //当我们对某一个控件的多个属性实行动画的时候 可以用PropertyValuesHolder保存每一个属性的动画效果,最后调用这个方法组合起来,参数也是一个可变长的。
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) //设置某个对象的动画 在后面做详细介绍
1.ObjectAnimator
下面先讲讲ObjectAnimator的用法(因为实际中这个用的更多。PS:完全个人感觉)
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 0f,200f); oa.setDuration(1000); oa.start();
上面代码实现的是改变控件iv的translationX的值,从0到200,0f表示从0开始使X坐标平移,这个值可以自己设置,当我们是指的值加入设定为100f的时候,给我们的感觉就是控件iv首先跳到X坐标为100f处(这个100指的是相对于控件坐标的左上角为原点)
左图为0f,200f时的效果,右图为100f,200f的效果,明显感觉右侧的动画突然跳跃了一段距离。
其他各种属性的值,我们可以去对应控件的类或者顶级View的源码里面查看,不再一一赘述了。
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 0f,100f); oa.setDuration(2000); //设置重复次数 次数为-1的时候重复模式INFINITE才有作用意思是一直重复执行 oa.setRepeatCount(-1); //设置重复的模式 INFINITE 无限重复执行 RESTART 从开始重复执行 REVERSE 反转重复执行(类似反着执行一次) oa.setRepeatMode(ObjectAnimator.REVERSE); //动画在延迟1000毫秒之后开始 oa.setStartDelay(1000); //开始动画 oa.start();
上面的动画效果是重复执行,注释很清楚了。
2.PropertyValueHolder
他可以设置多个动画同时作用于同一个空间,有点类似于补间动画的动画集合AnimationSet
//属性存储器 PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 0,100,200,300); //缩放动画,X Y轴缩放比例从原始比例,缩小到最小,再放大到原始比例1f 再又缩放到最小 最后放大到最大 PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f,0f,1f,0f,1f); PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f,0f,1f,0f,1f); //同时执行三个动画 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(iv,pvh1,pvh2,pvh3); oa.setDuration(1000); oa.start();
最后运行的动画效果 可能由于浏览器的原因,没有缩放到最小,后面会将源码留下,大家可以去模拟器上看看效果。
AnimatorSet可以实现同样的类似于PropertyValueHolder的功能 但是AnimatorSet功能更强大,它可以设置某一个动画在另外一个动画前面或者后面执行,还可以设置某几个动画同时执行。
<pre name="code" class="java">AnimatorSet set = new AnimatorSet(); //透明度 ObjectAnimator anim1 = ObjectAnimator.ofFloat(iv, "alpha", 0f,1f); anim1.setDuration(4000); //横向平移 ObjectAnimator anim2 = ObjectAnimator.ofFloat(iv, "translationX", 0f,100f); anim2.setDuration(3000); //纵向平移 ObjectAnimator anim3 = ObjectAnimator.ofFloat(iv, "translationY", 0f,100f); anim3.setDuration(3000); //旋转 ObjectAnimator anim4 = ObjectAnimator.ofFloat(iv, "rotation", 0f,360f); anim4.setDuration(500); //制定动画执行的顺序 //anim1 在 anim2 之前执行 set.play(anim1).before(anim2); //anim2 anim3 同时执行 set.play(anim2).with(anim3); //anim4 在 anim3之后执行 set.play(anim4).after(anim3); set.start();
执行效果如下图
AnimatorSet里面有几个控制动画执行先后顺序的方法
- after(Animator anim) 将现有动画插入到传入的动画之后执行
- after(long delay) 将现有动画延迟指定毫秒后执行
- before(Animator anim) 将现有动画插入到传入的动画之前执行
- with(Animator anim) 将现有动画和传入的动画同时执行
3.关键帧KeyFrame
意思就是设置动画的某一帧(关键帧)的效果,先看代码和效果图,在根据代码和效果图仔细讲解
//关键帧 //Keyframe 定义动画在特定的时间点上特定的状态 //第一个0,动画的执行进度,第二个0,初始的状态(0度) // Keyframe kf0 = Keyframe.ofFloat(0f,0f); //动画执行到30%时,应该旋转了360度 //Keyframe kf1 = Keyframe.ofFloat(0.2f,360f); // Keyframe kf1 = Keyframe.ofFloat(0.2f,1f); //动画执行到100%,重新旋转到0度 // Keyframe kf2 = Keyframe.ofFloat(1f,0f); Keyframe kf0=Keyframe.ofFloat(0f,0f); Keyframe kf1=Keyframe.ofFloat(0.1f,100f); Keyframe kf2=Keyframe.ofFloat(1f,300f); //区别:ObjectAnimator.ofFloat(iv, "rotation", 0f,360f,0f); //将关键帧打包 PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX", kf0,kf1,kf2); // PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("alpha", kf0,kf1,kf2); //创建属性动画对象 ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(iv,holder); oa.setDuration(2000); oa.start();
代码的第10定义了关键帧kf0,调用了Keyframe.ofFloat(float fraction , float value) ,第一个参数代表的是动画执行的进度,在此kf0的fraction=0f,就是动画开始执行的时候,属性类型为Float的值为value
代码的第11行fraction=0.1f表示动画执行到10%的时候 属性值(本例中为translationX的值)为100f,同理kf2中动画执行完成之后translationX的值为300f,整个过程中fraction的值为0~1,不能超过1。
代码16行将关键帧打包到PropertyValueHolder中,并且将所有的关键帧的效果作用在属性"translationX"上。最后执行动画。本例的动画执行效果就是控件iv 在沿X平移的时候0-100px内移动的快一些(换算成时间就是在0.1s内平移了100px)后面的200px在0.9s内执行完成
4.事件监听
ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "alpha", 1f,0f); oa.setDuration(1000); oa.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { //动画开始时调用 } @Override public void onAnimationRepeat(Animator animation) { //动画重复时调用 } @Override public void onAnimationEnd(Animator animation) { ////动画结束时调用 //移除按钮 ViewGroup parent = (ViewGroup) btn.getParent(); if (parent != null) { parent.removeView(btn); } } @Override public void onAnimationCancel(Animator animation) { //动画取消时调用 } }); oa.start();
代码中为ObjectAnimator设置了事件监听AnimatorListener,可以重写其中的四个方法onAnimationStart表示动画刚刚开始执行时候的回调,onAnimationRepeat动画重复执行的时候回调,onAnimationEnd动画执行完毕的时候的回调,大部分应用程序都会复写这个方法来执行相关的操作,onAnimationCancel动画取消的时候回调。
如果在程序中我们需要监听动画的完成或者执行状态但是不想回调或者复写AnimatorListener的方法的时候,可以用AnimatorListener的子类AnimatorListenerAdapter,选择你需要重写的方法,而不必重写所有的方法。
oa.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(android.animation.Animator animation) { // TODO Auto-generated method stub super.onAnimationEnd(animation); } });
5.插值器Interpolator
对于不同的插值器有不同的动画效果,可以调用ObjectAnimator的setInterpolator()方法设置自己向想要的插值器。TimeInterpolator时间插值器,是以上所有插值器的父接口,我们可以写自己的插值器的实现,重写其getInterpolation方法,也可以用上面图片所提到的几种插值器,这都是谷歌帮我们写好的。
AccelerateInterpolator:加速差值,意思就是动画执行的速度越来越快。
AniticipateInterpolator:动画执行的效果是控件会从动画开始执行的地方倒退一段距离之后再执行,和OvershootInterpolator相反,OvershootInterpolator是在动画执行完成之后继续执行一段距离,有点类似跳出当前控件所在的父容器之外的感觉
LinearInterpolator:线性插值器,就是类似匀速运动的效果
DecelerateInterpolator:减速插值器,开始动画执行快,后面没执行慢
BounceInterpolator:动画执行的效果类似乒乓球掉在地上的效果,落地之后会反弹,知道最后完全停止。
CycleInterpolator:字面意思 ,循环插值器,可以使动画循环执行多次,类似repeate效果,控制translationX属性可以做出控件的抖动效果
当然还可以有这几种插值器的组合效果。
6 ValueAnimator
正如前面所说,ValueAnimator就是值动画执行类,常配合AnimatorUpdateListener使用。他和ObjectAnimator的区别是,ObjectAnimator是ValueAnimator的子类,在调用ofFloat,ofInt....创建ValueAnimator的时候我们不需要也不能像ObjectAnimator
oa = ObjectAnimator.ofFloat(iv, "translationY", 0,300); 这样为动画指定相应的属性(translationY),俗套的说,就是计算你知道你要改变的属性输translationY但是你也不能再创建的时候指定它,你只能指定属性的取值范围,例如ValueAnimator va = ValueAnimator.ofFloat(0f,200f); 你只知道0f~200f但是从代码字面意思你不知道到底是什么属性0f~200f,这时候就需要配合AnimatorUpdateListener来使用了,在AnimatorUpdateListener的onAnimationUpdate中写我们的动画的具体执行逻辑。
ValueAnimator va = ValueAnimator.ofFloat(0f,200f); va.setDuration(2000); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float v = Float.parseFloat(animation.getAnimatedValue().toString()); //如果要使动画同时适配API11以下的版本 就得使用开源jar包nineoldandroids.jar进行统一适配 //操作方法就变成了ViewHelper.setTranslationX(iv, v); iv.setTranslationX(v); //优点:可以同时在回掉方法中,更新多个控件的属性 //使用ObjectAnimator还是ValueAnimator取决于是否在一个动画中更新多个控件的属性 //ViewHelper.setTranslationY(btn, v); } }); va.start();
例子很简单,但是能够说明ValueAnimator的简单使用方法,我们可以在回调方法onAnimationUpdate方法中处理多个控件(其他非动画执行的控件的状态),当然ValueAnimator还可以配合AnimatorListener使用监听动画执行的开始,结束,重复,取消等操作,也可以使用AnimatorListenerAdapter只监听其中的动画执行的某一个状态。
7 TypeEvaluator
类型估值,用于设置复杂的动画操作属性的值,它是一个接口,有3个实现类,分别为IntEvaluator,FloatEvaluator,ArgbEvaluator,从字面意思可以看出他们的意思。和上面的ofFloat,ofInt有异曲同工之妙。这三个实现类都实现了TypeEvaluator中的抽象方法public T evaluate(float fraction, T startValue,
T endValue); 其中float fraction表示动画执行的进度,T startValue泛型startValue表示动画开始执行的位置或者状态,endValue代表动画执行结束的位置或者状态 ,用泛型T表示 说明开发者可以根据自己的需要,定义自己的TypeEvalotor,例如Integer,Float,Point,PointF...等等。下面说说他们的简单用法。
FloatEvaluator evaluator=new FloatEvaluator(); ValueAnimator valueAnimator = ValueAnimator.ofObject(evaluator,0,200); valueAnimator.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float x=Float.parseFloat(animation.getAnimatedValue().toString()); iv.setX(x); } }); valueAnimator.setDuration(3000); valueAnimator.start();
上面动画的意思是使控件IV的X坐标从0-200改变,类似做直线运动,效果图就免了,很简单。
下面来说说TypeEvaluator的用法,先上代码和效果。
final PointF point = new PointF(); TypeEvaluator<PointF> typeEvaluator = new TypeEvaluator<PointF>() { // @Override public PointF evaluate(float fraction, PointF startValue, PointF endValue) { //fraction 就相当于进度0->1 // point.x=(endValue.x-startValue.x)*fraction; // point.y=(endValue.y-startValue.y)*fraction; point.x=(endValue.x-startValue.x)*fraction; point.y=600+200*(float) Math.sin(point.x/65); return point; } }; ValueAnimator valueAnimator = ValueAnimator.ofObject(typeEvaluator, new PointF(0,600),new PointF((float)(260*Math.PI),600)); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { //getAnimatedValue方法返回的值,由evaluate计算得到的 PointF point = (PointF) animation.getAnimatedValue(); // ViewHelper.setX(iv, point.x); // ViewHelper.setY(iv, point.y); iv.setX(point.x); iv.setY(point.y); } }); valueAnimator.setDuration(3000); valueAnimator.start();
上面的代码使用TypeEvaluator实现了控件IV沿着我们设置的正弦曲线运动,正弦函数为y=600+200*sin(x/65);
代码第1行首先定义一个点PointF 应为里面的坐标为float类型,所以使用PointF
代码第2-13行定义了一个TypeEvalutor并重写了evaluate方法 泛型T代表PointF
代码第9行定义了点的x坐标为point.x=(endValue.x-startValue.x)*fraction;
代码第10行定义了y坐标,我们根据正弦函数的函数方程式写出point.y=600+200*(float) Math.sin(point.x/65); 由于手机屏幕的坐标的水平向右为X正方形,垂直向下为Y轴正方形,数字600 ,200 ,主要是为了增加Y轴的幅度,使动画效果更加明显,65主要是为了扩大X轴方向的距离。
代码第14行创建ValueAnimator对象并调用ofObject方法,将TypeEvaluator,startValue,endValue传递给它。
代码15行,添加动画监听,这正应证了前面说的ValueAnimator通常和ValueAnimator.AnimatorUpdateListener配对使用,在他的回调方法onAnimationUpdate里面控制空间IV的x和y坐标 最后启动动画就得到上面的效果。
文章可能写的不太全面,好多该详细讲解的地方,可能没有讲解到,本文只是讲解了属性动画中常用的一些API,想要写出非常炫酷的动画效果,还是需要开篇所说的逻辑思维能力。希望本文能给大家带来些帮助,谢谢。代码传送门源码