1.引言
早期Android提供了兩種動畫方式,逐幀動畫(frame-by-frame animation)和補間動畫(tweened animation)。逐幀動畫的工作原理如同卡通片一樣,通過一張張單獨的圖檔連貫起來播放。補間動畫則是可以對View進行一系列的動畫操作,包括淡入淡出、縮放、平移、旋轉四種。
這裡就會有人有疑問了,然而我們還是要看到這兩種動畫的一些限制,猶其是補間動畫這種非常常用的動畫,在功能和可擴充方面還是有相當大的局限性,主要展現在以下三個方面:
- 補間動畫是隻能夠作用在View上的,無法非View的對象進行動畫操作。這是什麼意思呢?舉個例子,比如說我們有一個自定義的View,在這個View當中有一個Point對象用于管理坐标,然後在onDraw()方法當中就是根據這個Point對象的坐标值來進行繪制的。也就是說,如果我們可以對Point對象進行動畫操作,那麼整個自定義View的動畫效果就有了。顯然,補間動畫是不具備這個功能的,這是它的第一個缺陷。
- 補間動畫機制就是使用寫死的方式來完成的,功能上無法擴充。比如想讓view的背景色變換,它是沒辦法做到的
- 補間動畫還有一個緻命的缺陷,就是它隻是改變了View的顯示效果而已,而不會真正去改變View的屬性。什麼意思呢?比如說,現在螢幕的左上角有一個按鈕,然後我們通過補間動畫将它移動到了螢幕的右下角,現在你可以去嘗試點選一下這個按鈕,點選事件是絕對不會觸發的,因為實際上這個按鈕還是停留在螢幕的左上角,隻不過補間動畫将這個按鈕繪制到了螢幕的右下角而已。
為了解決上述的問題,Android團隊推出了新的動畫機制:Property Animation,也就是屬性動畫。
二、ValueAnimator
在我們的動畫機制中,有兩個類是比較基本的,一個是ValueAnimator,一個是ObjectAnimator,前面一個是基類,後一個是子類,但使用更為友善,也很常用。
ValueAnimator是整個屬性動畫機制當中最核心的一個類,前面我們已經提到了,屬性動畫的運作機制是通過不斷地對值進行操作來實作的,而初始值和結束值之間的動畫過渡就是由ValueAnimator這個類來負責計算的。它的内部使用一種時間循環的機制來計算值與值之間的動畫過渡,我們隻需要将初始值和結束值提供給ValueAnimator,并且告訴它動畫所需運作的時長,那麼ValueAnimator就會自動幫我們完成從初始值平滑地過渡到結束值這樣的效果。除此之外,ValueAnimator還負責管理動畫的播放次數、播放模式、以及對動畫設定監聽器等,确實是一個非常重要的類。
注意,ValueAnimator操作的隻是值的變化,我們可以為看下面這個例子:
ValueAnimator anim = ValueAnimator.ofFloat(f, f);
anim.setDuration();
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float currentValue = (float) animation.getAnimatedValue();
Log.d("TAG", "cuurent value is " + currentValue);
}
});
anim.start();
這個裡面我們進行的操作主要是将數值0到1進行變化,變化的時間為300毫秒,而且設定了一個updateListener,來監聽并列印數值的變化。可以料到,程式運作的結果就是在300毫秒内,列印了一串0-1之間的數。
但是說的是屬性動作,隻模拟了數值的變化怎麼行。是的,ValueAnimator隻是将數值進行操作,本身并沒有涉及到屬性。在使用中需要配合屬性來進行。
它的好處在于:不必針對某個單一的屬性,你可以自己根據目前動畫的計算值,來操作任何屬性,隻要這個屬性有getter,setter,比如【希望一個動畫能夠讓View既可以放大、背景又能夠由淺變深(3個屬性scaleX,scaleY,alpha)】,就可以利用上述構造出來的anim,直接設定:
//利用單個ValueAnimator,對多個屬性進行操作
View.setScalX(anim);
View.setScalY(anim);
View.setAlpha(anim);
這裡就不得不扯一下ObjectAnimator的一個非典型用法:
public void rotateyAnimRun(final View view)
{
ObjectAnimator anim = ObjectAnimator//
.ofFloat(view, "suibian", F, F)//
.setDuration();//
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float cVal = (Float) animation.getAnimatedValue();
view.setAlpha(cVal);
view.setScaleX(cVal);
view.setScaleY(cVal);
}
});
}
這一串代碼用了ObjectAnimator,實作的功能和前面用ValueAnimator一模一樣,為啥呢?主要看這一句。
ObjectAnimator anim = ObjectAnimator//
.ofFloat(view, “suibian”, 0.0F, 1.0F)//
.setDuration(500);
解釋一樣,view就是我們要操作的對象,第二個參數是屬性,看到我們傳了一個”suibian ”進去,“suibian”并不是這個對象有的屬性,于是ObjectAnimator就退化為ValueAnimator對第三個第四個參數進行值操作,使用是就需要利用屬性的setter方法了。
既然說到了操作多個動畫,這裡再提另外一種方法:使用propertyValuesHolder,聽名字就可以猜到用法
public void propertyValuesHolder(View view)
{
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("alpha", ,
, );
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("scaleX", ,
, );
PropertyValuesHolder pvhZ = PropertyValuesHolder.ofFloat("scaleY", ,
, );
ObjectAnimator.ofPropertyValuesHolder(view, pvhX, pvhY,pvhZ).setDuration().start();
}
三、ObjectAnimator
談起ObjectAnimator,還是要對比ValueAnimator.ObjectAnimator就直覺的特點就是可以直接對任意對象的任意屬性進行動畫操作的,比如說View的alpha屬性。
但說到繼承關系,ObjectAnimator歸根結底調用的都是ValueAnimator,是以呢,其實ObjectAnimator能夠實作的用ValueAnimator照樣能夠實作。
由于參數比較直覺,我直接用一個例子來展示了:
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "scaleX", f, f,f);
animator.setDuration();
animator.start();
可以看出,我們對一個textView在X軸方向上進行縮放操作,與前面的不同,這裡我們傳了了三個參數,1.0f—>0.5f—>1.0f,對應的效果就是textView在x方向上先縮小到一半,再放大到原來大小。
四、組合動畫
到這裡,有讀者可能想問,如果有多個動畫,而且動畫的播放有一個先後順序怎麼辦。嗯,Android團隊肯定也想到這個問題了。
實作組合動畫功能主要需要借助AnimatorSet這個類,這個類提供了一個play()方法,如果我們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)将會傳回一個AnimatorSet.Builder的執行個體,AnimatorSet.Builder中包括以下四個方法:
after(Animator anim) 将現有動畫插入到傳入的動畫之後執行
after(long delay) 将現有動畫延遲指定毫秒後執行
before(Animator anim) 将現有動畫插入到傳入的動畫之前執行
with(Animator anim) 将現有動畫和傳入的動畫同時執行
直接上代碼:
public void playWithAfter(View view)
{
float cx = mBlueBall.getX();
ObjectAnimator anim1 = ObjectAnimator.ofFloat(mBlueBall, "scaleX",
f, f);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(mBlueBall, "scaleY",
f, f);
ObjectAnimator anim3 = ObjectAnimator.ofFloat(mBlueBall,
"x", cx , f);
ObjectAnimator anim4 = ObjectAnimator.ofFloat(mBlueBall,
"x", cx);
/**
* anim1,anim2,anim3同時執行
* anim4接着執行
*/
AnimatorSet animSet = new AnimatorSet();
animSet.play(anim1).with(anim2);
animSet.play(anim2).with(anim3);
animSet.play(anim4).after(anim3);
animSet.setDuration();
animSet.start();
}
}
那一串的playwith,playafter就是執行的先後。雖然是支援鍊式程式設計的,但是還是要謹慎的,不要擠在一起寫,一句就寫完,分開來寫比較好。
最後提一下監聽器,addListener裡面總共實作了四個接口,onAnimationStart()方法會在動畫開始的時候調用,onAnimationRepeat()方法會在動畫重複執行的時候調用,onAnimationEnd()方法會在動畫結束的時候調用,onAnimationCancel()方法會在動畫被取消的時候調用。
有人會覺得挺麻煩的,沒關系,Android提供了一個擴充卡類,叫作AnimatorListenerAdapter,使用這個類就可以解決掉實作接口繁瑣的問題了,可以實作自己需要的接口:
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
}
});
在xml中實作動畫的方式也是極其簡單的,我們可以在res目錄下面建立一個animator檔案夾,所有屬性動畫的XML檔案都存放在這個檔案夾當中。
XML檔案中主要有三種标簽:
<animator> 對應代碼中的ValueAnimator
<objectAnimator> 對應代碼中的ObjectAnimator
<set> 對應代碼中的AnimatorSet
而如果我們想将一個視圖的alpha屬性從1變成0,就可以這樣寫:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:propertyName="alpha"/>
然後怎麼在代碼中調起這段動畫呢,一句話搞定
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);
animator.setTarget(view);
animator.start();
loadAnimator進來,和使用layout很類似有木有,就是這麼簡單。
有興趣的可以去看看鴻洋用動畫實作的線:
http://blog.csdn.net/lmj623565791/article/details/38067475