先來一個例子: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
首先經過一個簡單的例子看一下屬性動畫的工做原理。如圖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封裝了TimeInterpolator,TypeEvaluator 。 TimeInterpolator定義了動畫插值器,TypeEvaluator則定義了怎樣計算當前的屬性值,在圖2中,使用的TimeInterpolator是AccelerateDecelerateInterpolator, 使用的theTypeEvaluator是IntEvaluator 。
要開始一個動畫,首先建立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 動畫能夠知足要求,或者已有的代碼中已經使用的視圖動畫的話,也沒有必要遷移到屬性動畫。
在 android.animation 下幾乎能夠找到全部的屬性動畫系統的API
. 因爲視圖動畫系統已在 android.view.animation 下定義了不少實用的插值器
, 你能夠在屬性動畫中直接使用它們. 下面的表格展現屬性動畫系統的主要組件。
T 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. 要實現屬性動畫有兩個方面的工做必須作:一是 計算屬性值 , 而是把這些值更新到目標對象的相應屬性上。. 因此你必須監聽 |
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 |
The ValueAnimator
class lets you animate values of some type for the duration of an animation by specifying a set of int
, float
, 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 startPropertyValue
and 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設置監聽器來實如今適當的時候更新目標對象的屬性。
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.
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 AnimatorSet
objects within each other.
The following sample code taken from the Bouncing Balls sample (modified for simplicity) plays the followingAnimator
objects in the following manner:
Plays bounceAnim
.
Plays squashAnim1
, squashAnim2
, stretchAnim1
, and stretchAnim2
at the same time.
Plays bounceBackAnim
.
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.
You can listen for important events during an animation's duration with the listeners described below.
onAnimationStart()
- Called when the animation starts.
onAnimationEnd()
- Called when the animation ends.
onAnimationRepeat()
- Called when the animation repeats itself.
onAnimationCancel()
- Called when the animation is canceled. A cancelled animation also calls onAnimationEnd()
, regardless of how they were ended.
onAnimationUpdate()
- 動畫的每一幀都會調用此方法. Listen to this event to use the calculated values generated by ValueAnimator
during an animation. To use the value, query the ValueAnimator
object passed into the event to get the current animated value with the getAnimatedValue()
method. Implementing this listener is required if you use ValueAnimator
.
Depending on what property or object you are animating, you might need to call invalidate()
on a View to force that area of the screen to redraw itself with the new animated values. 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 as setAlpha()
and setTranslationX()
invalidate the View properly, so you do not need to invalidate the View when calling these methods with new values.
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());
}
。。。。。。
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 int
, float
, or a color, which are supported by the IntEvaluator
, FloatEvaluator
, 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.
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;
}
A 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.
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.
rotation
, rotationX
, 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.
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.
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:
ValueAnimator
- <animator>
ObjectAnimator
- <objectAnimator>
AnimatorSet
- <set>
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.