属性动画详解 Interpolator TypeEvaluator

时间:2021-05-06 08:50:57

属性动画概述

3.0以前,android仅支持两种动画模式,tweened animation 和 frame-by-frame animation,在android3.0中又引入了一个新的动画系统:property animation,这三种动画模式在SDK中被称为【property,view,drawable】animation。 


View Animation非常简单,不过只支持简单的缩放、平移、旋转、透明度等基本的动画,且有一定的局限性……
属性动画框架操作的是真实的属性值,直接改变了对象的属性,因此可以很灵活的实现各种效果,而不局限于以前的4种动画效。
另外Property Animation不止可以应用于View,还可以应用于任何对象。Property Animation只是表示一个值在一段时间内的改变,当值改变时要做什么事情完全是你自己决定的。
总的来说,属性动画就是:通过动画的执行类(Animator)设置要进行动画操作的对象(Object)、对象的属性(Property)、持续时间(Duration)、类型估值(TypeEvaluator)、开始和结束的属性值(Values)、插值器(TimeInterpolation)等,然后动画的执行类就会根据设置的参数动态的改变对象的属性。

可通过NineOldAndroids项目在3.0之前的系统中使用Property Animation

几个可能需要设置的属性:
  • Duration:动画的持续时间,默认300ms。
  • TimeInterpolation:时间差值,和Interpolator一样,定义动画的变化率。
  • Repeat count and behavior:重复次数、以及重复模式;可以定义重复多少次;重复时从头开始,还是反向。
  • Animator sets: 动画集合,你可以定义一组动画,一起执行或者顺序执行。
  • Frame refresh delay:帧刷新延迟,对于你的动画,多久刷新一次帧;默认为10ms,但最终依赖系统的当前状态;基本不用管。

ObjectAnimator

ObjectAnimator是属性动画框架中最重要的类,创建一个ObjectAnimator只需通过他的静态工厂类直接返回一个ObjectAnimator对象。传的参数包括一个对象的引用和要操作的此对象的属性的名字,但这个属性必须有【get和/或set】函数,动画在执行过程中会通过java【反射】机制来调用set函数修改对象属性值。

可操纵的属性参数有:alpha;x/y;scaleX/scaleY;rotation/rotationX/rotationY;transitionX/transitionY;pivotX/pivotY等等。
注意:X表示的是View最终的位置,translationX表示的是最终位置与布局时初始位置的差值,即在原来基础上移动了多少。
两者关系为:getX()=getLeft()+getTranslationX()

ObjectAnimator提供了ofInt、ofFloat、ofObject等几个方法,这几个方法的可变参数的规则为:
  • 当属性值只设置一个,会认为对象当前属性值为开始值,设置的值为最终值
  • 如果设置两个,则第一个为开始值、另一个为结束值
  • More than two values imply a starting value, values to animate through along the way, and an ending value

多动画效果的合成

1、使用AnimatorSet中的顺序控制方法:playTogether、playSequentially、.play()、with()、defore()、after()等

2、使用ObjectAnimator
       ObjectAnimator anim = ObjectAnimator.ofFloat(iv_src, "包青天", 1.5f, 0.5f, 3f, 2f).setDuration(3000);
        anim.start();
        anim.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float cVal = (Float) animation.getAnimatedValue();
                iv_src.setAlpha(cVal*0.1);
                iv_src.setScaleX(cVal);
            }
        }); 
在这里,我们监听了iv_src中一个并不存在的属性,在监听动画更新过程中,去修改我们需要修改的属性。
监听一个不存在的属性的原因就是,我们只需要获取动画的【变化值】,通过这个值,我们自己来实现要修改的效果(当然这个值一般都要经过我们处理后才会有意义)。
实际上,实现这种效果更简单明了的方法,就是使用ValueAnimator。

3、使用PropertyValuesHolder
       ObjectAnimator.ofPropertyValuesHolder(iv_src, pvh1, pvh2, pvh3).setDuration(2000).start();  

动画绘制过程与执行状态的监听

1、动画绘制过程的监听:
     animator.addUpdateListener(new AnimatorUpdateListener() { public void onAnimationUpdate(ValueAnimator arg0) {} });
该方法用来监听动画绘制过程中的每一帧的改变,通过这个方法,我们可以在动画重绘的过程中,实现自己的逻辑。
  • 对于ObjectAnimator,当属性值计算完成时会自动调用对象的set**方法更新对象相应的属性,所以我们一般不需要监听此事件。
  • 对于ValueAnimator,我们一般都需要自己去监听此事件以去执行我们指定的动作,不然Animation将没任何意义或效果。
