原創作者:迷途開發者的博客
一、概述
3.0以前,Android支持兩種動畫模式,tween animation,frame animation,在android3.0中又引入了一個新的動畫系統:property animation,這
三種動畫模式在SDK中被稱爲property animation,view animation,drawable animation。
二、分類
1、View Animation(Tween Animation)
View Animation(Tween Animation):補間動畫,給出兩個關鍵幀,通過一些算法將給定屬性值在給定的時間內在兩個關鍵幀間漸變。
View animation只能應用於View對象,而且只支持一部分屬性,如支持縮放旋轉而不支持背景顏色的改變。
而且對於View animation,它只是改變了View對象繪製的位置,而沒有改變View對象本身,比如,你有一個Button,座標 (100,100),
Width:200,Height:50,而你有一個動畫使其變爲Width:100,Height:100,你會發現動畫過程中觸 發按鈕點擊的區域仍是(100,100)-(300,150)。
View Animation就是一系列View形狀的變換,如大小的縮放,透明度的改變,位置的改變,動畫的定義既可以用代碼定義也可以用XML定義,當
然,建議用XML定義。可以給一個View同時設置多個動畫,比如從透明至不透明的淡入效果,與從小到大的放大效果,這些動畫可以同時進行,也可
以在一個完成之後開始另一個。
用XML定義的動畫放在/res/anim/文件夾內,XML文件的根元素可以 爲<alpha>,<scale>,<translate>,<rotate>,interpolator 元素或<set>(表示以上
幾個動畫的集合,set可以嵌套)。默認情況下,所有動畫是同時進行的,可以通過startOffset屬性設置 各個動畫的開始偏移(開始時間)來達到動畫順
序播放的效果。
可以通過設置interpolator屬性改變動畫漸變的方式,如AccelerateInterpolator,開始時慢,然後逐漸加快。默認爲
AccelerateDecelerateInterpolator。
定義好動畫的XML文件後,可以通過類似下面的代碼對指定View應用動畫。
- <span style="font-size:14px;">ImageView spaceshipImage = (ImageView)findViewById(R.id.spaceshipImage);
- Animation hyperspaceJumpAnimation=AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
- spaceshipImage.startAnimation(hyperspaceJumpAnimation);</span>
2. Drawable Animation(Frame Animation)
Drawable Animation(Frame Animation):幀動畫,就像GIF圖片,通過一系列Drawable依次顯示來模擬動畫的效果。在XML
- <span style="font-size:14px;"><animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="true">
- <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
- <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
- <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
- </animation-list></span>
必須以<animation-list>爲根元素,以<item>表示要輪換顯示的圖片,duration屬性表示各項顯示的時間。XML文件要放
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- imageView = (ImageView) findViewById(R.id.imageView1);
- imageView.setBackgroundResource(R.drawable.drawable_anim);
- anim = (AnimationDrawable) imageView.getBackground();
- }
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- anim.stop();
- anim.start();
- return true;
- }
- return super.onTouchEvent(event);
- }
我在實驗中遇到兩點問題:
要在代碼中調用Imageview的setBackgroundResource方法,如果直接在XML佈局文件中設置其src屬性當觸發動畫時會FC。
最後一點是SDK中提到的,不要在onCreate中調用start,因爲AnimationDrawable還沒有完全跟Window相關聯,如果想要界面
3. Property Animation
屬性動畫,這個是在Android 3.0中才引進的,以前學WPF時裏面的動畫機制好像就是這個,它更改的是對象的實際屬性,在View Animation(Tween Animation)中,其改變的是View的繪製效果,真正的View的屬性保持不變,比如無論你在對話中如何縮放Button的大小,Button的 有效點擊區域還是沒有應用動畫時的區域,其位置與大小都不變。而在Property Animation中,改變的是對象的實際屬性,如Button的縮放,Button的位置與大小屬性值都改變了。而且Property Animation不止可以應用於View,還可以應用於任何對象。Property Animation只是表示一個值在一段時間內的改變,當值改變時要做什麼事情完全是你自己決定的。在Property Animation中,可以對動畫應用以下屬性:
Duration:動畫的持續時間
TimeInterpolation:屬性值的計算方式,如先快後慢
TypeEvaluator:根據屬性的開始、結束值與TimeInterpolation計算出的因子計算出當前時間的屬性值
Repeat Country and behavoir:重複次數與方式,如播放3次、5次、無限循環,可以此動畫一直重複,或播放完時再反向播放
Animation sets:動畫集合,即可以同時對一個對象應用幾個動畫,這些動畫可以同時播放也可以對不同動畫設置不同開始偏移
Frame refreash delay:多少時間刷新一次,即每隔多少時間計算一次屬性值,默認爲10ms,最終刷新時間還受系統進程調度與硬件的影響
3.1 Property Animation的工作方式
對於下圖的動畫,這個對象的X座標在40ms內從0移動到40 pixel.按默認的10ms刷新一次,這個對象會移動4次,每次移動40/4=10pixel。也可以改變屬性值的改變方法,即設置不同的interpolation
ValueAnimator即表示一個動畫,包含動畫的開始值,結束值,持續時間等屬性。
ValueAnimator封裝了一個TimeInterpolator,TimeInterpolator定義了屬性值在開始值與結束值之間的插值方法。
ValueAnimator還封裝了一個TypeAnimator,根據開始、結束值與TimeIniterpolator計算得到的值計算出屬性值。
ValueAnimator根據動畫已進行的時間跟動畫總時間(duration)的比計算出一個時間因子(0~1),然後根據TimeInterpolator計算出另一個因子,最後TypeAnimator通過這個因子計算出屬性值,如上例中10ms時:
首先計算出時間因子,即經過的時間百分比:t=10ms/40ms=0.25
經插值計算(inteplator)後的插值因子:大約爲0.15,上述例子中用了AccelerateDecelerateInterpolator,計算公式爲(input即爲時間因子):
- <span style="font-size:14px;color:#333333;">(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
- 最後根據TypeEvaluator計算出在10ms時的屬性值:0.15*(40-0)=6pixel。上例中TypeEvaluator爲FloatEvaluator,計算方法爲 :
- public Float evaluate(float fraction, Number startValue, Number endValue) {
- float startFloat = startValue.floatValue();
- return startFloat + fraction * (endValue.floatValue() - startFloat);
- }</span>
參數分別爲上一步的插值因子,開始值與結束值。
3.2 ValueAnimator
ValueAnimator包含Property Animation動畫的所有核心功能,如動畫時間,開始、結束屬性值,相應時間屬性值計算方法等。應用Property Animation有兩個步聚:計算屬性值
根據屬性值執行相應的動作,如改變對象的某一屬性。
ValuAnimiator只完成了第一步工作,如果要完成第二步,需要實現ValueAnimator.onUpdateListener接口,如:
- ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
- animation.setDuration(1000);
- animation.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- Log.i("update", ((Float) animation.getAnimatedValue()).toString());
- }
- });
- animation.setInterpolator(new CycleInterpolator(3));
- animation.start();
此示例中只是向Logcat輸出了一些信息,可以改爲想做的工作。
- Animator.AnimatorListener
- onAnimationStart()
- onAnimationEnd()
- onAnimationRepeat()
- onAnimationCancel
ValueAnimator.AnimatorUpdateListener
onAnimationUpdate() //通過監聽這個事件在屬性的值更新時執行相應的操作,對於ValueAnimator一般要監聽此事件執行相應的動作,不然Animation沒意義(可用於計時),在ObjectAnimator(繼承自ValueAnimator)中會自動更新屬性,如無必要不必監聽。在函數中會傳遞一個ValueAnimator參數,通過此參數的getAnimatedValue()取得當前動畫屬性值。
可以繼承AnimatorListenerAdapter而不是實現AnimatorListener接口來簡化操作,這個類對AnimatorListener中的函數都定義了一個空函數體,這樣我們就只用定義想監聽的事件而不用實現每個函數卻只定義一空函數體。
- ObjectAnimator oa=ObjectAnimator.ofFloat(tv, "alpha", 0f, 1f);
- oa.setDuration(3000);
- oa.addListener(new AnimatorListenerAdapter(){
- public void on AnimationEnd(Animator animation){
- Log.i("Animation","end");
- }
- });
- oa.start();
3.3 ObjectAnimator
對象應該有一個setter函數:set<PropertyName>(駝峯命名法)
如上面的例子中,像ofFloat之類的工場方法,第一個參數爲對象名,第二個爲屬性名,後面的參數爲可變參數,如果values…參數只設置了 一個值的話,那麼會假定爲目的值,屬性值的變化範圍爲當前值到目的值,爲了獲得當前值,該對象要有相應屬性的getter方 法:get<PropertyName>
如果有getter方法,其應返回值類型應與相應的setter方法的參數類型一致。
如果上述條件不滿足,則不能用ObjectAnimator,應用ValueAnimator代替。
- tv=(TextView)findViewById(R.id.textview1);
- btn=(Button)findViewById(R.id.button1);
- btn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- ObjectAnimator oa=ObjectAnimator.ofFloat(tv, "alpha", 0f, 1f);
- oa.setDuration(3000);
- oa.start();
- }
- });
把一個TextView的透明度在3秒內從0變至1。
根據應用動畫的對象或屬性的不同,可能需要在onAnimationUpdate函數中調用invalidate()函數刷新視圖。
3.4 通過AnimationSet應用多個動畫
AnimationSet提供了一個把多個動畫組合成一個組合的機制,並可設置組中動畫的時序關係,如同時播放,順序播放等。
以下例子同時應用5個動畫:
播放anim1;
同時播放anim2,anim3,anim4;
播放anim5。
- AnimatorSet bouncer = new AnimatorSet();
- bouncer.play(anim1).before(anim2);
- bouncer.play(anim2).with(anim3);
- bouncer.play(anim2).with(anim4)
- bouncer.play(anim5).after(amin2);
- animatorSet.start();
3.5 TypeEvalutors
根據屬性的開始、結束值與TimeInterpolation計算出的因子計算出當前時間的屬性值,android提供了以下幾個evalutor:IntEvaluator:屬性的值類型爲int;
FloatEvaluator:屬性的值類型爲float;
ArgbEvaluator:屬性的值類型爲十六進制顏色值;
TypeEvaluator:一個接口,可以通過實現該接口自定義Evaluator。
自定義TypeEvalutor很簡單,只需要實現一個方法,如FloatEvalutor的定義:
- 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);
- }
- }
根據動畫執行的時間跟應用的Interplator,會計算出一個0~1之間的因子,即evalute函數中的fraction參數,通過上述FloatEvaluator應該很好看出其意思。
3.6 TimeInterplator
time interplator定義了屬性值變化的方式,如線性均勻改變,開始慢然後逐漸快等。在Property Animation中是TimeInterplator,在View Animation中是Interplator,這兩個是一樣的,在3.0之前只有Interplator,3.0之後實現代碼轉移至了 TimeInterplator。Interplator繼承自TimeInterplator,內部沒有任何其他代碼。AccelerateInterpolator 加速,開始時慢中間加速
DecelerateInterpolator 減速,開始時快然後減速
AccelerateDecelerateInterolator 先加速後減速,開始結束時慢,中間加速
AnticipateInterpolator 反向 ,先向相反方向改變一段再加速播放
AnticipateOvershootInterpolator 反向加超越,先向相反方向改變,再加速播放,會超出目的值然後緩慢移動至目的值
BounceInterpolator 跳躍,快到目的值時值會跳躍,如目的值100,後面的值可能依次爲85,77,70,80,90,100
CycleIinterpolator 循環,動畫循環一定次數,值的改變爲一正弦函數:Math.sin(2 * mCycles * Math.PI * input)
LinearInterpolator 線性,線性均勻改變
OvershottInterpolator 超越,最後超出目的值然後緩慢改變到目的值
TimeInterpolator 一個接口,允許你自定義interpolator,以上幾個都是實現了這個接口
3.7 當Layout改變時應用動畫
ViewGroup中的子元素可以通過setVisibility使其Visible、Invisible或Gone,當有子元素可見性改變時,可以向其應用動畫,通過LayoutTransition類應用此類動畫:transition.setAnimator(LayoutTransition.DISAPPEARING, customDisappearingAnim);
通過setAnimator應用動畫,第一個參數表示應用的情境,可以以下4種類型:
APPEARING 當一個元素變爲Visible時對其應用的動畫
CHANGE_APPEARING 當一個元素變爲Visible時,因系統要重新佈局有一些元素需要移動,這些要移動的元素應用的動畫
DISAPPEARING 當一個元素變爲InVisible時對其應用的動畫
CHANGE_DISAPPEARING 當一個元素變爲Gone時,因系統要重新佈局有一些元素需要移動,這些要移動的元素應用的動畫 disappearing from the container.
第二個參數爲一Animator。
mTransitioner.setStagger(LayoutTransition.CHANGE_APPEARING, 30);
此函數設置動畫持續時間,參數分別爲類型與時間。
3.8 Keyframes
keyFrame是一個 時間/值 對,通過它可以定義一個在特定時間的特定狀態,而且在兩個keyFrame之間可以定義不同的Interpolator,就相當多個動畫的拼接,第一個動 畫的結束點是第二個動畫的開始點。KeyFrame是抽象類,要通過ofInt(),ofFloat(),ofObject()獲得適當的 KeyFrame,然後通過PropertyValuesHolder.ofKeyframe獲得PropertyValuesHolder對象,如以下 例子:- Keyframe kf0 = Keyframe.ofInt(0, 400);
- Keyframe kf1 = Keyframe.ofInt(0.25f, 200);
- Keyframe kf2 = Keyframe.ofInt(0.5f, 400);
- Keyframe kf4 = Keyframe.ofInt(0.75f, 100);
- Keyframe kf3 = Keyframe.ofInt(1f, 500);
- PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("width", kf0, kf1, kf2, kf4, kf3);
- ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(btn2, pvhRotation);
- rotationAnim.setDuration(2000);
上述代碼的意思爲:設置btn對象的width屬性值使其:
開始時 Width=400
動畫開始1/4時 Width=200
動畫開始1/2時 Width=400
動畫開始3/4時 Width=100
動畫結束時 Width=500
第一個參數爲時間百分比,第二個參數是在第一個參數的時間時的屬性值。
定義了一些Keyframe後,通過PropertyValuesHolder類的方法ofKeyframe封裝,然後通過ObjectAnimator.ofPropertyValuesHolder獲得Animator。
用下面的代碼可以實現同樣的效果:
- ObjectAnimator oa=ObjectAnimator.ofInt(btn2, "width", 400,200,400,100,500);
- oa.setDuration(2000);
- oa.start();
3.9 Animating Views
在View Animation中,對View應用Animation並沒有改變View的屬性,動畫的實現是通過其Parent View實現的,在View被drawn時Parents View改變它的繪製參數,draw後再改變參數invalidate,這樣雖然View的大小或旋轉角度等改變了,但View的實際屬性沒變,所以有效 區域還是應用動畫之前的區域,比如你把一按鈕放大兩倍,但還是放大這前的區域可以觸發點擊事件。爲了改變這一點,在Android 3.0中給View增加了一些參數並對這些參數增加了相應的getter/setter函數(ObjectAnimator要用這些函數改變這些屬性):translationX,translationY:轉換座標(control where the View is located as a delta from its left and top coordinates which are set by its layout container.)
rotation,rotationX,rotationY:旋轉,rotation用於2D旋轉角度,3D中用到後兩個
scaleX,scaleY:縮放
x,y:View的最終座標(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:透明度
跟位置有關的參數有3個,以X座標爲例,可以通過getLeft(),getX(),getTranslateX()獲得,若有一Button btn2,佈局時其座標爲(40,0):
- //應用動畫之前
- btn2.getLeft(); //40
- btn2.getX(); //40
- btn2.getTranslationX(); //0
- //應用translationX動畫
- ObjectAnimator oa=ObjectAnimator.ofFloat(btn2,"translationX", 200);
- oa.setDuration(2000);
- oa.start();
- /*應用translationX動畫後
- btn2.getLeft(); //40
- btn2.getX(); //240
- btn2.getTranslationX(); //200
- */
- //應用X動畫,假設沒有應用之前的translationX動畫
- ObjectAnimator oa=ObjectAnimator.ofFloat(btn2, "x", 200);
- oa.setDuration(2000);
- oa.start();
- /*應用X動畫後
- btn2.getLeft(); //40
- btn2.getX(); //200
- btn2.getTranslationX(); //160
- */
無論怎樣應用動畫,原來的佈局時的位置通過getLeft()獲得,保持不變;
X是View最終的位置;
translationX爲最終位置與佈局時初始位置這差。
所以若就用translationX即爲在原來基礎上移動多少,X爲最終多少
getX()的值爲getLeft()與getTranslationX()的和
對於X動畫,源代碼是這樣的:
- case X:
- info.mTranslationX = value - mView.mLeft;
- break;
Property Animation也可以在XML中定義
- <set> - AnimatorSet
- <animator> - ValueAnimator
- <objectAnimator> - ObjectAnimator
- XML文件應放大/res/animator/中,通過以下方式應用動畫:
- AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, R.anim.property_animator);
- set.setTarget(myObject);
- set.start();
3.10 ViewPropertyAnimator
如果需要對一個View的多個屬性進行動畫可以用ViewPropertyAnimator類,該類對多屬性動畫進行了優化,會合並一些invalidate()來減少刷新視圖,該類在3.1中引入。以下兩段代碼實現同樣的效果:
- <pre name="code" class="java">PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
- PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
- ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
- myView.animate().x(50f).y(100f);
- End