Android动画框架(二)----属性动画
轉載請注明出處:http://blog.csdn.net/fishle123/article/details/50705928
Android提供三種形式動畫:視圖動畫,幀動畫,屬性動畫。其中屬性動畫的功能最強大,在Android?3.0中開始引入。本文介紹屬性動畫。屬性動畫可以針對Object的任何一個屬性實施動畫,并且Object的屬性值會隨著動畫改變,這一點與視圖動畫不同。屬性動畫功能非常強大,幾乎可以實現任何動畫效果。本文首先介紹屬性動畫的工作原理,然后全面的介紹了屬性動畫的使用技巧,為了更好的使用屬性動畫,還詳細的介紹了TimeInterpolator和TypeEvaluatory,以及KeyFrame的使用,最后總結了多種同時對多個屬性作用動畫的方法。
屬性動畫工作原理
介紹屬性動畫的工作原理之前,先看兩個例子。下圖展示了一個屬性動畫,它針對Object的橫坐標x做動畫,動畫時間是40ms,橫坐標x從x=0增加到x=40。從圖中可以看出,每個10ms(Android默認的動畫更新頻率)Object向右水平移動10pixel。在40ms的時候,Object停止在x=40的位置。這個例子中描述的是一個使用線性差值器的例子,Object以一個平均速度移動。
?
當然,通過使用不同的差值器,可以讓Object以非線性的速度移動。下圖展示的這個動畫,Object仍然是在40ms內移動40pixel。但是在開始的時候移動速度不斷加快,快結束的時候移動速度逐漸變小。從圖中可以看出,Object在中間時刻移動速度比開始和結束的時候都要快一些。
?
屬性動畫的核心在于計算不同時刻Object的屬性值。下圖指出了Android屬性動畫相關的幾個核心類之間如何協作完成動畫的。
?
其中,ValueAnimator記錄了動畫的相關信息,如屬性的起始值和結束值,以及開始動畫后,該屬性在當前時刻的值(value),動畫播放了多長時間等。
在ValueAnimator中有一個TimeInterpolator參數,即時間差值器,它根據時間流逝的百分比來計算當前屬性值改變的百分比。在ValueAnimator中還有一個TypeEvaluator參數,即類型估值器,它根據時間差值器來計算在動畫播放過程中某一個特定時間Object的屬性值。例如,在上面的圖2中TimeInterpolator使用的是?AccelerateDecelerateInterpolator?,ValueEvaluator使用的是IntEvaluator。
設計動畫的時候,先使用Object、屬性起始值、結束值、動畫時間等參數創建一個ValueAnimator,然后調用ValueAnimator的start方法開始播放。在動畫期間,ValueAnimator根據流逝的時間計算出一個elapsed?fraction流逝分數(0----1之間),這個流逝分數描述了動畫已經完成了多少,0表示0%即動畫還沒開始,1表示100%即動畫已經全部完成。例如,在上面的圖1中,在t=10ms的時候流逝比例為10ms/40ms=0.25。
ValueAnimator計算完流逝分數之后,調用TimeInterpolator根據流逝分數計算interpolated?fraction差值分數。例如,在上面圖2中,設置的TimeInterpolator是AccelerateDecelerateInterpolator?,在t=10ms的時候,流逝分數是0.25,但是差值分數是0.15(AccelerateDecelerateInterpolator?在開始的時候緩慢加速)。在上面圖1中,因為是勻速移動,差值分數一直等于流逝分數。
計算完差值分數之后,ValueAnimator調用TypeEvaluator根據差值分數、屬性起始值、結束值來計算屬性的最新值。例如,在上面的圖2中,在t=10ms時,差值分數為0.15,所以屬性的值為0.15*(40-0)=6.
屬性動畫與視圖動畫的差異
1)視圖動畫的對象僅限于View,而且只是View的幾個屬性(透明度,旋轉,縮放,平移)可以做動畫。
2)視圖動畫僅僅影響View的繪制,并沒有真正改變View對應的屬性值。如針對Button的平移動畫,將Button的中心從A點移動到B點,動畫完成后Button在B點顯示,但如果要點擊Button,還是得點A點區域,而不是點B點區域。
3)屬性動畫則沒有視圖動畫的這些缺點,你可以對任何Object(Views?或者non-Views)的任何屬性(如顏色,位置,大小)。
當然,視圖動畫使用起來更簡單。如果視圖動畫已經滿足了你的動畫需求,那也沒必要說一定要使用屬性動畫。
相關API
在Android中Animator是屬性動畫框架的基礎,但是我們并不直接使用Animator來設計屬性動畫。而是使用Animator的三個子類ValueAnimator、ObjectAnimator、AnimatorSet。另外,還提供了一些TimeInterpolator和ValueEvaluator。
先看看Animator相關的類:
| Class | Description |
| ValueAnimator | 它實現了屬性動畫的所有核心功能。屬性動畫有兩個核心部分:1)計算動畫屬性的值;2)將計算出來的值賦值給該動畫屬性。ValueAnimator沒有實現第2)點功能,因此需要我們自己監聽ValueAnimator.AnimatorUpdateListener, 并在onAnimationUpdate(ValueAnimator?animation)中手動更新屬性值。 |
| ObjectAnimator | 它是ValueAnimator的子類。ObjectAnimator不僅實現了動畫播放(即上面的核心功能1計算動畫屬性得值),還會將動畫過程中計算的屬性值設置到相應的屬性上(即上面的核心功能2)。因此,大部分情形使用ObjectAnimator就可以了。當然,有時候也需要使用ValueAnimator,因為ObjectAnimator要求動畫操作的屬性必須提供相應的getXXX()和setXXX()方法。? |
| AnimatorSet | 動畫集合,可以操作多個動畫之間的播放順序。 |
?
估值器Evaluator在屬性動畫中用來計算動畫過程中被操作的屬性的值。Android提供了以下Evaluators:
| Class/Interface | Description |
| IntEvaluator | int類型屬性的默認估值器 |
| FloatEvaluator | float類型屬性的默認估值器 |
| ArgbEvaluator | color類型屬性的默認估值器 |
| TypeEvaluator | 它是一個interface類型。如果我們要實現自己的Evaluator,則需要實現這個接口。當動畫屬性的類型不是int,float,color的時候,就需要我們定義自己的Evaluator了。? |
?
時間差值器TimeInterpolator用來定義動畫過程中被操作的屬性值隨時間變化的規則。例如,可以指定為LinearInterpolator線性差值器,這樣Object的屬性值隨時間均速變化。當然,也可以指定為非線性變化的差值器,如AccelerateDecelerateInterpolator加速減速差值器,動畫開始的時候變化速率逐漸增加,快結束的時候變化速率逐漸減慢,即開始和結束的時候變化慢,中間變化快。Android在android.view.animation包中提供了很多差值器,如果這些差值器不能滿足要求,我們還可以定義自己的差值器,只需要實現TimeInterpolator接口就可以了。下面看一下系統提供的差值器:
| Class/Interface | Description |
| AccelerateDecelerateInterpolator | 開始和結束的時候變化慢,中間變化快 |
| AccelerateInterpolator | 開始后一直加速 |
| AnticipateInterpolator | 開始的時候向后然后向前移動 |
| AnticipateOvershootInterpolator | 開始的時候向后然后向前越過一定值后返回最后的值 |
| BounceInterpolator | 動畫結束的時候彈起 |
| CycleInterpolator | 循環播放特定的次數,速率改變沿著正弦曲線 |
| DecelerateInterpolator | 開始快,逐漸減速 |
| LinearInterpolator | 勻速變化 |
| OvershootInterpolator | 向前越過一定值后再回到原來位置 |
| TimeInterpolator | 定義了時間差值器的接口,通過實現TimeInerpolaor接口來定義自己的差值器 |
?
ValueAnimator的使用
它實現了屬性動畫的所有核心功能。屬性動畫有兩個核心部分:1)計算動畫屬性的值;2)將計算出來的值賦值給該動畫屬性。ValueAnimator沒有實現第2點功能,因此需要我們自己監聽ValueAnimator.AnimatorUpdateListener,
并在onAnimationUpdate(ValueAnimator?animation)中手動更新屬性值。如果不手動實現第2點功能,將看不到任何動畫效果。
下面使用ValueAnimator演示屬性動畫。這個例子里面針對小球的橫縱坐標x,y做動畫,從0pixel到500pixel。
private void doVauleAnimator(){ ValueAnimator animator = ValueAnimator.ofFloat(0,500); animator.setDuration(3000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub float value = (Float) animation.getAnimatedValue(); mBall.setTranslationX(value); } }); animator.start(); }protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBall = (ImageView)findViewById(R.id.imageViewBall); mBtnStart = (Button)findViewById(R.id.start); mBtnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub doVauleAnimator(); } }); }布局文件activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context=".MainActivity" ><ImageViewandroid:id="@+id/imageViewBall"android:layout_width="40dp"android:layout_height="40dp"android:src="@drawable/red_ball" /><Buttonandroid:id="@+id/start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:text="start" /></RelativeLayout>動畫效果如下:
?
ObjectAnimator的使用
它是ValueAnimator的子類。ObjectAnimator不僅實現了動畫播放(即上面的核心功能1計算動畫屬性得值),還會將動畫過程中計算的屬性值設置到相應的屬性上(即上面的核心功能2)。因此,大部分情形使用ObjectAnimator就可以了。當然,有時候也需要使用ValueAnimator,因為ObjectAnimator要求動畫操作的屬性必須提供相應的getXXX()和setXXX()方法。?
同樣地,在上面平移的例子中改成旋轉,可以使用ObjectAnimator如下實現:
private void doObjectAnimator(View target){ ObjectAnimator animator=ObjectAnimator.ofFloat(target, "rotationX", 0,-180,0); animator.setDuration(3000); animator.start(); }由此可見,ObjectAnimator使用起來比ValueAnimator簡單很多。動畫效果如下:
?
如果想使用ObjeactAnimator同時對多個屬性進行動畫,如旋轉的同時做平移,同樣可以利用ValueAnimator.AnimatorUpdateListener接口來手動更新屬性值,因此可以如下實現:
private void doTranslateAndRoate(final View target){ String propName="anything"; ObjectAnimator animator = ObjectAnimator.ofFloat(target, propName, 0f,1.0f); animator.setDuration(2000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub float value =(Float)animation.getAnimatedValue(); //value也可以利用animation.getAnimatedFraction()來計算; target.setTranslationX(value*100); target.setRotationX(value*360); } }); animator.start(); }AnimatorSet的使用
對同一個Object同時作用多個屬性動畫效果,除了按上面提到的通過監聽ValueAnimator.AnimatorUpdateListener來實現外,還可以使用AnimatiorSet。AnimatorSet除了能同時作用多個屬性外,還可以實現更為精確的順序控制。上面使用ObjectAnimator實現的同時作用多個屬性的動畫效果,使用AnimatorSet可以如下實現:
private void doTranslateAndRoatateWithAnimatorSet(View target){ ObjectAnimator animator0=ObjectAnimator.ofFloat(target, "RotationX", 0,360); ObjectAnimator animator1=ObjectAnimator.ofFloat(target, "TranslationX", 0,100); AnimatorSet set = new AnimatorSet(); set.playTogether(animator0,animator1); set.setDuration(2000); set.start(); }使用XML設計屬性動畫
除了代碼的方式,也可以使用XML來設計屬性動畫。屬性動畫的xml文件需要保存到res/animator目錄下(而不是視圖動畫的res/anim),當然Google也說了使用animator目錄是可選的。但是如果使用的eclipse?adt,就必須保存到animator下面。在XML中分別使用<animator>、<objectAnimator>、<set>三個標簽來定義ValueAnimator、ObjectAnimator、AnimatorSet這三種類型的動畫。這里給出一個例子:
<set xmlns:android="http://schemas.android.com/apk/res/android"android:ordering="sequentially" ><set><objectAnimatorandroid:duration="500"android:propertyName="x"android:valueTo="400"android:valueType="intType" /><objectAnimatorandroid:duration="500"android:propertyName="y"android:valueTo="300"android:valueType="intType" /></set><objectAnimatorandroid:duration="500"android:propertyName="alpha"android:valueTo="1" /></set>然后在代碼中引用這個動畫:
AnimatorSet set = (AnimatorSet) AnimatorInflater .loadAnimator(MainActivity.this, R.animator.prop_anim); set.setTarget(target); set.start();Animation?Listeners
Android提供了以下監聽器幫助我們來處理一些重要的動畫事件:
Animator.AnimatorListener
onAnimationStart()?-?動畫開始時被調用
onAnimationEnd()?-?動畫結束時被調用
onAnimationRepeat()?-動畫重復播放時被調用
onAnimationCancel()?-?動畫被取消時被調用,接著onAnimationEnd也會被調用
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate()?-?動畫的每一幀都會調用,通過監聽Update事件,我們就可以使用ValueAnimator計算出來的屬性值(通過?getAnimatedValue()方法獲取),然后實現自己的動畫邏輯。
對于AnimatorListener接口,如果不想實現所有方法,可以繼承?AnimatorListenerAdapter,僅僅重寫我們需要監聽的事件方法。
當我們對不同的屬性作用動畫時,有時候可能需要調用View的invalidate()方法來強制重繪,以便新的屬性值生效。例如,對一個Drawable對象的color屬性作用屬性動畫時,在重繪的時候僅僅只是刷新屏幕。而View的所有提供了setter的屬性如setAlpha,setTranslationX等會自動調用invalidate,不需要手動調用invalidate。
理解TypeEvaluator
Android提供了IntEvaluator,FloatEvaluator和ArgbEvaluator三種類型的估值器,如果我們想用來做動畫的屬性不是這三種類型,如color類型,或者某個自定義的類型,那么可以通過實現TypeEvaluator來定義我們自己的TypeEvaluator類型估值器,只需要實現一個evaluate方法就可以了。evaluate方法的參數是TimeInterpolator時間差值器計算出來的差值分數、屬性的起始值、結束值,然后根據這個三個計算當前的屬性值。
下面看一下FloatEvaluator的源碼:
public class FloatEvaluator implements TypeEvaluator<Number> {/*** This function returns the result of linearly interpolating the start and end values, with* <code>fraction</code> representing the proportion between the start and end values. The* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,* and <code>t</code> is <code>fraction</code>.** @param fraction The fraction from the starting to the ending values* @param startValue The start value; should be of type <code>float</code> or* <code>Float</code>* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>* @return A linear interpolation between the start and end values, given the* <code>fraction</code> parameter.*/public Float evaluate(float fraction, Number startValue, Number endValue) {float startFloat = startValue.floatValue();return startFloat + fraction * (endValue.floatValue() - startFloat);} }因為時間差值器已經在計算差值分數的時候已經把動畫的變化效果考慮進去了,所以在計算屬性值得時候不需要再考慮差值變化的問題。
理解Interpolators
在動畫中,差值器用來定義屬性值如何隨時間變化。差值器根據Animator計算出來的流逝時間分數來計算出一個差值分數。根據動畫效果的不同,相同的流逝時間分數會計算出不同的差值分數。如果Android提供的差值器都不能達到我們的想要的效果,可以實現TimeInterpolator來定義我們自己的差值器。
下面看一下AccelerateDecelerateInterpolator和?LinearInterpolator的源碼,看看它們是怎樣計算差值分數的:
AccelerateDecelerateInterpolator源碼:
/*** An interpolator where the rate of change starts and ends slowly but* accelerates through the middle.* */ public class AccelerateDecelerateInterpolator implements Interpolator {public AccelerateDecelerateInterpolator() {}@SuppressWarnings({"UnusedDeclaration"})public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {}public float getInterpolation(float input) {return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;} }LinearInterpolator源碼:
/*** An interpolator where the rate of change is constant**/ public class LinearInterpolator implements Interpolator {public LinearInterpolator() {}public LinearInterpolator(Context context, AttributeSet attrs) {}public float getInterpolation(float input) {return input;} }下表給出了使用AccelerateDecelerateInterpolator和?LinearInterpolator計算出來的差值。
| 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 |
從上表可以看出,LinearInterpolator計算出來的差值隨時間勻速變化,每200ms增加0.2。而AccelerateDecelerateInterpolator在開始的時候緩慢增加,在中間的時候如600ms的時候增加很快(在400ms----600ms之間增加明顯比LinearInterpolator快),后面又開始緩慢增加。
Keyframes的使用
可以使用關鍵幀KeyFrame來指定在特定時刻的動畫幀。KeyFrame提供了ofInt()、ofFloat()、ofObject()三個工廠方法來幫助我們初始化一個KeyFrame。每個KeyFrame可以指定自己的TimeInterpolator,這個TimeInterpolator在上一個KeyFrame和當前KeyFrame之間的這段時間的動畫上生效。我們可以如下使用KeyFrame:
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);//ofFloat(fraction,value) 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(5000);同時對多個屬性作用屬性動畫
上面的例子中有使用ValueAnimator.AnimatorUpdateListener可以實現同時對多個屬性作用動畫,其實Android提供了更簡單的辦法來同時對多個屬性作屬性動畫。下面簡單總結一下:
1)使用AnimatorSet
ObjectAnimator animX = ObjectAnimator.ofFloat(target, "x", 50f); ObjectAnimator animY = ObjectAnimator.ofFloat(target, "y", 100f); AnimatorSet animSetXY = new AnimatorSet(); animSetXY.playTogether(animX, animY); animSetXY.start();2)使用PropertyValuesHolder?
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f); PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f); ObjectAnimator.ofPropertyValuesHolder(target, pvhX, pvhY).start();3)使用ViewPropertyAnimator
target.animate().x(50f).y(100f).setDuration(4000);View的animate()方法返回一個ViewPropertyAnimator對象的實例。
4)使用ValueAnimator.AnimatorUpdateListener
String propName="anything"; ObjectAnimator animator = ObjectAnimator.ofFloat(target, propName, 0f,1.0f); animator.setDuration(2000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { // TODO Auto-generated method stub float value =(Float)animation.getAnimatedValue(); //value也可以利用animation.getAnimatedFraction()來計算; target.setTranslationX(value*100); target.setRotationX(value*360); } }); animator.start();相比于其他幾種方法,AnimatorSet可以更容易的控制多個動畫之間的順序。
總結
到此為止,本文詳細的分析android屬性動畫的工作原理及相關使用技巧,還介紹了布局動畫的詳細使用。
如果大家對動畫感興趣,可以閱讀我的動畫相關的其他文章,看完這些文章相信大多數動畫相關的問題都能解決了。
Android動畫框架(一)----視圖動畫&幀動畫
Android動畫框架(三)----布局動畫&Activity過渡動畫
總結
以上是生活随笔為你收集整理的Android动画框架(二)----属性动画的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: excel到期弹窗提醒桌面弹_打开Exc
- 下一篇: STM32F1单片机参考文档