Android Property Animation
Android属性动画可以改变几乎所有Object的属性,属性动画会在一段时间内改变一个Object的一个属性值,为了使用属性动画,可以指定Object想要改变的属性,例如一个Object在屏幕中的位置、想要改变的时间长短、想要其属性值在什么范围内改变等等。
属性动画系统可以定义以下动画的特性:
- Duration:你可以指定动画持续的时间,默认时长为300ms
- Time Interpolation:你可以指定属性值是如何随着动画流逝时间计算的
- Repeat Count and Behavior:你可以指定是否在动画结束时重复动画以及重复的次数,也可以指定是否反向执行动画过程
- Animator Sets:你可以将动画进行逻辑上的组合,让动画一起执行或者书序执行或者延时执行
- Frame Refresh Delay:你也可以指定刷新动画的频率,默认设置是10ms刷新一次,但是应用刷新的帧数最终决定于系统的繁忙程度以及系统能够运行的有多快
属性动画是如何工作的
ValueAnimator对象一直跟踪动画的计时,比如动画已经运行了多长时间、当前的属性值。ValueAnimator封装了一个TimeInterpolator,它定义动画的interpolation,TypeEvaluator定义如何计算动画的属性值。
首先创建一个ValueAnimator,设定想要改变的属性值的起始与结束值,调用start()方法后,动画开始,在整个animation期间,ValueAnimator计算时间流逝在0到1之间的百分比,这个百分比代表了动画完成的程度。
当ValueAnimator计算完后,调用当前设置的TimeInterpolator去计算interpolated的进度。
当interpolated fraction计算完毕后,ValueAnimator调用合适的TypeEvaluator并依据interpolated fraction、starting value、ending value计算属性的值。
属性动画与View动画有什么不同
View动画只能让View做动画,所以如果你想让非View对象做动画,那么就需要自己实现相应的代码了。
View动画的另一个劣势就是View动画值改变绘制出的图形,并不是改变View自身。
API Overview
在android.animation中可以找到大多数的属性动画api,因为在android.view.animation中已经定义了很多的interpolators,你也可以在属性动画中使用这些interpolator,以下列表列出了属性动画的主要元素。
Animator:Animator类提供了创建动画的基本结构,通常你不需要直接使用这个类,因为它只提供了最小的功能单元并且需要被子类继承实现,以下类是直接继承Animator的子类:
- ValueAnimator:包含两个主要部分:计算动画值与给Object设置这些属性值。ValueAnimator不携带第二部分,所以需要监听更新的属性值,并在逻辑中自己去改变Object的属性值,
- ObjectAnimator:是ValueAnimator的子类,这个类允许设置目标对象和目标的属性去做动画,当这个类计算一个新的值后会相应的更新属性值。你可以大多数时间去使用ObjectAnimator来作动画,因为这种方式更加的简单,然而有时你也想要直接使用ValueAnimator,因为ObjectAnimator也会有一些局限性,比如需要target中包含访问属性的方法等。
- AnimatorSet:提供了一种将animation编组的一种机制,所以各种动画可以关联执行,你可以设置动画一起播放或者顺序播放,或者指定延时后播放。
Evaluator:
- IntEvaluator:默认Evaluator计算int型属性值
- FloatEvaluator:默认Evaluator计算float型属性值
- ArgbEvaluator:默认Evaluator计算颜色属性值
- TypeEvaluator:允许自定义Evaluator的接口,如果想要改变非int、float、color值的属性,必须实现该接口,并指定如何计算属性值,如果你不想按照默认的方式去处理int、float、color等种类的值,你也可以自定义TypeEvaluator去实现这些操作。
Interpolator:时间插值器定义了指定动画属性值是如何作为时间函数去进行计算的,例如你可以指定动画在动画期间内线性执行,也就是说,动画在整个时间内均匀的移动,或者你可以指定非线性动画,例如在动画开始的时候加速并在快结束的时候减速,下表列出了android.view.animation中的插值器,如果其中没有你需要的,你可以实现TimeInterpolator接口去创建自己的插值器。
- AccelerateDecelerateInterpolator:开始与结束时慢速,中间时段加速
- AccelarateInterpolator:开始慢速然后加速
- AnticipateInterpolator:变化开始时反向,然后向前
- AnticipateOvershootInterpolator:
- BounceInterpolator:在结束时反弹
- CycleInterpolator:按照指定的数量重复动画
- DecelerateInterpolator:开始时改变的速率快,然后减速
- LinearInterpolator:变化率保持恒定
- OvershootInterpolator:
- TimeInterpolator:Interpolator接口
Animating with ValueAnimator
ValueAnimator类可以让你指定一系列的int、float或者color值并在动画期间改变这些类型的值,通过调用其中的一个factory方法:ofInt()、ofFloat()、ofObject()去获得ValueAnimator,例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f)
animation.setDuration(1000)
animation.start()
在上面的代码中,当ValueAnimator执行start()方法后开始计算动画的值,值的范围是0~1,持续时间为1000ms。
也可以指定自定义类型的值去animate:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue)
animation.setDuration(1000)
animation.start()
在以上代码中,ValueAnimator在调用start方法后,使用MyTypeEvaluator提供的逻辑开始计算animation的值,值的范围是startPropertyValue到endPropertyValue之间,持续时间为1000ms。
然而,在之前的代码片段中,并没有对Object产生真正的效果,因为ValueAnimator并不直接操作Object或者是Properties,你可以通过在ValueAnimator中定义listeners去合适地操作重要的事件,当实现listener后,可以通过调用getAnimatedValue方法获得计算的值。
Animating with ObjectAnimator
ObjectAnimator是ValueAnimator的子类,它融合了timing engine和Object的属性值的计算,这使得动画会更加的容易实现,也不需要再实现ValueAnimator.AnimatorUpdateListener,因为属性会自动的更新。
使用ObjectAnimator和使用ValueAnimator类似,也同时需要指定object和object的属性名:
ObjectAnimator anim = ObjectAnimator.ofFloat(prop, "alpha", 0f, 1f)
anim.setDuration(1000)
anim.start()
为了正确的更新动画属性,需要满足以下条件:
目标的属性必须有setter方法,因为ObjectAnimator自动的更新属性值,必须要利用setter方法访问属性值,要是不存在setter方法,有如下三个选择:
- 有访问class权限的话就添加setter方法
- 使用wrapper类包裹class,wrapper接收到value的改变后转发给object对象
- 使用ValueAnimator代替ObjectAnimator
如果在ObjectAnimator的factory方法中只指定了一个value参数,那么这个参数就认为是动画的结束参数值,因此,动画的对象必须要有getter方法去获取起始的参数值,getter方法必须是getPropertyName格式,例如属性名为prop,方法名必须是getProp。
- 指定的ObjectAnimator的getter和setter方法必须操作同一种类型的属性
- 根据属性或者是Object的类型,你可能需要去调用invalidate方法去强制屏幕去重新绘制,在onAnimationUpdate中进行操作。
Choreographing Multiple Animations with AnimatorSet
在很多情况下,你需要在其他动画开始或者结束后才开始动画,Android系统允许把多种动画捆绑在一起,这样就可以指定同时、顺序或者延迟执行动画了。
以下从BounceBall提取的代码演示了AnimatorSet:
- Play bounceAnim
- Play aquashAnim1,aquashAnim2,stretchAnim1 and stretchAnim2
- Play bounceBackAnim
- Play fadeAnim
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f)
fadeAnim.setDuration(200)
AnimatorSet bouncer = new AnimatorSet()
bouncer.play(bounceAnim).before(squashAnim1)
bouncer.play(squashAnim1).with(squashAnim2)
bouncer.play(squashAnim1).with(stretchAnim1)
bouncer.play(squashAnim2).with(stretchAnim2)
bouncer.play(bounceBackAnim).after(stretchAnim2)
AnimationSet animationSet = new AnimationSet()
animationSet.play(bouncer).before(faceAnim)
animationSet.start()
Animation Listeners
你可以利用以下的监听器监听动画过程中的重要事件:
- Animator.AnimatorListener
- onAnimationStart()
- onAnimationEnd()
- onAnimationRepeat()
- onAnimationCancel
- onAnimationEnd
- ValueAnimator.AnimatorUpdateListener
- onAnimationUpdate():在动画的每一帧都调用,依靠Object或者是属性,你能需要调用incalidate方法强制屏幕区域用新的值去重新绘制自己
Animating Layout Changes to ViewGroups
属性动画系统能够实现与View Object一样简单的方式去给ViewGroup实现相同的动画。
你可以使用LayoutTransition类在ViewGroup中做动画,当在ViewGroup中添加或者移除View或是使用setVisibility方法就可以使View实现消失与显现动画。你可以在LayoutTransition对象中利用以下LayoutTransition常量,并调用setAnimator方法并传递Animator对象:
- APPEARING:在容器中显示的标志位
- CHANGE_APPEARING:
- DISAPPEARING:
Using a TypeEvaluator
如果想要做未知类型的动画,你可以实现TypeEvaluator接口创建自己的evaluator。TypeEvaluator中只有一个方法需要实现-evaluate()。
public class FloatEvaluator implements TypeEvaluator{
public Object evaluate(float fraction, Object startValue, object endValue){
float startFloat = ((Number)startValue).floatValue();
return startFloat + fraction * (((Number)endValue).floatValue() - startFloat);
}
}
Using Interpolators
Interpolator定义了指定的动画值是如何作为时间函数进行计算的,例如,你可以指定动画在整个时间内线性的执行,或者你也可以指定动画非线性执行,例如使用加速度或者减速度方式。
Android系统在android.view.animation包中提供了一系列的通用interpolation,如果这些都不能够满足需要,那么你可以实现TimeInterpolator接口创建自己的interpolator。
Specifying Keyframes
Keyframe对象包含一个键值对,这个键值对允许你定义动画中的一个指定时间的指定状态,每个Keyframe可以有自己的interpolator去控制前一个Keyframe与后一个Keyframe之间的行为。
你必须使用ofInt、ofFloat、ofObject等方法去获得合适的Keyframe,然后调用ofKeyframe()方法去获得一个PropertyValuesHolder()对象,一旦拥有了这个对象,你可以通过传递PropertyValueHolder与object去开启一个动画,以下代码演示了如何使用:
Keyframe k0 = Keyframe.ofFloat(0f, 0f)
Keyframe k1 = Keyframe.ofFloat(.5f, 360f)
Keyframe k2 = Keyframe.ofFloat(1f, 0f)
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("rotation", k0, k1, k2)
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, holder)
animator.setDuration(5000)
Animating Views
View Animation系统通过改变绘制的方式进行动画的,这是通过View的容器进行操作,因为View自身没有可以操作的属性,这导致view做动画后它的属性还是在原来的位置,即使它已经被绘制在了不同的位置,在Android3.0中,这个缺陷已经通过添加getter与setter方法被消除了。
属性动画系统可以通过改变View对象的实际属性而产生动画效果,除此之外,View也能够在属性值改变时自动的调用invalidate方法刷新屏幕显示,View类中的新的属性值有:
-translationX & translationY:这两个属性控制着View在容器中的左上角位置
-rotation,rotationX,rotationY:这三个属性控制着View2D与3D的旋转
-scaleX & scaleY:这两个属性控制2D中绕转轴点的缩放
-pivotX & pivotY:这两个属性控制pivot点的位置,默认是View的中心
-x & y:描述view在容器中的最终位置,是left & top与translationX & translationY的和
-alpha:代表View的alpha透明度,默认是1(不透明)
对View使用属性动画,需要创建property animator并指定想要改变的属性值:
ObjectAnimator.ofFloat(view, "rotation", 0f, 360f);
Animating with ViewPropertyAnimator
ViewPropertyAnimator提供了一种简单的方式去实现属性动画,它使用起来与ObjectAnimator类似,因为它改变了View的实际属性值,但是当同时对多个属性做动画会更加有效率,除此之外,使用ViewPropertyAnimator代码会更加易于阅读,一下代码片段展示了对一个View的x与y使用多个ObjectAnimator与一个ObjectAnimator与一个ViewPropertyAnimator的区别。
ObjectAnimator animX = ObjectAnimator.ofFloat(view,"x",50f)
ObjectAnimator animY = ObjectAnimator.ofFloat(view,"y",100f)
AnimatorSet set = new AnimatorSet()
set.playTogether(animX, animY)
set.start()
PropertyValueHolder pX = PropertyValueHolder.ofFloat("x", 50f)
PropertyValueHolder pY = PropertyValueHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(view, pX, pY).start()
view.animate().x(50f).y(100f)
Declaring Animations in XML
属性动画系统允许在xml中声明而不需要编程实现,通过在xml中定义自己的动画,你可以很容易的在多个activity中重用,并且会更加容易编辑。
为了加以区分,需要在res/animator路径中定义xml。以下三个类中已经有特定的命名支持:
-ValueAnimator:
-ObjectAnimator:
-AnimatorSet:
以下代码中演示了xml中使用set与ObjectAnimator
<set android:ordering="sequentially">
<set>
<objectAnimator android:propertyName="x" android:duration="500" android:valueTo="400" android:valueType="intType"/>
<objectAnimator android:propertyName="y" android:duration="500" android:valueTo="300" android:valueType="intType"/>
</set>
<objectAnimator android:propertyName="alpha" android:duration="500" android:valueTo="1f"/>
</set>
为了运行这个动画,你必须在代码中inflate这个xml资源成为AnimatorSet对象,然后在开启动画set前将所有的动画设置到target对象上,调用setTarget方法给target设置动画集合是最方便的,一下为示例代码:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator)
set.setTarget(myObject)
set.start()