今天的文章裡,我將會和大家討論對動畫流的控制。我們可以通過Animator系列的API來控制動畫的開始、停止和取消。在 KitKat也就是API level 19中,我們還可以控制動畫的暫停和恢復。在本文中,我將會帶你體驗整個動畫流的控制,並且通過一些函式方法來讓你能夠觀察到動畫的狀態。
動畫流介紹
在之前的教程中,我們已經使用過多次Animator.start這個方法。這個方法是用來讓動畫從第一幀開始播放。該方法只是動畫流控制方法集中的一個方法而已,完整的方法集合如下所示:
1 2 3 4 5 6 |
; html-script: false ] Animator.start() // start the animation from the beginning Animator.end() // end the animation Animator.cancel() // cancel the animation Animator.pause() // added in API 19; pause the animation Animator.resume() // added in API 19; resume a paused animation |
start這個方法顧名思義是用來讓動畫從開頭開始播放的。如果動畫設定了一個大於0的播放延遲(startDelay),那麼呼叫該方法後還需要等到延遲的時間過去才回開始播放。
我們有兩種停止動畫的方法,你可以用end方法抑或cancel方法來停止一個播放著的動畫。在兩種方式中動畫都會終止並且只有再次呼叫start方法才會重新開始播放。兩者的區別則在於停止後動畫所在的狀態,當你使用cancel方法來停止動畫後,動畫只是停止了它的時間軸,動畫的狀態會停在一箇中間態(intermediate state)。如果通過end 方法來停止一個動畫,那麼動畫會直接快進到該動畫最後一幀並且停止,所有的物件都會保持在動畫最終結束後的狀態。
在 Kitkat 中增加的沒有怎麼被大家關注到的新API則是帶來了動畫可以暫停和恢復的能力。在那之前,一個動畫如果被取消並且停留在當前的中間態,此時你用start方法去重啟動畫,動畫只會從一開始重新播放。現在,你則可以呼叫pause方法來暫停當前播放中的動畫,pause也會有和cancel方法一樣的功效讓動畫停留在中間態,但是當你使用resume 方法去恢復這個動畫的時候,動畫會從這個狀態繼續播放下去。
現在,讓我們來實踐下看看效果。我們新建一個 Activity 並且包含一個 私有的動畫物件。並且在 onCreate 回撥中初始化這個動畫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
; html-script: false ] private ObjectAnimator anim; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.property_animations_flow); ImageView someImage = (ImageView) findViewById(R.id.some_image); anim = ObjectAnimator.ofFloat(someImage, "rotation", 0, 360); anim.setDuration(1000); anim.setRepeatCount(5); anim.setRepeatMode(ObjectAnimator.RESTART); } |
我們的動畫是一個簡單的旋轉動畫,它將把一個圖片完整旋轉360度五次。被旋轉的圖片會在 layout 的 XML 檔案中定義好,並且給予一個叫some_image的id。這個layout同時也包含了五個按鈕分別是:Start、End、Cancel、Pause以及Resume。這五個按鈕分別代表了動畫的五個呼叫方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
; html-script: false ] public void startAnimation(View view) { anim.start(); } public void endAnimation(View view) { anim.end(); } public void cancelAnimation(View view) { anim.cancel(); } public void pauseAnimation(View view) { anim.pause(); } public void resumeAnimation(View view) { anim.resume(); } |
這些方法只是簡單的呼叫了相應的動畫控制呼叫。下面會有兩張gif圖片來演示效果。上邊的動畫展示了end方法和cancel方法的區別。可以注意到,cancel讓圖片保持在停止時的中間態,但是end則讓動畫到了最後的狀態。
下邊的動畫則演示了pause和resume方法。可以注意到,pause 和cancel都暫停了動畫的時間軸。但是現在我們可以用resume 呼叫去恢復動畫的時間軸了。
動畫狀態的查詢
有些時候,我們需要去查詢當前動畫的狀態,這個需求可以通過下面這些方法來完成。
1 2 3 4 |
; html-script: false ] boolean isStarted() // added in API 14 boolean isRunning() boolean isPaused() // added in API 19 |
如果當前動畫已經呼叫了start函式並且還沒播放完成也沒有被取消掉,那麼isStarted方法會返回true。請注意,isStarted 方法最低的 API 需求是 14.同時,就算是在動畫播放延遲中,該方法依然會返回 true。這就是這個方法和 isRunning 方法的不同點,isRunning 方法只會在動畫確實在播放並且還沒停止的時候返回 true。
在 API 19 的時候,isPaused 方法被加入進來。這是由於那時候動畫可以被暫停和恢復了,如果 isPaused 返回了 true,那麼說明當前動畫是在暫停狀態下,反之亦然。
為了演示這些觀察方法的效果,我們會通過一個包含三個顯示動畫狀態文字框的例子來解釋。這三個文字框都會在Activity中做為TextView成員變數存在。
1 2 3 4 |
; html-script: false ] private TextView isStartedText; private TextView isRunningText; private TextView isPausedText; |
於此同時,我們在onCreate中新增下列三行程式碼來把這些文字框從layout中獲取到。
1 2 3 4 |
; html-script: false ] isStartedText = (TextView) findViewById(R.id.status_is_started); isRunningText = (TextView) findViewById(R.id.status_is_running); isPausedText = (TextView) findViewById(R.id.status_is_paused); |
我們還建立了一個根據當前動畫狀態來更新這些文字框的方法。
1 2 3 4 5 6 |
; html-script: false ] public void setStatusTexts() { isStartedText.setText("isStarted = " + anim.isStarted()); isRunningText.setText("isRunning = " + anim.isRunning()); isPausedText.setText("isPaused = " + anim.isPaused()); } |
我們在初始化以後每次修改動畫流的時候都去呼叫setStatusTexts方法,比如說,當我沒呼叫cancelAnimation的時候,程式碼是這樣的:
1 2 3 4 5 |
; html-script: false ] public void cancelAnimation(View view) { anim.cancel(); setStatusTexts(); } |
演示的結果我們可以從下面上方的動畫中看到。這裡我也建立了兩個動畫,一個演示End和cancel效果,另一個演示pause和 Resume效果。可以注意到,動畫的執行狀態在你點選了End和cancel之後是相同的,就算視覺上看兩者並不一樣。這說明了,你沒法通過動畫的狀態來區分出當前動畫是通過 End 呼叫還是cancel呼叫來停止的。這兩者情況下,isStarted和isRunning都會返回false。
而下邊的動畫則顯示了pause和resume呼叫的效果。當我們通過pause呼叫去暫停動畫時,isPaused會返回true。然後通過 resume呼叫去恢復動畫播放後,isPaused也會變成false。可以注意到,圖中如果動畫是自然結束的,動畫的狀態並沒有改變。當然,動畫停止播放後,isStarted和isRunning肯定應該是返回false的。實際上,在動畫自然停止後,如果我們再去呼叫 isStarted和isRunning他們的的確確會返回false。而動畫中的例子我們並沒有在動畫自然停止後去更新狀態,所以我們並不知道當前的狀態,自然那幾個文字框也就沒有更新。為了能夠更新到最新的狀態,我們可以在動畫中新增AnimatorListener和 AnimatorPauseListener這兩個監聽,這兩個監聽具體的使用方法會是下一講的主要內容。
如果你對教程中的例子有興趣,歡迎去 GitHub 上下載完整程式碼。