編輯:關於Android編程
屬性動畫(Property Animation)系統是一個更加強大的框架,它幾乎允許你為任何東西設置動畫。不管一個對象是否需要繪制到屏幕上面,你都可以定義一個動畫讓這個對象的屬性隨著時間推移而改變。一個屬性動畫可以在規定的時間內改變一個屬性值(對象的一個成員變量)。設定動畫,你需要指定對象中需要設定動畫的屬性,例如對象在屏幕上的坐標,動畫需要執行的時間,以及動畫過程中屬性的變化值。
屬性動畫系統允許你為一個動畫設定以下屬性:
時長:你可以指定動畫的時長。默認的動畫時長是300毫秒。 時間插值器:你可以設定一個根據當前動畫已經執行的時間計算出對應屬性值的方法(它被稱為時間插值器)。 重復次數和行為:你可以設定當動畫結束之後是否需要自動重復執行,以及可以重復執行多少次。你同樣也可以設定動畫是否需要反向回放,設定動畫來回反復反向回放,直到完成動畫設定的重復次數。 動畫組:你可以在一個動畫組裡面邏輯嵌套多個動畫,讓這些動畫同時播放,或者順序播放,或者延遲一定的時間後播放。 延遲幀刷新:你可以設定動畫幀與幀之間的刷新頻率。默認設定是每10毫秒刷新一次,但你的應用程序實際刷新的幀頻率最終還是要取決於整體系統的繁忙程度,以及系統底層能夠支持的定時器有多快。
首先,我們通過一個簡單的示例來了解下動畫的工作過程。圖1假設了一個對象需要對它的x屬性設定動畫,這個x屬性代表這個對象在屏幕上面的橫坐標。這個動畫的時間設定為40毫秒,以及需要移動的距離是40個像素。每過10毫秒(默認的幀頻率),這個對象就會在水平方向上移動10個像素。等到40毫秒之後,這個動畫停止,對象在水平方向上總共移動了40個像素。這個例子中的動畫使用了一個線性插值器,意味著這個對象以勻速移動。
圖1:線性動畫示例<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPsTj0rK/ydLUyei2qLavu63KudPD0ru49rfHz9/Q1LLl1rXG96Gjzbwy0d3KvsHL0ru49rzZyei1xLbUz/PU2ravu621xNK7v6rKvL340NC808vZo6y1yLavu62/7L3hyvi1xMqxuvK9+NDQvPXL2aGj1eK49rbUz/PSu9H51No0MLrBw+vW0NfcubLSxravNDC49s/xy9i146OstavSxravyse3x8/f0NS1xKGj1NrSu7+qyryjrLavu62808vZ0sa2r7W91tC85LXjo6zIu7rztNPW0LzktePSu9axvPXL2bW9tq+7rb3hyviho9X9yOfNvDLL+cq+o6zSu7+qyry6zb2rvfy94cr4tcTKsbry0sa2r7XEvuDA67HI1tC85Mqxts7SxravtcS+4MDr0qrQoaGjPC9wPg0KPHA+PGltZyBhbHQ9"animation-nonlinear.png" src="/uploadfile/Collfiles/20160621/20160621090904739.png" title="\" />
圖2:非線性動畫示例
讓我們來仔細看一下屬性動畫系統的重要組成部分是如何像上面的插圖一樣計算動畫的。圖3描述了屬性動畫系統中一些主要類之間的相互關系。
圖3:動畫如何計算
ValueAnimator對象負責跟蹤動畫的時序,比如這個動畫已經運行了多久,以及當前的動畫屬性值是多少。
ValueAnimator對象中包含了一個TimeInterpolator對象,用來定義動畫的插值器;另外還包含了一個TypeEvaluator對象,這個對象定義了如何根據動畫的執行程度計算出對應的屬性值。例如,在圖2中,TimeInterpolator使用的是AccelerateDecelerateInterpolator,TypeEvaluator使用的是IntEvaluator。
啟動一個動畫,需要創建一個ValueAnimator對象,並給它設定動畫開始和結束時的屬性值,以及動畫的執行時間。當你調用start()方法啟動動畫,在整個動畫執行期間,ValueAnimator會基於整個動畫需要執行的時間以及當前已經執行的動畫時間,計算出一個從0到1的值,表示已消耗的時間比例。這個已消耗的時間比例代表了動畫已完成的百分比,0表示0%,1就表示100%。例如,在圖1中,當t=10ms的時候,這個已消耗的時間比例就是0.25,因為整個動畫時長為40ms。
當ValueAnimator完成了一次已消耗的時間比例計算,它就會調用當前設定的TimeInterpolator去計算對應的(屬性)插值比例。插值比例根據所設定的時間插值器將已消耗的時間比例映射到一個新的比例值上。例如,在圖2中,由於動畫緩慢加速,在t=10ms的時候,它的插值比例大概是0.15,顯然比已消耗的時間比例0.25要小,因此在這個時間點上,屬性值將會是0.15 * (40-0),也就是6個像素點。
在API Demo的com.example.android.apis.animation 包中提供了一些關於如何使用屬性動畫系統的實例代碼。
視圖動畫(View Animation)系統只提供了給View對象設置動畫的功能,如果你要給非View對象設置動畫,你只能完全依靠自己來實現相關的邏輯代碼。實際上,視圖動畫還受限於只能對View對象的一部分屬性設置動畫,例如它可以縮放和旋轉一個View,但是無法改變view的背景顏色等等。
視圖動畫系統還有一個缺點就是它實際上只修改了View對象的繪制位置,而不是View的本身。舉個例子,使用視圖動畫你可以將一個按鈕從屏幕上移動過去,這個按鈕能夠正確的繪制到目標位置上,但這個按鈕的點擊位置實際上沒有隨著按鈕移動而移動,因此你需要自己實現對應的代碼響應邏輯來處理這個問題。
使用屬性動畫系統就完全沒有這樣的限制,你可以給一個對象(View對象或者非View對象)的任意屬性設定動畫,隨著動畫的執行,對象的屬性也會跟著一起改變。屬性動畫系統同樣提供了更強大的功能來實現動畫協作。在更高的等級上,你可以為多個屬性添加多個動畫(例如顏色、坐標、大小),然後定義動畫的各個方面(例如插值器和多個動畫同步運作)。
視圖動畫系統,相對的,配置起來需要花費的時間會少一點,需要的代碼量也會少一點。如果視圖動畫已經能夠滿足你所有的要求,或者你現有的代碼中已經使用了視圖動畫,那麼你也不是非得使用屬性動畫系統才可以。在某些特別的情況下,為不同的情景使用不同的動畫系統也是合情合理的。
你可以在android.animation 包中找到大部分屬性動畫系統的API。由於視圖動畫系統已經在android.view.animation 包中定義了很多插值器,你也可以在屬性動畫系統中直接使用這些插值器。下面的表格中列舉了屬性動畫系統的主要組件。
Animator 類中提供了創建動畫的基本結構。一般情況下你不需要直接使用這個類,繼承這個類只能夠提供滿足屬性動畫的最少方法。一般使用的是下面這些Animator的子類:
表1:Animator
計算器(Evaluator)用於告知屬性動畫系統如何計算屬性值。它們持有Animator類提供的時序數據,動畫的初始值和結束值。動畫執行過程中的屬性值都依賴於這些時序數據。屬性動畫系統提供了下列計算器:
表2:計算器
時間插值器中定義了一個動畫執行過程中針對指定值的時間函數。例如,你可以設定動畫在整個過程中線性變化,也就是在整個動畫過程中動畫會一直均勻的移動。同樣的,你也可以設定動畫使用非線性的時間過渡,例如,動畫在一開始的時候加速,在結尾的時候減速。表3中列舉了android.view.animation包中的插值器。如果沒有找到適合你的插值器,你可以實現TimeInterpolator接口自定義一個插值器。參考下面使用插值器章節獲取更多如何自動以插值器的內容。
表3:插值器
ValueAnimator類讓你通過設定一些類型值,比如一組int、float或者顏色值,在動畫執行期間進行動畫過渡。你可以調用ValueAnimator的任意一個工廠方法來獲取一個ValueAnimator實例:ofInt(),ofFloat(),或者ofObject()。例如:
ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f);
animation.setDuration(1000);
animation.start();
在這段代碼中,ValueAnimator調用start()方法後,開始在1000毫秒的時間裡面從0到1計算動畫過程中對應的數值。
你也可以使用下面的代碼指定一個自定義的動畫值類型:
ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();
在這段代碼中,ValueAnimator調用start()方法之後,開始使用MyTypeEvaluator提供的計算邏輯,在1000毫秒裡面從startPropertyValue到endPropertyValue計算動畫過程中對應的數值。
在上面的代碼片段中,其實,並不會對任何對象產生影響,因為ValueAnimator不負責直接操作對象或者屬性。你可能想要做的事是根據這些計算值修改需要設定動畫的對象。為了實現這個功能,你需要實現一個ValueAnimator中的listener,通過listener在動畫的生命周期中適當處理一些重要的事件,例如幀更新。當實現這些listener的時候,你可以通過getAnimatedValue() 方法獲取特定幀刷新時的動畫值。更多詳細內容,請參考下面的動畫監聽器章節。
ObjectAnimator是ValueAnimator的一個子類(前面章節已經提過了),它集成了時序引擎和ValueAnimator的計算值能力,能夠直接操作目標對象中指定的屬性。這就讓設定任意對象的動畫變得更加容易,因為你不再需要去實現ValueAnimator.AnimatorUpdateListener接口,動畫屬性會自己自動更新。
實例化一個ObjectAnimator對象跟ValueAnimator很類似,但除了設定動畫起始值之外,還需要指定一個對象,並以字符串的形式傳入這個對象中需要設定動畫的屬性名稱:
ObjectAnimator anim = ObjectAnimator.ofFloat(foo, "alpha", 0f, 1f);
anim.setDuration(1000);
anim.start();
為了讓ObjectAnimator能夠正確更新屬性,你必須按照下面步驟操作:
需要設定動畫的對象屬性必須按照set
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
取決於你設定動畫的屬性或者對象,你可能需要調用View的invalidate() 方法來強制屏幕重新繪制屬性值有更新的目標對象。你可以在onAnimationUpdate()回調方法中執行這些操作。例如,要執行一個Drawable對象的顏色屬性動畫,它只會在屏幕重新繪制這個對象後才能看到顏色動畫效果。View對象每一個屬性的setter方法,例如setAlpha()和setTranslationX()方法都會自動調用invalidate()來刷新View的屬性,因此,當調用這些方法給View設定新的屬性值時,不需要調用invalidate()方法。更多關於監聽器的細節,請參考下面動畫監聽器章節。
在一些情況下,你需要在一個動畫啟動或者結束的同時啟動另一個動畫。android系統允許你使用一個AnimatorSet捆綁多個動畫,這樣你就可以設定多個動畫是否同時執行,還是順序或者延遲一段時間後再執行。你甚至可以在AnimatorSet對象中嵌套另一個AnimatorSet對象。
下面的示例代碼取自Bouncing Balls示例項目(為修改方便起見),它按照下面的順序執行動畫:
1. 執行bounceAnim動畫。
2. 同時執行squashAnim1、squashAnim2、stretchAnim1和stretchAnim2四個動畫。
3. 執行bounceBackAnim動畫。
4. 執行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();
更多如何使用動畫套件的復雜示例,請查看Bouncing Balls示例項目。
你可以使用下面列舉的listener(監聽器)監聽動畫執行過程中的重要事件:
Animator.AnimatorListener如果你不想實現Animator.AnimatorListener接口中的每一個方法,那麼你可以選擇繼承AnimatorListenerAdapter類來取代直接實現Animator.AnimatorListener接口。AnimatorListenerAdapter類提供了Animator.AnimatorListener接口中每一個方法的空實現,你可以有選擇的重寫其中幾個。
例如,在Bouncing Balls示例項目中創建了一個僅僅重寫onAnimationEnd()回調方法的AnimatorListenerAdapter:
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());
}
屬性動畫系統提供了為ViewGroup對象設定布局動畫的功能,這些功能使用起來的效果就像直接為布局中的子View設定動畫一樣。
你可以使用LayoutTransition類來為ViewGroup內部對象的改變過程設定動畫。當你在一個ViewGroup中添加或移除一個子View,又或者是使用View.setVisibility()給子view設定VISIBLE、INVISIBLE和GONE值,這個子view變化的過程都可以添加顯示和消失動畫。當你添加或者移除子view的時候,剩余的子view一樣也可以使用動畫移動到新的位置上。你可以在LayoutTransition對象的setAnimator()方法中設定這些動畫,這個方法需要傳入一個Animator對象和下面列舉的一個LayoutTransition常量作為參數值:
APPEARING - 這個標識表明設定的動畫要在子view從視圖容器中顯示出來的過程中執行。 CHANGE_APPEARING - 這個標識表明設定的動畫要在視圖容器中原有的子View由於要顯示其它子View而引起自身布局變化時執行。 DISAPPEARING - 這個標識表明設定的動畫要在子View從視圖容器中消失的時候執行。 CHANGE_DISAPPEARING - 這個標識表明設定的動畫要在視圖容器中的子view由於其它子view的小時而引起自身布局變化的時候執行。你可以為你的布局過渡定義上面四種事件類型的自定義動畫,也可以讓動畫系統直接使用默認的動畫。
LayoutAnimations示例項目中介紹了如何為布局過渡定義動畫,以及如何把動畫設定到View對象上面。
LayoutAnimationsByDefault示例項目和它的layout_animations_by_default.xml布局文件介紹了如何在xml文件中為ViewGroup開啟默認的布局動畫。你唯一需要做的事情就是講ViewGroup的android:animationLayoutChanges屬性設置為true。例如:
將這個屬性設置為true之後,ViewGroup中的添加或者移除子View都會有動畫效果,同時仍然保留在ViewGroup中的View在這個過程中也會有動畫過度效果。
如果你要給一個android系統未支持的類型添加動畫,你可以通過實現TypeEvaluator接口來創建一個自定義計算器。android系統已支持的類型包括int、float、顏色值,它們分別通過IntEvaluator、FloatEvaluator和ArgbEvaluator計算器來實現動畫支持。
TypeEvaluator接口中只包含了一個方法evaluate()。這個方法允許你根據動畫當前的執行進度點返回一個合適的屬性值。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);
}
}
注意:當ValueAnimator(或者ObjectAnimator)運行的過程中,它會計算當前動畫已消耗的時間比例(一個0到1之間的值),然後再根據你使用的插值器計算出一個插值比例。這個插值比例就是就是你在TypeEvaluator.evaluate()方法中接收到的fraction參數,因此你不需要在計算動畫屬性值的時候關心如何計算插值。
插值器定義了動畫在運行過程中屬性值的時間函數。例如,你可以讓動畫在整個執行過程中線性過度,這意味著動畫在整段時間裡面都做勻速運動。你也可以設定動畫非線性過度,例如,在動畫開始的時候加速,在動畫結束的時候減速。
在動畫系統中,插值器會從Animator接收到一個代表從動畫開始到目前已花費的時間比例值。插值器會整合動畫的類型去修改這個比例值。android在android.view.animation包中提供了一系列通用的插值器。如果你在裡面沒有找到你想要的插值器,你可以實現TimeInterpolator接口來自定義一個插值器。
作為例子,下面比較了默認的AccelerateDecelerateInterpolator和LinearInterpolator插值器是如何計算插值比例的。LinearInterpolator插值器沒有對已消耗的時間比例進行修改。AccelerateDecelerateInterpolator插值器在開始的時候調整加大已消耗的時間比例,在結束的時候降低已消耗的時間比例。下面的代碼列舉了這些插值器的邏輯:
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;
}
下面的表格列舉了一個耗時1000毫秒的動畫在執行過程中使用這些插值器計算出來的大概值:
正如表格所描述的,LinearInterpolator在每個時間段裡面修改的插值都是一樣,即每過200毫秒增加0.2。AccelerateDecelerateInterpolator從200毫秒到600毫秒之間修改的插值比LinearInterpolator快,在600毫秒到1000毫秒之間又比LinearInterpolator慢。
Keyframe對象中包含了一對時間與屬性值的鍵值對,它定義了一個動畫在指定的時間點上要呈現出特定的狀態。每一個Keyframe都可以設定自己的插值器,用來控制動畫從上一個Keyframe到當前這個Keyframe這段時間間隔裡面的行為。
獲取一個Keyframe實例對象,你必須選擇一個Keyframe的ofInt()、ofFloat(),或者ofObject()工廠方法來構造一個合適的Keyframe對象。然後,再調用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);
更多關於使用關鍵幀的復雜示例,請查看MultiPropertyAnimation示例項目。
屬性動畫系統允許為View對象設定流線型的動畫,並且提供了一些比視圖動畫系統更加高級的功能。視圖動畫系統是通過修改View的繪制位置來改變View對象。這些操作是在每個View的容器中進行處理的,因為View本身並沒有可以執行這類操作的屬性。這就會導致View已經開始動畫了,但是View對象本身並沒有任何變化。整個過程看起來就像是View對象已經被繪制到屏幕上面的另一個位置,但它實際上還是在原來的位置上。從android 3.0開始,View類添加了新的屬性以及對應的getter和setter方法,以解決這個缺陷。
屬性動畫系統可以通過直接修改View對象的實際屬性來完成它在屏幕上的動畫效果。另外,當View的屬性有變化的時候,它也會自動調用invalidate()方法刷新屏幕。View類中支持屬性動畫的新屬性如下:
translationX和translationY:這兩個屬性以布局容器給View的left和top坐標值為基准,控制View在橫豎方向上需要移動位置的增量值。 rotation、rotationX和rotationY:這三個屬性以View的中心點為基准,控制view在2D視覺(rotation屬性)和3D視覺上的旋轉。 scaleX和scaleY:這兩個屬性以View中心點為基准,控制View在2D視覺上的大小縮放。 pivotX和pivotY:這兩個屬性控制View對象的中心點,當View進行旋轉或者縮放的時候需要依賴這個中心點的位置。默認情況下,中心點的位置在View對象的中心。 x和y:這兩個屬性描述了View在它所在的視圖容器中的最終位置,可以看做是left或者top和translationX或者translationY值的和。 alpha:代表View的透明度。默認值為1,表示不透明,當值為0時即為完全透明(不顯示)。給View對象的屬性設定動畫,例如它的顏色或者旋轉值,你需要做的只是創建一個屬性的Animator,然後指定需要設定動畫的屬性。例如:
ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
更多關於創建動畫的細節,請參考上面使用ValueAnimator設定動畫和使用ObjectAnimator設定動畫章節。
ViewPropertyAnimator底層封裝了一個Animator對象,提供了一種簡單的途徑來給View的多個屬性值同時設置動畫。它的操作與ObjectAnimator很類似,因為它也是直接修改view的屬性值,不同的是它能夠一次操作多個屬性值。此外,使用ViewPropertyAnimator的代碼會更加簡潔和易讀。下面的代碼片段比較了同時給view的x和y屬性設定動畫時,使用多個ObjectAnimator對象、使用一個ObjectAnimator對象,以及使用ViewPropertyAnimator對象之間的區別。
多個ObjectAnimator對象
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();
一個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);
更多關於ViewPropertyAnimator的信息,請參考Android Developers的博客.
屬性動畫系統允許你不直接寫代碼而使用xml來聲明定義屬性動畫。通過在xml文件中定義動畫,可以讓動畫更容易在多個activity中重復使用,也能讓編輯動畫序列更加容易。
為了區分使用了新屬性動畫API的動畫文件和舊視圖動畫框架的動畫文件,從android 3.1開始,你必須將使用屬性動畫的xml文件保存在res/animator/文件夾下。
下面列舉了屬性動畫類對應的xml標簽:
ValueAnimator - ObjectAnimator -為了執行這個動畫,你需要在你的代碼中把xml動畫資源實例化為AnimatorSet對象,然後在開始動畫之前設定動畫的目標對象。可以簡單的調用setTarget()方法給一個目標對象設定AnimatorSet中的全部子動畫。代碼如下:
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
R.anim.property_animator);
set.setTarget(myObject);
set.start();
更多關於定義屬性動畫的xml語法,請參考Animation Resources。
前言因為工作需要可能要用到JNI開發,本篇文章就分享一下我在這方面的實踐,以前我們使用Eclipse做NDK開發,非常麻煩,需要配cygwin的編譯環境,後面NDK功能完
項目使用的技術呢,說白了,就是將要傳遞的event(一個Object對象,可任意自定義),發送到公共的組件EventBus中進行存儲, 在通過
中國科學院開源協會鏡像站地址:IPV4/IPV6: http://mirrors.opencas.cn 端口:80IPV4/IPV6: http://mirrors.op
Activity棧主要用於管理Activity的切換。當使用Intent跳轉至某個目標Activity,需要根據目標Activity的加載模式來加載。Activity一共