Property Animator

先来一个例子:html

view的 invalidate 和 requestLayout方法是不同的, invalidatejava

ValueAnimator va = ValueAnimator.ofInt(0, shopTopHeight);
			
va.addUpdateListener(new AnimatorUpdateListener(){

	@Override
	public void onAnimationUpdate(ValueAnimator animation) {
		int curHeight =(Integer)animation.getAnimatedValue();
		view.getLayoutParams().height = curHeight;
		view.requestLayout();	
	}
				
});
			
va.setDuration(600);			
va.start();

// 能够这样设置本身的evaluator:
//    va.setEvaluator(new TypeEvaluator<Integer>(){
//
//	@Override
//	public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
//	    if(fraction >= 0.99){
//		shopTop.setVisibility(View.GONE);
//		return 0;
//	    }
//	    int curHeight =(int)( startValue - fraction*startValue);
//           // view的动画属性能够在这里更新,也能够在 updateListener 里更新
//	    view.getLayoutParams().height = curHeight;
//	    view.invalidate();
//	    return curHeight;
//      }
//				
//   });


属性动画系统(property animation system) 是一个健壮稳定的框架,它能够帮助你实现几乎全部的动画需求。你能够定义动画实现一个对象的属性的值随着时间改变而改变,无论这个对象是否是被画在屏幕上。属性动画是值在必定时间段内改变一个对象的属性值(从一个原始值到一个新值)。要是某样东西“动”起来,你必须指定改对象的一个属性(好比它在屏幕上的位置)、动画的持续时间以及 要“动”的属性的开始值和结束值。android

属性动画系统容许你定义一下动画特性:api

  • Duration(动画时间): You can specify the duration of an animation. The default length is 300 ms.app

  • 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.less

  • Animator sets(动画组): You can group animations into logical sets that play together or sequentially or after specified delays.ide

  • 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.post



How Property Animation Works


首先经过一个简单的例子看一下属性动画的工做原理。如图1所示,假设咱们对该对象的属性X作属性动画,动画时间设置为40ms,要移动的距离是40个像素。每10ms(默认的帧刷新频率) 对象水平移动10pixel,动画结束时移动了40pixel。该例子使用的是线性插值器,即对象以恒定速率移动。动画

Figure 1. Example of a linear animation

你也可使用非线性的插值。图2展现的是使用了一个在动画开始阶段加速在结束阶段减速的插值器,该物体一样在40ms里移动了40pixel, 可是不是以线性速度移动的。

Figure 2. Example of a non-linear animation

下面看一下属性动画系统的关键组件是如何实现上文展现的动画的。图3描绘了主要类之间的协做。

Figure 3. How animations are calculated

ValueAnimator 对象负责动画计时,包括动画进行的时间以及动画属性的当前值。

ValueAnimator封装了TimeInterpolatorTypeEvaluator 。 TimeInterpolator定义了动画插值器,TypeEvaluator则定义了怎样计算当前的属性值,在图2中,使用的TimeInterpolator是AccelerateDecelerateInterpolator, 使用的theTypeEvaluatorIntEvaluator 。

要开始一个动画,首先建立ValueAnimator 而且设置属性的开始和结合值,同时还要设置动画的持续时间。当你调用 start()  方法时,动画开始。在整个动画期间,ValueAnimator 以动画持续时间为基准,将已流逝时间转换成一个介于0和1之间的分数(流逝时间分数),该分数表明动画的完成程度。

ValueAnimator完成了计算流逝时间分数,它会调用内设的 TimeInterpolator 来计算插值分数。插值分数是有插值器生成的对流逝时间分数的一个映射值。以图2为例,因为设置的插值器是开始加速而后加速的,当t=10ms时, 流逝时间分数是0.25(由于总时间是40ms),而插值分数是0.15, 要比流逝时间分数小。在图1中,因为默认的插值器是线性的,所以插值分数与流逝时间分数始终是相等的。

完成了插值分数的计算后,ValueAnimator 会调用合适的  TypeEvaluator,根据插值分数、属性开始值、属性结束值来计算目标属性的当前值。 以图2为例,t=10ms时,插值分数是0.15, 那么属性值是 0.15 * (40 - 0), 即 6 pixel 。

How Property Animation Differs from View Animation

属性动画系统与视图动画系统(view animation)的不一样。 视图动画只能使 view 动起来, 而对于 non-view的对象,就只能本身写代码来实现。属性动画只能对View公开的一些属性进行动画,好比 scaling、rotation, 可是像 background color 则实现不了动画效果。

另外视图动画只修改了view的绘画位置,而不是view自己。好比你经过视图动画实现一个button在屏幕上移动,看起来button是在移动,可是你能够点击button的位置并无改变(追着button点击是无效果的)。

经过使用属性动画系统,能够把这些弊病通通移除,对view 和 non-view的对象均可以实现动画,而且对象自己是真真切切被改变了的。属性动画系统在实现动画效果时也更稳健。你能够设置要改变的属性(颜色、位置、大小等)也能够设置动画的特性(插值、多个动画的播放顺序等)

固然,视图动画实现起来更便捷,能够写更少的代码。若是一个view 动画能够知足要求,或者已有的代码中已经使用的视图动画的话,也没有必要迁移到属性动画。

API Overview