根据应用动画的对象的属性的不同,如果你操作对象的该属性方法里面没有调用view的重绘操作,则你需要在onAnimationUpdate函数中手动调用invalidate()或postInvalidate()函数来刷新视图。

2、动画执行状态的监听:
我们可以为动画注册动画执行状态的监听,以定义自己的开始、结束、被取消、重复事件。
为方便使用,我们可以继承AnimatorListenerAdapter而不是实现AnimatorListener接口来简化操作,这个类对AnimatorListener中的函数都定义了一个空函数体,这样我们就只定义想监听的事件而不用实现每个函数却只定义一空函数体。
      anim.addListener(new AnimatorListener() 或 new AnimatorListenerAdapter() { } );

ViewAnimator 数值产生器

属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的,ValueAnimator对过渡动画值的计算依靠一个时间因子fraction,而这个时间因子fraction是系统由setDuration()方法设置的动画执行时间通过计算得来的,所以ValueAnimator还负责管理动画的持续时间、播放次数、播放模式、以及对动画设置监听器等,是一个非常重要的类。

ValueAnimator并没有在属性上做操作,好处是不需要操作的对象的属性一定要有get和/或set方法,你可以自己根据当前动画的计算值,来操作任何属性,虽然多了几行代码,但是提高了灵活性。

应用ValueAnimation有两个步聚:
  • 计算属性值。ValuAnimiator自身完成了计算的工作,但是我们需要通过addUpdateListener注册AnimatorUpdateListener监听,在这个监听接口的回调方法onAnimationUpdate()中获取ValueAnimator对象,通过这个ValueAnimator对象的getAnimatedValue()函数可以得到当前计算出的属性值(或其他数据)。
  • 根据属性值执行相应的动作,如改变对象的某一属性。这一步是需要我们自己去实现的,通过上一步我们已经拿到了计算出的当前的属性值,我们只需将其按我们希望的方式作用到对象指定的属性上即可。

本质上来说,ValueAnimator只是一个数值产生器,和ObjectAnimator不同的是,他本身不作用于任何一个对象,但是我们可以根据产生的值进行动画处理。
据此我们可以发现,动画生成的原理就是:通过差值器(TimeInterpolator)实时计算出按照某种规律(也即一个时间函数)变化的模型在当前时间的数值,并将这个数值实时作用到对象上来实现对对象某些属性的改变,以此产生动画的效果。

TimeInterpolator 插值器

TimeInterpolator是实现动画的基础,了解这些东西,才能作出不一样的动画效果。
TimeInterpolator可以翻译为时间插值器或时间估值器,用于定义动画速度的变化,可以实现让动画速度具有匀速、正向加速、负向加速、正向减速、负向减速等效果。
所谓的插值器,就是通过一些数学公式,计算出一些数值,提供给动画来使用。就好比我们定义了起始值是0,结束值是100,时间为1秒,那么在这1秒内我们指定的值是如何0跑到100的呢?这就是插值器TimeInterpolator要起到的作用。

TimeInterpolator是一个接口,其本身并未提供插值逻辑的具体实现,其只声明了一个方法 getInterpolation (float input)。所以,我们如果想自定义一个TimeInterpolator,只需重写getInterpolation方法即可。
getInterpolation(float input)方法中的 input 参数是系统根据设置的动画持续时间计算出来的,取值范围是[0,1],并且是从0匀速增加到1的。你完全可以把 input 参数当做是"单位时间"。
需要注意的是,TimeInterpolator定义的是动画的速度的变化(the rate of change),而非速度(rate)。

我是这么理解的:
getInterpolation方法中定义了一个【s-t】函数,s代表要达到的值,t代表时间。
所以函数曲线本身只是代表在各个时间点 t' 时的函数值 s' ,这和速度没有半毛钱关系。
但是根据大学学的知识我们知道,对 s-t 函数【求导】的结果其实就是【速度v - 时间t】的函数。
换句话说,在各个时间点 t' 时的速度 v' 其实就是【s-t】函数在时间点 t' 时的切线。
所以【s-t】函数当然也就决定了速度的变化(the rate of change)。

PS:
  • 在Property Animation中插值器是TimeInterplator,在View Animation中是Interplator,这两个其实是完全一样的!在3.0之前只有Interplator,3.0之后实现代码转移至了TimeInterplator,Interplator继承自TimeInterplator,内部没有任何其他代码。
  • 在代码中设置Interpolator:anim.setInterpolator(new **Interpolator());
  • 在布局中设置Interpolator:android:interpolator="@android:anim/**_interpolator"

