属性动画(Property Animation)深入分析(原理、源码、实践)

最近想试试Material Design的ripple效果,于是下载了github上的material-ripple,效果还不错。用到的当然是动画啦,之前也没用接触过,索性来学习一下动画。

整体来说,我参考了google的官方文档、郭大神关于Property Animation的分析、material-ripple的源码。

所以以下就分这三块来讲,正好能“有个整体认识”、“了解其内部原理”、“如何实践”


1. google的官方文档,英文好的直接看:http://developer.android.com/guide/topics/graphics/prop-animation.html#listeners ,肯定比我说的好多了。

属性动画会在一段特定的时间内改编属性的值。属性动画允许你定义一下动画的特性:

Duration: 动画的时间,默认值是300ms;

Time interpolation: 你可以指定在某个时间点上的属性值;

Repeat count and behavior: 指定动画是否重复、重复次数;

Animator sets: 动画集,使得动画叠加,或者一个个连续,或者延迟 执行;

Frame refresh delay: 动画帧刷新的次数,默认是10ms。


有几个类是很重要:

ValueAnimator: 为动画计时;计算时刻的属性值。

同时ValueAnimator包含TimeInterpolator、TypeEvaluator,顾名思义,TimeInterpolator可以理解为“插入器”,是用来插入动画的,可以选择线性还是非线性(比如AccelerateDecelerateInterpolator 

在动画进行时,ValueAnimator负责计算elapsed fraction ,也就是过去的时间因数,如果时间过去了1/4,那么elapsed fraction 就是0.25;

elapsed fraction 计算完成后,会根据elapsed fraction ,由TimeInterpolator计算interpolated fraction,也就是插入因数。如果是线性的TimeInterpolator,那么应该是和elapsed fraction一样的;如果不是线性的,比如是AccelerateDecelerateInterpolator ,那么就和elapsed fraction不一样了(0.15)。

interpolated fraction也计算完成后,ValueAnimator会让TypeEvaluator计算动画的属性值。

总而言之,某frame刷新时刻的属性值是和 “动画起点和终点值”、“消逝的时间”、“插入器”类型有关。


API总览:

1.使用动画时,应该采用Animator的子类:

ValueAnimator:正如之前讲的,计算属性值。但是有一点需要注意,属性动画应该包含两部分,1.计算属性值,2.将计算出来的属性值赋给属性,但是ValueAnimator并不执行赋值,所以你应该自己对属性赋值。

ObjectAnimator:ValueAnimator的子类,相对于ValueAnimator其优点是计算完属性值后会对属性赋值。普遍用ObjectAnimator~

AnimatorSet动画集,使得动画叠加,或者一个个连续,或者延迟 执行。

2.Evaluators: 计算属性值。包含以下几类:

IntEvaluator int属性的默认evaluator;

FloatEvaluatorfloat属性的默认evaluator;

ArgbEvaluator颜色属性的默认evaluator(16进制颜色);

TypeEvaluator是接口,用于自定义类型的Evaluator,如果你的属性不是int、float、颜色,那么就要用自定义类型的Evaluator

3.Interpolators:A time interpolator defines how specific values in an animation are calculated as a function of time.(定义的多好~),包含以下几类就不多说了:AccelerateDecelerateInterpolator(两头慢,中间快)、LinearInterpolator、TimeInterpolator(接口,用于自定义的Interpolator


使用ValueAnimator

前面我们说到计算属性值得时候需要是根据三点:“动画起点和终点值”、“消逝的时间”、“插入器”类型。

“消逝的时间”当然是根据帧的刷新速率,那么想必在初始化ValueAnimator的时候要有其他两点了。

1.

ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
2.
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
前面也提到,ValueAnimator有一个缺点,就是没法赋值,需要自己添加赋值逻辑。那么该怎么操作呢?

可以对ValueAnimator设一个监听器,在帧更新的时候通过getAnimatedValue()获取属性值。具体会在下面讲到。


使用ObjectAnimator

这个使用很普遍,因为你不需要设立监听器ValueAnimator.AnimatorUpdateListener, 因为属性的更新是自动的。

初始化ObjectAnimator和初始化ValueAnimator类似,但是需要制定一个对象,和属性的名称(String),例如:

ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
想要ObjectAnimator正确的更新属性,需要做一下几点:

1. 设置set方法,(要对属性赋值嘛~)如果属性名是aaa,那么set方法就是setAaa()。如果不能直接设置set方法,可以尝试设置一个包装类(material-ripple就是这么做的),实在不行就只能使用ValueAnimator了;

2.如果对于属性值数组values只有一个值(每个值都和开始的时候一样),那么每次更新都应该有一个get方法;

3.getter(if needed)和setter操作的属性值类型应该和开始值、结束值保持一致,这个很好理解

4.对于一个view的变化展现,我们应该在每一次动画更新的时候(回调onAnimationUpdate)去调用invalidate()(如果是颜色变化,调用setters方法,比如 setAlpha()setTranslationX()就可以实现invalidate了,不需要再去调用invalidate())


使用AnimatorSet

可以simultaneously, sequentially, or after a specified delay.

小例子进行了如下操作:

  1. Plays bounceAnim.
  2. Plays squashAnim1squashAnim2stretchAnim1, and stretchAnim2 at the same time.
  3. Plays bounceBackAnim.
  4. Plays fadeAnim.
    AnimatorSet bouncer = new AnimatorSet();
    bouncer.play(bounceAnim).before(squashAnim1);
    bouncer.play(squashAnim1).with(squashAnim2);
    bouncer.play(squashAnim1).with(stretchAnim1);
    bouncer.play(squashAnim1).with(stretchAnim2);
    bouncer.play(bounceBackAnim).after(stretchAnim2);
    ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
    fadeAnim.setDuration(250);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(bouncer).before(fadeAnim);
    animatorSet.start();


    对于ViewGroups的动画布局

    使用 LayoutTransition,当ViewGroups中View显现或者消失的时候(例如调用SetVisibility的时候),可以有动画效果。

    使用方法,就是对LayoutTransition中的常量setAnimaor(),这些常量包括:

    APPEARING(新显现的item)、CHANGE_APPEARING(当有新item出现,原来的items的动画);相反的有DISAPPEARING、CHANGE_DISAPPEARING。


    使用TypeEvaluator

    对于int、float、color这种属性类型,可以使用IntEvaluator、FloatEvaluator、ArgbEvaluator,如果是其他类型,则需要继承TypeEvaluator,实现evaluate()。看一下FloatEvaluator的实现就明白了:

    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);
        }
    }
    


    使用Interpolators

    根据过去的时间和Interpolator类型,计算出Interpolated fraction 

    ms elapsed Elapsed fraction/Interpolated fraction (Linear) Interpolated fraction (Accelerate/Decelerate)
    0 0 0
    200 .2 .1
    400 .4 .345
    600 .6 .8
    800 .8 .9
    1000 1 1

    使用keyframes

    keyframe对象有time/value对组成,可以在特定的时间展现特定的状态。

    使用 ofInt()ofFloat(), 或者 ofObject() 来获取特定类型的keyframe,然后使用 ofKeyframe()获取PropertyValuesHolder 对象,当得到这个对象后,调用ofPropertyValuesHolders将这个对象添加到特定的target,例子如下:

    Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
    Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
    Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
    PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
    ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
    rotationAnim.setDuration(5000ms);

    让View动起来

    属性动画系统相比较于视图动画系统,有一些有点。比如在视图动画系统中,并不是对view进行操作(因为view没有相关的属性),而是通过它的容器,这就使得view本身没有改变(比如看似移动了它的位置,但它实际上还是在原来的位置)。

    而属性动画系统是真正对于view的操作,改变view的属性。另外view也自动调用invalidate()。这些关于view中关于动画的属性是:

    • translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates which are set by its layout container.
    • rotationrotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
    • scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
    • pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
    • x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
    • alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).

    使用ViewPropertyAnimator
    ViewpropertyAnimator对于一组并行的View动画,提供了更加简单地方法:

    Multiple ObjectAnimator objects

    ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
    ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
    AnimatorSet animSetXY = new AnimatorSet();
    animSetXY.playTogether(animX, animY);
    animSetXY.start();

    One ObjectAnimator

    PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
    PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
    ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

    ViewPropertyAnimator

    myView.animate().x(50f).y(100f);

    使用xml定义动画
    相对应的tag如下
    xml布局:
    <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>

    AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
        R.anim.property_animator);
    set.setTarget(myObject);
    set.start();


    好了,关于Property Animation的官方部分就到这里了。
    如果接下来在看这两篇博文,应该会有更好的理解:

    Android 属性动画(Property Animation) 完全解析 (上)

    Android 属性动画(Property Animation) 完全解析 (下)


    对于源码分析,可以看这里:

    Property Animation

    The property animation system is a robust framework that allows you to animate almost anything. You can define an animation to change any object property over time, regardless of whether it draws to the screen or not. A property animation changes a property's (a field in an object) value over a specified length of time. To animate something, you specify the object property that you want to animate, such as an object's position on the screen, how long you want to animate it for, and what values you want to animate between. 

    The property animation system lets you define the following characteristics of an animation:

    • Duration: You can specify the duration of an animation. The default length is 300 ms.
    • Time interpolation: You can specify how the values for the property are calculated as a function of the animation's current elapsed time.
    • Repeat count and behavior: You can specify whether or not to have an animation repeat when it reaches the end of a duration and how many times to repeat the animation. You can also specify whether you want the animation to play back in reverse. Setting it to reverse plays the animation forwards then backwards repeatedly, until the number of repeats is reached.
    • Animator sets: You can group animations into logical sets that play together or sequentially or after specified delays.
    • Frame refresh delay: You can specify how often to refresh frames of your animation. The default is set to refresh every 10 ms, but the speed in which your application can refresh frames is ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.

    How Property Animation Works


    First, let's go over how an animation works with a simple example. Figure 1 depicts a hypothetical object that is animated with its x property, which represents its horizontal location on a screen. The duration of the animation is set to 40 ms and the distance to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at horizontal position 40. This is an example of an animation with linear interpolation, meaning the object moves at a constant speed.

    Figure 1. Example of a linear animation

    You can also specify animations to have a non-linear interpolation. Figure 2 illustrates a hypothetical object that accelerates at the beginning of the animation, and decelerates at the end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the beginning, this animation accelerates up to the halfway point then decelerates from the halfway point until the end of the animation. As Figure 2 shows, the distance traveled at the beginning and end of the animation is less than in the middle.

    Figure 2. Example of a non-linear animation

    Let's take a detailed look at how the important components of the property animation system would calculate animations like the ones illustrated above. Figure 3 depicts how the main classes work with one another.

    Figure 3. How animations are calculated

    The ValueAnimator object keeps track of your animation's timing, such as how long the animation has been running, and the current value of the property that it is animating.

    The ValueAnimator encapsulates a TimeInterpolator, which defines animation interpolation, and a TypeEvaluator, which defines how to calculate values for the property being animated. For example, in Figure 2, the TimeInterpolator used would be AccelerateDecelerateInterpolator and the TypeEvaluatorwould be IntEvaluator.

    To start an animation, create a ValueAnimator and give it the starting and ending values for the property that you want to animate, along with the duration of the animation. When you call start() the animation begins. During the whole animation, the ValueAnimator calculates an elapsed fraction between 0 and 1, based on the duration of the animation and how much time has elapsed. The elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0% and 1 meaning 100%. For example, in Figure 1, the elapsed fraction at t = 10 ms would be .25 because the total duration is t = 40 ms.

    When the ValueAnimator is done calculating an elapsed fraction, it calls the TimeInterpolator that is currently set, to calculate an interpolated fraction. An interpolated fraction maps the elapsed fraction to a new fraction that takes into account the time interpolation that is set. For example, in Figure 2, because the animation slowly accelerates, the interpolated fraction, about .15, is less than the elapsed fraction, .25, at t = 10 ms. In Figure 1, the interpolated fraction is always the same as the elapsed fraction.

    When the interpolated fraction is calculated, ValueAnimator calls the appropriate TypeEvaluator, to calculate the value of the property that you are animating, based on the interpolated fraction, the starting value, and the ending value of the animation. For example, in Figure 2, the interpolated fraction was .15 at t = 10 ms, so the value for the property at that time would be .15 X (40 - 0), or 6.

    The com.example.android.apis.animation package in the API Demos sample project provides many examples on how to use the property animation system.

    How Property Animation Differs from View Animation


    The view animation system provides the capability to only animate View objects, so if you wanted to animate non-View objects, you have to implement your own code to do so. The view animation system is also constrained in the fact that it only exposes a few aspects of a View object to animate, such as the scaling and rotation of a View but not the background color, for instance.

    Another disadvantage of the view animation system is that it only modified where the View was drawn, and not the actual View itself. For instance, if you animated a button to move across the screen, the button draws correctly, but the actual location where you can click the button does not change, so you have to implement your own logic to handle this.

    With the property animation system, these constraints are completely removed, and you can animate any property of any object (Views and non-Views) and the object itself is actually modified. The property animation system is also more robust in the way it carries out animation. At a high level, you assign animators to the properties that you want to animate, such as color, position, or size and can define aspects of the animation such as interpolation and synchronization of multiple animators.

    The view animation system, however, takes less time to setup and requires less code to write. If view animation accomplishes everything that you need to do, or if your existing code already works the way you want, there is no need to use the property animation system. It also might make sense to use both animation systems for different situations if the use case arises.

    API Overview


    You can find most of the property animation system's APIs in android.animation. Because the view animation system already defines many interpolators in android.view.animation, you can use those interpolators in the property animation system as well. The following tables describe the main components of the property animation system.

    The Animator class provides the basic structure for creating animations. You normally do not use this class directly as it only provides minimal functionality that must be extended to fully support animating values. The following subclasses extend Animator:

    Table 1. Animators

    Class Description
    ValueAnimator The main timing engine for property animation that also computes the values for the property to be animated. It has all of the core functionality that calculates animation values and contains the timing details of each animation, information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate. There are two pieces to animating properties: calculating the animated values and setting those values on the object and property that is being animated. ValueAnimator does not carry out the second piece, so you must listen for updates to values calculated by the ValueAnimator and modify the objects that you want to animate with your own logic. See the section about Animating with ValueAnimator for more information.
    ObjectAnimator A subclass of ValueAnimator that allows you to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation. You want to use ObjectAnimator most of the time, because it makes the process of animating values on target objects much easier. However, you sometimes want to use ValueAnimator directly because ObjectAnimator has a few more restrictions, such as requiring specific acessor methods to be present on the target object.
    AnimatorSet Provides a mechanism to group animations together so that they run in relation to one another. You can set animations to play together, sequentially, or after a specified delay. See the section about Choreographing multiple animations with Animator Setsfor more information.

    Evaluators tell the property animation system how to calculate values for a given property. They take the timing data that is provided by an Animator class, the animation's start and end value, and calculate the animated values of the property based on this data. The property animation system provides the following evaluators:

    Table 2. Evaluators

    Class/Interface Description
    IntEvaluator The default evaluator to calculate values for int properties.
    FloatEvaluator The default evaluator to calculate values for float properties.
    ArgbEvaluator The default evaluator to calculate values for color properties that are represented as hexidecimal values.
    TypeEvaluator An interface that allows you to create your own evaluator. If you are animating an object property that is not an intfloat, or color, you must implement the TypeEvaluator interface to specify how to compute the object property's animated values. You can also specify a custom TypeEvaluator for intfloat, and color values as well, if you want to process those types differently than the default behavior. See the section about Using a TypeEvaluator for more information on how to write a custom evaluator.

    A time interpolator defines how specific values in an animation are calculated as a function of time. For example, you can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or you can specify animations to use non-linear time, for example, accelerating at the beginning and decelerating at the end of the animation. Table 3 describes the interpolators that are contained in android.view.animation. If none of the provided interpolators suits your needs, implement the TimeInterpolator interface and create your own. See Using interpolators for more information on how to write a custom interpolator.

    Table 3. Interpolators

    Class/Interface Description
    AccelerateDecelerateInterpolator An interpolator whose rate of change starts and ends slowly but accelerates through the middle.
    AccelerateInterpolator An interpolator whose rate of change starts out slowly and then accelerates.
    AnticipateInterpolator An interpolator whose change starts backward then flings forward.
    AnticipateOvershootInterpolator An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value.
    BounceInterpolator An interpolator whose change bounces at the end.
    CycleInterpolator An interpolator whose animation repeats for a specified number of cycles.
    DecelerateInterpolator An interpolator whose rate of change starts out quickly and and then decelerates.
    LinearInterpolator An interpolator whose rate of change is constant.
    OvershootInterpolator An interpolator whose change flings forward and overshoots the last value then comes back.
    TimeInterpolator An interface that allows you to implement your own interpolator.

    Animating with ValueAnimator


    The ValueAnimator class lets you animate values of some type for the duration of an animation by specifying a set of intfloat, or color values to animate through. You obtain a ValueAnimator by calling one of its factory methods: ofInt()ofFloat(), or ofObject(). For example:

    ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
    animation.setDuration(1000);
    animation.start();

    In this code, the ValueAnimator starts calculating the values of the animation, between 0 and 1, for a duration of 1000 ms, when the start() method runs.

    You can also specify a custom type to animate by doing the following:

    ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
    animation.setDuration(1000);
    animation.start();

    In this code, the ValueAnimator starts calculating the values of the animation, between startPropertyValueand endPropertyValue using the logic supplied by MyTypeEvaluator for a duration of 1000 ms, when the start() method runs.

    The previous code snippets, however, has no real effect on an object, because the ValueAnimator does not operate on objects or properties directly. The most likely thing that you want to do is modify the objects that you want to animate with these calculated values. You do this by defining listeners in the ValueAnimator to appropriately handle important events during the animation's lifespan, such as frame updates. When implementing the listeners, you can obtain the calculated value for that specific frame refresh by calling getAnimatedValue(). For more information on listeners, see the section about Animation Listeners.

    Animating with ObjectAnimator


    The ObjectAnimator is a subclass of the ValueAnimator (discussed in the previous section) and combines the timing engine and value computation of ValueAnimator with the ability to animate a named property of a target object. This makes animating any object much easier, as you no longer need to implement the ValueAnimator.AnimatorUpdateListener, because the animated property updates automatically.

    Instantiating an ObjectAnimator is similar to a ValueAnimator, but you also specify the object and the name of that object's property (as a String) along with the values to animate between:

    ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
    anim.setDuration(1000);
    anim.start();

    To have the ObjectAnimator update properties correctly, you must do the following:

    • The object property that you are animating must have a setter function (in camel case) in the form ofset<propertyName>(). Because the ObjectAnimator automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo, you need to have a setFoo() method. If this setter method does not exist, you have three options:
      • Add the setter method to the class if you have the rights to do so.
      • Use a wrapper class that you have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.
      • Use ValueAnimator instead.
    • If you specify only one value for the values... parameter in one of the ObjectAnimator factory methods, it is assumed to be the ending value of the animation. Therefore, the object property that you are animating must have a getter function that is used to obtain the starting value of the animation. The getter function must be in the form of get<propertyName>(). For example, if the property name is foo, you need to have a getFoo() method.
    • The getter (if needed) and setter methods of the property that you are animating must operate on the same type as the starting and ending values that you specify to ObjectAnimator. For example, you must havetargetObject.setPropName(float) and targetObject.getPropName(float) if you construct the following ObjectAnimator:
      ObjectAnimator.ofFloat(targetObject, "propName", 1f)
    • Depending on what property or object you are animating, you might need to call the invalidate() method on a View to force the screen to redraw itself with the updated animated values. You do this in theonAnimationUpdate() callback. For example, animating the color property of a Drawable object only cause updates to the screen when that object redraws itself. All of the property setters on View, such assetAlpha() and setTranslationX() invalidate the View properly, so you do not need to invalidate the View when calling these methods with new values. For more information on listeners, see the section about Animation Listeners.

    Choreographing Multiple Animations with AnimatorSet


    In many cases, you want to play an animation that depends on when another animation starts or finishes. The Android system lets you bundle animations together into an AnimatorSet, so that you can specify whether to start animations simultaneously, sequentially, or after a specified delay. You can also nest AnimatorSetobjects within each other.

    The following sample code taken from the Bouncing Balls sample (modified for simplicity) plays the following Animator objects in the following manner:

    1. Plays bounceAnim.
    2. Plays squashAnim1squashAnim2stretchAnim1, and stretchAnim2 at the same time.
    3. Plays bounceBackAnim.
    4. Plays fadeAnim.
    AnimatorSet bouncer = new AnimatorSet();
    bouncer.play(bounceAnim).before(squashAnim1);
    bouncer.play(squashAnim1).with(squashAnim2);
    bouncer.play(squashAnim1).with(stretchAnim1);
    bouncer.play(squashAnim1).with(stretchAnim2);
    bouncer.play(bounceBackAnim).after(stretchAnim2);
    ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
    fadeAnim.setDuration(250);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(bouncer).before(fadeAnim);
    animatorSet.start();

    For a more complete example on how to use animator sets, see the Bouncing Balls sample in APIDemos.

    Animation Listeners


    You can listen for important events during an animation's duration with the listeners described below.

    You can extend the AnimatorListenerAdapter class instead of implementing the Animator.AnimatorListener interface, if you do not want to implement all of the methods of the Animator.AnimatorListener interface. The AnimatorListenerAdapter class provides empty implementations of the methods that you can choose to override.

    For example, the Bouncing Balls sample in the API demos creates an AnimatorListenerAdapter for just the onAnimationEnd() callback:

    ValueAnimatorAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
    fadeAnim.setDuration(250);
    fadeAnim.addListener(new AnimatorListenerAdapter() {
    public void onAnimationEnd(Animator animation) {
        balls.remove(((ObjectAnimator)animation).getTarget());
    }

    Animating Layout Changes to ViewGroups


    The property animation system provides the capability to animate changes to ViewGroup objects as well as provide an easy way to animate View objects themselves.

    You can animate layout changes within a ViewGroup with the LayoutTransition class. Views inside a ViewGroup can go through an appearing and disappearing animation when you add them to or remove them from a ViewGroup or when you call a View's setVisibility() method with VISIBLE, android.view.View#INVISIBLE}, or GONE. The remaining Views in the ViewGroup can also animate into their new positions when you add or remove Views. You can define the following animations in a LayoutTransitionobject by calling setAnimator() and passing in an Animator object with one of the following LayoutTransition constants:

    • APPEARING - A flag indicating the animation that runs on items that are appearing in the container.
    • CHANGE_APPEARING - A flag indicating the animation that runs on items that are changing due to a new item appearing in the container.
    • DISAPPEARING - A flag indicating the animation that runs on items that are disappearing from the container.
    • CHANGE_DISAPPEARING - A flag indicating the animation that runs on items that are changing due to an item disappearing from the container.

    You can define your own custom animations for these four types of events to customize the look of your layout transitions or just tell the animation system to use the default animations.

    The LayoutAnimations sample in API Demos shows you how to define animations for layout transitions and then set the animations on the View objects that you want to animate.

    The LayoutAnimationsByDefault and its corresponding layout_animations_by_default.xml layout resource file show you how to enable the default layout transitions for ViewGroups in XML. The only thing that you need to do is to set the android:animateLayoutchanges attribute to true for the ViewGroup. For example:

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:id="@+id/verticalContainer"
        android:animateLayoutChanges="true" />

    Setting this attribute to true automatically animates Views that are added or removed from the ViewGroup as well as the remaining Views in the ViewGroup.

    Using a TypeEvaluator


    If you want to animate a type that is unknown to the Android system, you can create your own evaluator by implementing the TypeEvaluator interface. The types that are known by the Android system are intfloat, or a color, which are supported by the IntEvaluatorFloatEvaluator, and ArgbEvaluator type evaluators.

    There is only one method to implement in the TypeEvaluator interface, the evaluate() method. This allows the animator that you are using to return an appropriate value for your animated property at the current point of the animation. The FloatEvaluator class demonstrates how to do this:

    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);
        }
    }

    Note: When ValueAnimator (or ObjectAnimator) runs, it calculates a current elapsed fraction of the animation (a value between 0 and 1) and then calculates an interpolated version of that depending on what interpolator that you are using. The interpolated fraction is what your TypeEvaluator receives through the fraction parameter, so you do not have to take into account the interpolator when calculating animated values.

    Using Interpolators


    An interpolator define how specific values in an animation are calculated as a function of time. For example, you can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or you can specify animations to use non-linear time, for example, using acceleration or deceleration at the beginning or end of the animation.

    Interpolators in the animation system receive a fraction from Animators that represent the elapsed time of the animation. Interpolators modify this fraction to coincide with the type of animation that it aims to provide. The Android system provides a set of common interpolators in the android.view.animation package. If none of these suit your needs, you can implement the TimeInterpolator interface and create your own.

    As an example, how the default interpolator AccelerateDecelerateInterpolator and the LinearInterpolator calculate interpolated fractions are compared below. The LinearInterpolator has no effect on the elapsed fraction. The AccelerateDecelerateInterpolator accelerates into the animation and decelerates out of it. The following methods define the logic for these interpolators:

    AccelerateDecelerateInterpolator

    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }

    LinearInterpolator

    public float getInterpolation(float input) {
        return input;
    }

    The following table represents the approximate values that are calculated by these interpolators for an animation that lasts 1000ms:

    ms elapsed Elapsed fraction/Interpolated fraction (Linear) Interpolated fraction (Accelerate/Decelerate)
    0 0 0
    200 .2 .1
    400 .4 .345
    600 .6 .8
    800 .8 .9
    1000 1 1

    As the table shows, the LinearInterpolator changes the values at the same speed, .2 for every 200ms that passes. The AccelerateDecelerateInterpolator changes the values faster than LinearInterpolatorbetween 200ms and 600ms and slower between 600ms and 1000ms.

    Specifying Keyframes


    Keyframe object consists of a time/value pair that lets you define a specific state at a specific time of an animation. Each keyframe can also have its own interpolator to control the behavior of the animation in the interval between the previous keyframe's time and the time of this keyframe.

    To instantiate a Keyframe object, you must use one of the factory methods, ofInt()ofFloat(), or ofObject() to obtain the appropriate type of Keyframe. You then call the ofKeyframe() factory method to obtain a PropertyValuesHolder object. Once you have the object, you can obtain an animator by passing in the PropertyValuesHolder object and the object to animate. The following code snippet demonstrates how to do this:

    Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
    Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
    Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
    PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
    ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
    rotationAnim.setDuration(5000ms);

    For a more complete example on how to use keyframes, see the MultiPropertyAnimation sample in APIDemos.

    Animating Views


    The property animation system allow streamlined animation of View objects and offers a few advantages over the view animation system. The view animation system transformed View objects by changing the way that they were drawn. This was handled in the container of each View, because the View itself had no properties to manipulate. This resulted in the View being animated, but caused no change in the View object itself. This led to behavior such as an object still existing in its original location, even though it was drawn on a different location on the screen. In Android 3.0, new properties and the corresponding getter and setter methods were added to eliminate this drawback.

    The property animation system can animate Views on the screen by changing the actual properties in the View objects. In addition, Views also automatically call the invalidate() method to refresh the screen whenever its properties are changed. The new properties in the View class that facilitate property animations are:

    • translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates which are set by its layout container.
    • rotationrotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
    • scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
    • pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
    • x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
    • alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值