在 android.animation 下几乎能够找到全部的属性动画系统的API. 因为视图动画系统已在 android.view.animation 下定义了不少实用的插值器, 你能够在属性动画中直接使用它们. 下面的表格展现属性动画系统的主要组件。

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

是属性动画的主要计时引擎,同时负责计算动画属性的值.它拥有计算动画属性的值的所有核心功能

,而且包含了每一个动画的计时细节, 动画是否重复播放的相关信息,接收动画更新事件的监听器, 

and the ability to set custom types to evaluate. 要实现属性动画有两个方面的工做必须作:一是 计算属性值 ,

而是把这些值更新到目标对象的相应属性上。. ValueAnimator 没有作第二项工做, 

因此你必须监听 ValueAnimator 计算新值触发的更新事件并按照本身的逻辑修改目标对象的属性.

ObjectAnimator

 ValueAnimator 的子类,容许设置目标对象以及目标对象要实现动画的属性.

 此类没计算出动画属性值后会自动更新目标对象的属性. 由于它使实现目标对象的属性动画效果变得容易,

因此大部分状况下你都应该使用ObjectAnimator 这个类。

可是有时候你让然须要直接使用 ValueAnimator 由于ObjectAnimator 有一些限制, 

好比要求目标对象提供相应的getter 或 setter方法.

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 Sets for more information.


 求值器(Evaluators)可以辅助属性动画系统计算给定的属性的值。 求值器以 Animator 提供的计时数据、属性开始值以及属性结束值做为参数来计算属性的当前值。属性动画系统提供了一下 求职器:

Table 2. Evaluators

Class/Interface Description
IntEvaluator int 型属性的默认求值器
FloatEvaluator float 型属性的默认求值器
ArgbEvaluator 以十六进制表示的color属性 的默认求值器
TypeEvaluator

实现定制的求值器时必须实现的接口

。若是你在实现一个object (非 int 、float)类型的属性 的动画,

那么你必须实现TypeEvaluator接口来明确怎样计算属性的动画值。


时间插值器定义了怎样根据时间来计算确切的属性动画值。你能够定义动画匀速的、线性的执行,也可让动画非线性的、有加减速的执行。

Table 3. Interpolators

Class/Interface Description

AccelerateDecelerateInterpolator 变化速率在开始和结束时慢,打在中间阶段会加速
AccelerateInterpolator 又慢变快
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 thestart() 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 callinggetAnimatedValue(). For more information on listeners, see the section about Animation Listeners.

上面的代码并不会对目标对象产生任何实际的影响,由于 ValueAnimator 不对目标对象会属性直接进行操做。你能够经过为ValueAnimator设置监听器来实如今适当的时候更新目标对象的属性。


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 theValueAnimator.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:

  • 要实现动画效果的属性必须有对应的setter方法  (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 isfoo, 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.

  • 若是对ObjectAnimator工厂方法的可变参数 values...只设置一个参数, 那么改参数值被认为是动画结束时的属性值. 所以动画属性必须有相应的getter方法来取得属性的开始值. The getter function must be in the form of get<propertyName>(). For example, if the property name is foo, you need to have agetFoo() 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 aboutAnimation 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 followingAnimator 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 theAnimator.AnimatorListener interface, if you do not want to implement all of the methods of theAnimator.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 theonAnimationEnd() 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

。。。。。。


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 thefraction 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 theLinearInterpolator 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;
}

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(), orofObject() 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).

To animate a property of a View object, such as its color or rotation value, all you need to do is create a property animator and specify the View property that you want to animate. For example:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

For more information on creating animators, see the sections on animating with ValueAnimator andObjectAnimator.

Animating with ViewPropertyAnimator

The ViewPropertyAnimator provides a simple way to animate several properties of a View in parallel, using a single underlying Animator object. It behaves much like an ObjectAnimator, because it modifies the actual values of the view's properties, but is more efficient when animating many properties at once. In addition, the code for using the ViewPropertyAnimator is much more concise and easier to read. The following code snippets show the differences in using multiple ObjectAnimator objects, a single ObjectAnimator, and theViewPropertyAnimator when simultaneously animating the x and y property of a 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);

For more detailed information about ViewPropertyAnimator, see the corresponding Android Developers blog post.

Declaring Animations in XML


The property animation system lets you declare property animations with XML instead of doing it programmatically. By defining your animations in XML, you can easily reuse your animations in multiple activities and more easily edit the animation sequence.

To distinguish animation files that use the new property animation APIs from those that use the legacy view animation framework, starting with Android 3.1, you should save the XML files for property animations in theres/animator/ directory (instead of res/anim/). Using the animator directory name is optional, but necessary if you want to use the layout editor tools in the Eclipse ADT plugin (ADT 11.0.0+), because ADT only searches the res/animator/ directory for property animation resources.

The following property animation classes have XML declaration support with the following XML tags:

The following example plays the two sets of object animations sequentially, with the first nested set playing two object animations together:

<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>

In order to run this animation, you must inflate the XML resources in your code to an AnimatorSet object, and then set the target objects for all of the animations before starting the animation set. Calling setTarget() sets a single target object for all children of the AnimatorSet as a convenience. The following code shows how to do this:

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

For information about the XML syntax for defining property animations, see Animation Resources.

相关文章
相关标签/搜索