TypeEvaluator 估值器

Android提供了以下几个简单的Evalutor实现类:
  • IntEvaluator:属性的值类型为int
  • FloatEvaluator:属性的值类型为float
  • ArgbEvaluator:属性的值类型为十六进制颜色值
TypeEvaluator:一个接口,可以通过实现该接口自定义Evaluator。

TypeEvaluator的实现过程:
首先根据动画【已进行的时间】跟动画【总时间】的比计算出一个时间因子(0~1,即evalute()中的fraction参数,这个值是自动计算得到的)
然后根据【TimeInterpolator】得到出另一个算法因子
TypeEvaluator通过这【两个】因子计算出属性值并将其【传给】我们定义的泛型对象
最后在我们监听动画的绘制过程中,将泛型对象中保存的值【取出】并用于【更改】目标对象的属性

自定义TypeEvaluator时传入的泛型可以根据自己的需求,自己设计个Bean。
只要evaluate中的函数能够满足:当fraction=0时返回值为startValue,并且当fraction=1时返回值为endValue,就是一个比较合理的函数。

KeyFrame 关键帧

KeyFrame是一个【时间/值】对,通过它可以定义一个在特定时间的特定状态,而且可以在两个keyFrame之间定义不同的Interpolator,就好像多个动画的拼接,第一个动画的结束点是第二个动画的开始点。
KeyFrame是抽象类,要通过ofInt(),ofFloat(),ofObject()获得适当的KeyFrame,然后通过PropertyValuesHolder.ofKeyframe获得PropertyValuesHolder对象,然后通过ObjectAnimator.ofPropertyValuesHolder便可获取ObjectAnimator对象。

View的animate动画 属性动画

在SDK11的时候,给View添加了animate方法,可以使用一行代码非常方便的实现动画效果。
此后在SDK12,SDK16又分别添加了withStartAction和withEndAction用于在动画前、动画后执行一些操作。当然也可以使用.setListener(listener)完成此操作。
注意,这种动画不是View动画,而是属性动画的一种,因为改变了控件的实际属性。

XML中设置属性动画

set标签的orderring属性设置为together表示同时播放,sequentially表示一个接一个播放
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scale);
anim.setTarget(mChangeAppear);
anim.start();  

LayoutTransition 属性动画

使用LayoutTransition可为布局容器设置动画,当容器中的视图层次发生变化时产生相应的过渡的动画效果。
ViewGroup的xml属性中有一个animateLayoutChanges属性,设置该属性为true,可以添加ViewGroup增加view的过渡效果:
过渡的类型一共有四种:
  • LayoutTransition.APPEARING 当一个View在ViewGroup中出现时,对此View设置的动画
  • LayoutTransition.CHANGE_APPEARING 当一个View在ViewGroup中出现时,对此View对其他View位置造成影响,对其他View设置的动画
  • LayoutTransition.DISAPPEARING  当一个View在ViewGroup中消失时,对此View设置的动画
  • LayoutTransition.CHANGE_DISAPPEARING 当一个View在ViewGroup中消失时,对此View对其他View位置造成影响,对其他View设置的动画
也可以在代码中通过container.setLayoutTransition(new LayoutTransition());自定义以上四种动画类型

LayoutTransition一般可以用在listview等adapterview中,显得比较炫一些。

LayoutAnimationController 补间动画

LayoutAnimationController用于为一个layout里面的控件在显示时设置同样的动画效果。
可以在XML文件中设置【android:layoutAnimation="@anim/layout_anim"】,其中layout_anim是一个特殊的补间动画
例如:
  1. <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
  2. <!-- 其中delay的单位为秒;animation为设置动画的文件;animationOrder为子控件进入的方式 -->
  3.     android:animation="@anim/anim_set"
  4.     android:animationOrder="random"
  5.     android:delay="0.5" >
  6. </layoutAnimation>
其中layout_anim中又引用了一个普通的补间动画。

我们也可以在Java代码中设置【vg.setLayoutAnimation(LayoutAnimationController)】
其中LayoutAnimationController中封装了一个补间动画,例如:lac= new LayoutAnimationController(animation);
同样可以通过setOrder、setDelay设置延时和进入方式。

注意:和上面的LayoutTransition不一样的是,此动画类型是补间动画,而非属性动画。
注意:和上面的LayoutTransition不一样的是,此类型动画仅仅在子View显示时有用,在添加移除View时没任何卵用。