安卓爲UI元素和本身繪製2D、3D圖形提供了一系列的API,這篇文章介紹了這些API的主要用法。javascript
安卓框架提供了兩種動畫系統:屬性動畫(property animation)和視圖動畫(view animation)。兩種動畫效果均可以使用,可是一般狀況下推薦使用屬性動畫(property animation),由於它更靈活而且有更多特性。除了這兩種外,你還可使用幀動畫(Drawable animation),這種動畫使用圖片資源逐幀展現。java
在安卓3.0(API11)引進,屬性動畫容許你對任何對象的屬性進行動畫處理,包括未在屏幕上展現的對象。屬性動畫能夠擴展,而且容許你自定義屬性。android
視圖動畫只能用於views。相對來講它能提供幾乎知足你需求的全部動畫。git
幀動畫能夠逐幀顯示圖片,就像電影同樣。這種動畫在你想要顯示相似於進度一類的動畫時是有用的。github
在編寫應用程序時,重要的是要考慮你的圖形需求是什麼。不一樣的圖形任務最好用不一樣的技術來完成。 例如,用於靜態應用的圖形和動畫應當與用於交互式遊戲的圖形和動畫不一樣地實現。 在這裏,咱們將討論在Android上繪製圖形的幾個選項,以及它們最適合的任務。編程
Android提供了一組View小部件,爲各類用戶界面提供了一些通用功能。 你還能夠擴展這些小部件以修改它們的外觀或行爲方式。 此外,莫可使用Canvas類中包含的各類繪製方法來繪製本身的自定義2D渲染,或者爲按鈕或逐幀動畫之類的內容建立Drawable對象。canvas
從安卓3.0引入,容許你是用硬件特性來更好的繪製你的畫布。api
Android經過Android框架API以及本地開發工具包(NDK)能夠支持支持OpenGL ES 1.0和2.0。當你想要爲您的應用程序添加Canvas API不支持的一些圖形加強功能時,或者您但願獨立於平臺而且不要求高性能時,可使用安卓框架API。與NDK相比,使用框架API有一個性能損失,所以對於許多圖形密集型應用程序,如遊戲,使用NDK更好(重要的是,你仍然可使用框架API得到足夠的性能。例如,Google Body應用程序徹底使用框架API開發)。若是你有不少本地代碼,你想移植到Android,使用NDK的OpenGL也是有用的。框架
屬性動畫是一個強大的框架,幾乎容許你爲任何東西作動畫。你能夠定義隨時間更改而改變對象屬性的動畫,而不用關心它是否是在屏幕上。屬性動畫在指定的時間內更改屬性值。要設置動畫,須要指定對象屬性,例如對象在屏幕上的位置,設置時間,設置中間值。函數
屬性動畫中你能夠指定下列特性:
首先讓咱們來看一個簡單的例子:
圖1描述了使用其X軸屬性的動畫對象,表示在屏幕上水平位置的移動。動畫的持續時間40ms,行進距離40像素,這樣每10ms水平移動10像素。圖示的是線性插值動畫,速度保持恆定。
你也能夠指定非線性插值方式。圖2表示的是在動畫開始時加速,動畫結束時減速的動畫過程。仍然是40ms移動40個像素,可是移動過程是非線性的。
圖3描述了動畫的主要的類的彼此之間的工做
ValueAnimator跟蹤當前動畫的時間,例如動畫運行了多少時間,以及單籤的屬性值。
ValueAnimator封裝了一個定義動畫插值器的TimeInterpolator和定義TypeEvaluator。例如在圖2中,使用的TimeInterpolator是AccelerateDecelerateInterpolator,使用的TypeEvalutor是IntEvaluator。
要啓動動畫,請建立ValueAnimator,併爲其設置動畫屬性的起始值和結束值,以及動畫的持續時間。 當你調用start()時,動畫開始。在整個動畫期間,ValueAnimator根據動畫的持續時間和已通過去的時間,計算0和1之間的已過比例。 已過比例表示動畫已完成的時間百分比,0表示0%,1表示100%。 例如,在圖1中,在t = 10ms時的通過比例將是0.25,由於總持續時間是t = 40ms。
當ValueAnimator計算完已經完成的函數後,它調用當前設置的TimeInterpolator來計算接下來的插值函數。 插值函數將通過的函數映射到考慮了設置的時間新插值函數。 例如,在圖2中,由於動畫緩慢加速,因此插值函數(大約.15)小於通過的函數(.25),在t = 10ms。 在圖1中,插值老是與通過的相同。
當計算插值函數時,ValueAnimator調用相應的TypeEvaluator,根據動畫的插值函數,起始值和結束值計算你正在進行動畫的屬性的值。 例如,在圖2中,插值函數在t = 10ms時爲.15,所以此時的x軸屬性值爲.15 X(40 - 0)或6。
視圖動畫提供只對view對象進行處理的功能,所以若是你對非view進行動畫處理,必須實現本身的代碼。視圖動畫系統也受到約束,由於它僅將View對象的幾個屬性暴露給動畫,例如視圖的縮放和旋轉,而沒有背景顏色。
補間動畫能夠對View對象的內容執行一系列簡單的變換(位置,大小,旋轉和透明度)。因此,若是你有一個TextView對象,你能夠移動,旋轉,增加或收縮文本。 若是它有一個背景圖像,背景圖像將隨文本一塊兒變換。 動畫包提供了補間動畫中使用的全部類。
視圖動畫系統的另外一個缺點是,它只是在繪製視圖時修改,而不是實際的View自己。例如,若是你使一個按鈕在屏幕上移動,按鈕移動繪製正確,可是你能夠點擊按鈕的實際位置不會改變,因此你必須實現本身的邏輯來處理這個。
使用屬性動畫,上邊提到的視圖動畫的缺點全都沒有,你能夠操做視圖和非視圖對象。屬性動畫在執行動畫方式上也很好,你能夠爲要製做動畫的屬性(如顏色,位置或者大小)分配動畫製做器,並能夠定義動畫的各個方面,例如多個動畫製做工具的插值和同步。
然而,使用視圖動畫會比屬性動畫有較少的設置和代碼編寫。若是視圖動畫能知足你如今有的程序,就不須要使用屬性動畫,也能夠結合二者使用。
在視圖動畫中已經定義了不少動畫插值器,你也能夠在屬性動畫中使用他們。Animator類爲建立動畫提供了一個基礎框架,正常狀況下你不不鞥使用這個類,它只提供了不多的功能,必需要經過繼承來擴展它,下面是它的子類:
類 | 描述 |
---|---|
ValueAnimator | 屬性動畫的主要計時引擎,計算要須要使用動畫的屬性的值。它具備計算動畫值幷包含每一個動畫的時間細節的全部的核心功能,關於動畫是否重複執行的信息,接收更新時間的監聽器以及設置自定義類型的能力。動畫屬性有兩個部分:計算動畫值,設置動畫值。ValueAnimator不執行第二部分,所以你必須本身監聽有ValueAnimator計算的值的更新,並修改邏輯對象。 |
ObjectAnimator | ValueAnimator的子類,容許您將目標對象和對象屬性設置爲動畫。 此類在爲動畫計算新值時相應地更新屬性。 一般狀況下使用ObjectAnimator,由於它使得對目標對象的動畫過程更容易。 可是,你有時須要直接使用ValueAnimator,由於ObjectAnimator還有其餘一些限制,例如須要在目標對象上存在特定的方法。 |
AnimatorSet | 將一組動畫有機的結合起來。你能夠設置爲同時播放,順序播放,或者在指定延時後執行動畫 |
Evaluators 告訴了屬性動畫如何根據給定的屬性計算值。獲取由Animator類提供的時間數據,動畫的開始和結束值,並基於這些數據計算屬性的值。如下是一些Evaluators:
類/接口 | 描述 |
---|---|
IntEvaluator | 默認的計算int型屬性的Evaluator |
FloatEvaluator | 默認的計算float型屬性的Evaluator |
ArgbEvaluator | 默認的計算顏色屬性(16進制)的Evaluator |
TypeEvaluator | 一個容許你建立本身的Evaluator的接口。若是要對不是int,float或color的對象屬性進行動畫處理,則必須實現TypeEvaluator接口以指定如何計算對象屬性的動畫值。若是要處理那些類型與默認行爲不一樣,也能夠爲int,float和color值指定自定義TypeEvaluator。 |
時間插值器定義動畫中的具體值如何對應時間的函數計算。例如,您能夠指定動畫在整個動畫中線性發生,這意味着動畫在整個時間內均勻移動,或者您能夠指定動畫以使用非線性時間,例如,在開始時加速,在結束時減速的動畫。表3描述了android.view.animation中包含的內插器。 若是所提供的插值器都不適合您的須要,請實現TimeInterpolator接口並建立本身的插件。
類/接口 | 描述 |
---|---|
AccelerateDecelerateInterpolator | 開始和結束時慢,中間加速的插值器 |
AccelerateInterpolator | 先緩慢後快速的加速器 |
AnticipateInterpolator | 開始正向前而後反向的插值器 |
AnticipateOvershootInterpolator | 先反向在正向超過目標值最後達到目標值 |
BounceInterpolator | 在結束時反彈的插值器 |
CycleInterpolator | 動畫重複指定次數的週期插值器 |
DecelerateInterpolator | 快速開始而後減速的插值器 |
LinearInterpolator | 速度恆定的線性插值器 |
OvershootInterpolator | 正向超過目標值而後返回目標值 |
TimeInterpolator | 接口,實現你本身的插值器 |
ValueAnimator類容許你經過指定一組int,float或顏色設置動畫的持續時間,從而爲某種類型的值設置動畫。 您能夠經過調用其工廠方法之一得到一個ValueAnimator:ofInt(),ofFloat()或ofObject()。 例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();複製代碼
在此代碼中,當start()方法運行時,ValueAnimator開始計算動畫的值(在0和1之間),持續時間爲1000 ms。
您還能夠經過執行如下操做,指定自定義動畫:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();複製代碼
在此代碼中,當start()方法運行時,ValueAnimator開始計算startPropertyValue和endPropertyValue之間的動畫值,使用由MyTypeEvaluator提供的邏輯,持續時間爲1000 ms。
然而,第一段代碼片斷對對象沒有實際影響,由於ValueAnimator不直接對對象或屬性操做。 你想作的最可能的事情是修改要用這些計算值的對象。 你能夠經過在ValueAnimator中定義監聽器以在動畫的生命週期內適當地處理重要事件(例如幀更新)來實現此目的。 當實現偵聽器時,您能夠經過調用getAnimatedValue()獲取特定幀刷新的計算值。
ObjectAnimator是ValueAnimator的子類,並結合了定時引擎和ValueAnimator的值計算,以及爲目標對象的命名屬性設置動畫的能力。 這使得對任何對象使用動畫更容易,由於你再也不須要實現ValueAnimator.AnimatorUpdateListener,對象的屬性自動更新。
實例化一個ObjectAnimator相似於一個ValueAnimator,但你也指定對象和該對象的屬性(做爲一個String)的名稱以及值之間的動畫:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();複製代碼
要使ObjectAnimator正確的更新屬性,必須執行如下操做:
對象的屬性必須有set
若是你在ObjectAnimator工廠方法中的value的參數僅指定一個值,那麼將會假定他是動畫的結束值。所以你的對象屬性必需要有一個getter方法,用於獲取動畫的起始值。gettter方法必須是get
ObjectAnimator.ofFloat(targetObject,「propName」,1f)複製代碼
根據你要動畫化的屬性或對象,你可能須要在視圖上調用invalidate()方法,以強制屏幕使用更新的動畫值重繪自己。你能夠在onAnimationUpdate()回調中作到這一點。例如,對一個Drawable對象的顏色屬性進行動畫處理只會在該對象重繪自己時更新屏幕。 View上的全部設置的屬性,例如setAlpha()和setTranslationX(),因此當使用新值調用這些方法時,您不須要使invalidate。
下面看幾種效果:
1.AccelerateDecelerateInterpolator
效果:動畫開始的時候慢速,慢慢加速,後邊減速
2.AccelerateInterpolator
效果:動畫慢慢加速,最後達到速度最快
3.AnticipateInterpolator
效果:先反向運動一段,而後正向運動
在多數狀況下,你想在一個動畫開始或者結束的時候啓動另外一個動畫,Android系統容許你將動畫捆綁在一塊兒成爲AnimatorSet,以即可以指定是同時、按順序啓動動畫仍是在指定的延遲後啓動動畫。 您還能夠將AnimatorSet對象嵌套在彼此以內。
下面的例子是從Bouncing Balls中提取的一段代碼,播放順序以下:
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();複製代碼
下面看兩個例子
1.按順序播放動畫
2.按順序播放動畫的同時播放透明度變化
Note:此處注意,在一個set中不能複用同一個動畫
動畫監聽器
你能夠在動畫執行期間監聽一些重要的事件。
根據你要動畫化的屬性或對象,你可能須要在控件上調用invalidate(),以強制屏幕的該區域使用新的動畫值重繪自己。 例如,對一個Drawable對象的顏色屬性進行動畫處理只會在該對象重繪自己時更新屏幕。 View上的全部屬性設置,例如setAlpha()和setTranslationX()使Viewinvalidate,因此當使用新值調用這些方法時,您不須要使調用invalidate。
若是不想實現Animator.AnimatorListener接口的全部方法,你能夠擴展AnimatorListenerAdapter類,AnimatorListenerAdapter類提供了能夠選擇覆蓋的方法的空實現。
例如,Bouncing Balls例子中建立了一個只有onAnimationEnd回掉方法的AnimatorListenerAdapter。
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
balls.remove(((ObjectAnimator)animation).getTarget());
}複製代碼
屬性動畫系統可以爲ViewGroup對象提供像對View對象同樣簡單的方法來製做動畫。
你可使用LayoutTransition類在一個ViewGroup種使用動畫來更改佈局。當你爲添加和刪除的動做添加動畫時,使用android.view.View.GONE,VISIBLE,INVISIBLE等或者調用set Visibility()方法設置可見不可見的時候,你能夠調用setAnimator()並傳入具備如下LayoutTransition常量的Animator對象,在LayoutTransiton種定義動畫:
API演示中的LayoutAnimations示例顯示瞭如何定義佈局轉換的動畫,而後在要設置動畫的View對象上設置動畫。
LayoutAnimationsByDefault及其相應的layout_animations_by_default.xml佈局資源文件顯示如何在XML中爲ViewGroups啓用默認佈局轉換。 你惟一須要作的是爲ViewGroup設置android:animateLayoutchanges屬性爲true。 例如:
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="@+id/verticalContainer"
android:animateLayoutChanges="true" />複製代碼
將此屬性設置爲true會自動爲從ViewGroup中添加或刪除的視圖以及ViewGroup中的其他視圖建立動畫。
若是要爲Android系統未知的類型設置動畫,能夠經過實現TypeEvaluator接口建立本身的evaluator 。 Android系統已知的類型是int,float或color,它們由IntEvaluator,FloatEvaluator和ArgbEvaluator類型evaluator 支持。
在TypeEvaluator接口中只有一種方法,即evaluate()方法。這容許你使用的animator在動畫的當前點爲動畫屬性返回適當的值。 FloatEvaluator類演示如何作到這一點:
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:當ValueAnimator(或ObjectAnimator)運行時,它計算動畫的當前已過比例(0到1之間的值),而後根據你使用的插值器器計算插值號。 插值函數是你的TypeEvaluator經過fraction參數接收的,因此你沒必要在計算動畫值時考慮插值器。
插值器定義動畫中的具體值與時間的函數關係。例如你能夠指定動畫在整個動畫期間內線性運動,你也可使用非線性運動,加速和減速運動。
動畫系統中的插值器從動畫製做器接收表示動畫的通過時間的函數。 插值器修依靠函數以與其目標提供的動畫類型一致。 Android系統在android.view.animation包中提供了一組常見的內插器。 若是這些都不符合你的須要,你能夠實現TimeInterpolator接口並建立本身的插值器。
做爲示例,下面比較默認內插器中的AccelerateDecelerateInterpolator和LinearInterpolator如何計算插值函數。 LinearInterpolator對已過的沒有影響。 AccelerateDecelerateInterpolator加速而後減速。
public float getInterpolation(float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}複製代碼
public float getInterpolation(float input) {
return input;
}複製代碼
下面的表中列出了插值器在持續1000ms的過程種對應的數值:
時間 | Linear函數值 | Accelerate/Decelerate函數值 | |
---|---|---|---|
0 | 0 | 0 | |
200 | .2 | .1 | |
400 | .4 | .345 | |
600 | .6 | .8 | |
800 | .8 | .9 | |
1000 | 1 | 1 |
上表中能夠看到,Linear是勻速運動,每200ms移動0.2。AccelerateDecelerateInterpolator是變速運動,200-600ms時加速很快,600-1000ms時變化慢。
關鍵幀對象由時間/值的鍵值對組成,容許您在動畫的特定時間定義特定狀態。每一個關鍵幀也能夠有本身的插值器,以控制動畫在前一個關鍵幀的時間和該關鍵幀的時間之間的間隔中的行爲。
要實例化一個Keyframe對象,必須使用ofInt(),ofFloat()或ofObject()的工廠方法之一來獲取相應類型的關鍵幀。而後調用ofKeyframe()工廠方法來獲取PropertyValuesHolder對象。 一旦你有了對象,你能夠經過傳入PropertyValuesHolder對象和對象來得到animator。 如下代碼段演示瞭如何執行此操做:
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);複製代碼
屬性動畫系統容許對View對象使用流線型動畫,而且提供了超過視圖動畫系統的一些優勢。 視圖動畫系統經過更改繪製對象的方式來轉換視圖對象。 這是在每一個View的容器中處理的,由於View自己沒有可操做的屬性。 這致使View被動畫化,可是View對象自己沒有改變。 這致使對象,即便它被繪製在屏幕上的不一樣位置,仍然存在於其原始位置。在Android 3.0中,添加了新的屬性和相應的getter和setter方法以消除此缺點。
屬性動畫系統能夠經過更改View對象中的實際屬性來在屏幕上更新View。 此外,View還會在其屬性更改時自動調用invalidate()方法來刷新屏幕。 的View類中的新屬性是:
爲了使View對象的屬性擁有動畫,例如他的顏色或者旋轉角度,你要作的就是建立一個property animator對象而且指定你想要動畫的屬性。
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);複製代碼
使用ViewPropertyAnimator進行動畫處理。ViewPropertyAnimator提供了一種簡單的方法來使用單個底層Animator對象來並行地對視圖的多個屬性進行動畫。 它的行爲很是像一個ObjectAnimator,由於它修改View的屬性的實際值,可是當同時對許多屬性更有效。 此外,使用ViewPropertyAnimator的代碼更簡潔,更容易閱讀。 如下代碼片斷顯示了在同時對視圖的x和y屬性進行動畫處理時,在使用多個ObjectAnimator對象,單個ObjectAnimator和ViewPropertyAnimator時的差別。
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();複製代碼
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();複製代碼
myView.animate().x(50f).y(100f);複製代碼
屬性動畫系統容許您使用XML聲明屬性動畫,而不是以編程方式。 經過在XML中定義動畫,您能夠輕鬆地在多個活動中重複使用動畫,並更輕鬆地編輯動畫序列。
要將使用新屬性動畫API的動畫文件與使用舊視圖動畫框架的動畫文件區分開來,從Android 3.1開始,您應該將屬性動畫的XML文件保存在res / animator /目錄中。
如下屬性動畫類具備如下XML標記的XML聲明支持:
下面的例子是順序播放兩個動畫,第一個動畫集合中的兩個動畫是同時播放的:
<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>複製代碼
爲了運行此動畫,您必須將代碼中的XML資源擴充爲AnimatorSet對象,而後在開始動畫集合以前爲全部動畫設置目標對象。 爲方便起見,調用setTarget()爲AnimatorSet的全部子項設置單個目標對象。 如下代碼顯示瞭如何執行此操做:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();複製代碼
最後附上github地址:
Demo