Android 中動畫

Android動畫中通常分爲3種:android

1.Tween Animation:

補間動畫(tweened animation)經過對場景裏的對象不斷作圖像變換(平移、縮放、旋轉)產生動畫效果,便是一種漸變更畫;app

缺點 :補間動畫是隻可以做用在View上;它只可以實現移動縮放旋轉淡入淡出這四種動畫操做;它只是改變了View的顯示效果而已,而不會真正去改變View的屬性。它並不能改變事件響應的位置,它只是單純地修改了顯示。好比說實現一個按鈕的移動,那麼按鈕的實際點擊有效區依然在原來的地方,點擊移動後的地方是不會有點擊事件發生的。ide

Tween Animation有四種形式:函數

  alpha            漸變透明度動畫效果佈局

  scale             漸變尺寸伸縮動畫效果動畫

  translate             畫面位置移動動畫效果ui

  rotate              畫面旋轉動畫效果this

 這四種動畫實現方式都是經過Animation類和AnimationUtils配合實現。lua

 

2.Frame Animation:

順序播放事先作好的圖像,是一種畫面轉換動畫。幀動畫(frame-by-frame animation)的工做原理很簡單,其實就是將一個完整的動畫拆分紅一張張單獨的圖片,而後再將它們連貫起來進行播放,相似於動畫片的工做原理。spa

3.Property Animation:

屬性動畫,經過動態地改變對象的屬性從而達到動畫效果,屬性動畫爲API 11新特

新引入的屬性動畫機制已經再也不是針對於View來設計的了,也不限定於只能實現移動、縮放、旋轉和淡入淡出這幾種動畫操做,同時也再也不只是一種視覺上的動畫效果了。它其實是一種不斷地對值進行操做的機制,並將值賦值到指定對象的指定屬性上,能夠是任意對象的任意屬性

 

ValueAnimator

 

ValueAnimator是整個屬性動畫機制當中最核心的一個類,ObjectAnimator也是繼承自ValueAnimator。前面咱們已經提到了,屬性動畫的運行機制是經過不斷地對值進行操做來實現的,而初始值和結束值之間的動畫過渡就是由ValueAnimator這個類來負責計算的。它的內部使用一種時間循環的機制來計算值與值之間的動畫過渡,咱們只須要將初始值和結束值提供給ValueAnimator,而且告訴它動畫所需運行的時長,那麼ValueAnimator就會自動幫咱們完成從初始值平滑地過渡到結束值這樣的效果。除此以外,ValueAnimator還負責管理動畫的播放次數、播放模式、以及對動畫設置監聽器等,確實是一個很是重要的類。

ValueAnimator當中最經常使用的應該就是ofFloat()和ofInt()這兩個方法了,另外還有一個ofObject()方法。

那麼除此以外,咱們還能夠調用setStartDelay()方法來設置動畫延遲播放的時間,調用setRepeatCount()和setRepeatMode()方法來設置動畫循環播放的次數以及循環播放的模式,循環模式包括RESTART和REVERSE兩種,分別表示從新播放和倒序播放的意思。

/*

*/

  1. ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);    
  2. anim.setDuration(300);  
  3. anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    
  4.     @Override    
  5.     public void onAnimationUpdate(ValueAnimator animation) {    
  6.         float currentValue = (float) animation.getAnimatedValue();    
  7.         Log.d("TAG", "cuurent value is " + currentValue);    
  8.     }    
  9. });    
  10. anim.start();  

 

這裏咱們經過addUpdateListener()方法來添加一個動畫的監聽器,在動畫執行的過程當中會不斷地進行回調,這裏用來監聽數值的變化。

前面咱們使用過了ValueAnimator的ofFloat()和ofInt()方法,分別用於對浮點型和整型的數據進行動畫操做的,但實際上ValueAnimator中還有一個ofObject()方法,是用於對任意對象進行動畫操做的。可是相比於浮點型或整型數據,對象的動畫操做明顯要更復雜一些,由於系統將徹底沒法知道如何從初始對象過分到結束對象,所以這個時候咱們就須要實現一個本身的TypeEvaluator來告知系統如何進行過分。

下面來先定義一個Point類,以下所示:

 

  1. public class Point {    
  2.     
  3.     private float x;    
  4.     
  5.     private float y;    
  6.     
  7.     public Point(float x, float y) {    
  8.         this.x = x;    
  9.         this.y = y;    
  10.     }    
  11.     
  12.     public float getX() {    
  13.         return x;    
  14.     }    
  15.     
  16.     public float getY() {    
  17.         return y;    
  18.     }    
  19.     
  20. }  

Point類很是簡單,只有x和y兩個變量用於記錄座標的位置,並提供了構造方法來設置座標,以及get方法來獲取座標。接下來定義PointEvaluator,以下所示:

  1. public class PointEvaluator implements TypeEvaluator{    
  2.     
  3.     @Override    
  4.     public Object evaluate(float fraction, Object startValue, Object endValue) {    
  5.         Point startPoint = (Point) startValue;    
  6.         Point endPoint = (Point) endValue;    
  7.         float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());    
  8.         float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());    
  9.         Point point = new Point(x, y);    
  10.         return point;    
  11.     }      
  12. }   

能夠看到,PointEvaluator一樣實現了TypeEvaluator接口並重寫了evaluate()方法。其實evaluate()方法中的邏輯仍是很是簡單的,先是將startValue和endValue強轉成Point對象,而後一樣根據fraction來計算當前動畫的x和y的值,最後組裝到一個新的Point對象當中並返回。

這樣咱們就將PointEvaluator編寫完成了,接下來咱們就能夠很是輕鬆地對Point對象進行動畫操做了,好比說咱們有兩個Point對象,如今須要將Point1經過動畫平滑過分到Point2,就能夠這樣寫:

  1. Point point1 = new Point(0, 0);    
  2. Point point2 = new Point(300, 300);    
  3. ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), point1, point2);    
  4. anim.setDuration(5000);    
  5. anim.start();  

代碼很簡單,這裏咱們先是new出了兩個Point對象,並在構造函數中分別設置了它們的座標點。而後調用ValueAnimator的ofObject()方法來構建ValueAnimator的實例,這裏須要注意的是,ofObject()方法要求多傳入一個TypeEvaluator參數,這裏咱們只須要傳入剛纔定義好的PointEvaluator的實例就能夠了。

Object Animator

相比於ValueAnimator,ObjectAnimator可能纔是咱們最常接觸到的類,由於ValueAnimator只不過是對進行了一個平滑的動畫過渡,但咱們實際使用到這種功能的場景好像並很少。而ObjectAnimator則就不一樣了,它是能夠直接對任意對象的任意屬性進行動畫操做的,好比說View的alpha屬性。

因爲ObjectAnimator繼承自ValueAnimator,說明ValueAnimator中可使用的方法在ObjectAnimator中也是能夠正常使用的,它們的用法也很是相似。

 

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);    
  2. animator.setDuration(5000);    
  3. animator.start();  

第一個參數要求傳入一個object對象,咱們想要對哪一個對象進行動畫操做就傳入什麼,這裏我傳入了一個textview。第二個參數是想要對該對象的哪一個屬性進行動畫操做,因爲咱們想要改變TextView的不透明度,所以這裏傳入"alpha"。後面的參數就是不固定長度了,想要完成什麼樣的動畫就傳入什麼值,這裏傳入的值就表示將TextView從常規變換成全透明,再從全透明變換成常規。以後調用setDuration()方法來設置動畫的時長,而後調用start()方法啓動動畫。

ofFloat()方法的第二個參數到底能夠傳哪些值呢?其實這裏ObjectAnimator內部的工做機制並非直接對咱們傳入的屬性名進行操做的,而是會去尋找這個屬性名對應的get和set方法,所以這裏傳入的名字其實無所謂的,只是一個指示而已。

不過,在使用ObjectAnimator的時候,有一點很是的重要,就是要操做的屬性必須具備get、set方法,否則ObjectAnimator就沒法起效。

經常使用的能夠直接使用屬性動畫的屬性包括:
(1)translationX和translationY:控制view從它佈局容器左上角座標偏移的位置;
(2)rotation、rotationX和rotationY:控制view圍繞支點進行2D和3D旋轉;
(3)scaleX和scaleY:控制view圍繞着它的支點進行2D縮放;
(4)pivotX和pivotY:控制支點位置,圍繞這個支點進行旋轉和縮放處理。默認狀況下,支點是view的中心點;
(5)x和y:控制view在它的容器中的最終位置,它是最初的左上角座標和translationX、translationY的累計和;
(6)alpha:控制透明度,默認是1(不透明)。

若是一個屬性不具備get和set方法,有兩種解決的方法:一種是經過自定義一個屬性類或者包裝類,來間接地給這個屬性增長get和set方法;第二種就是經過ValueAnimator來實現。

使用包裝類的方法給一個屬性增長set和get的方法,代碼以下:

  1. private static class WrapperView{  
  2.     private View mTarget;  
  3.     public WrapperView(View target){  
  4.         mTarget = target;  
  5.     }  
  6.     public int getWidth(){  
  7.         return mTarget.getLayoutParams().width;  
  8.     }  
  9.   
  10.     public void setWidth(int width){  
  11.         mTarget.getLayoutParams().width = width;  
  12.         mTarget.requestFocus();  
  13.     }  
  14.   
  15. }  

使用的時候只須要操做包裝類就能夠間接地調用get,set方法了

 

  1. WrapperView wrapper = new WrapperView (mButton);  
  2. ObjectAnimator.ofInt(Wrapper,"width",500).setDuration(5000).start;  

ObjectAnimator中的ofObject()方法一樣是對任意對象進行動畫操做。

ObjectAnimator內部的工做機制是經過尋找特定屬性的get和set方法,而後經過方法不斷地對值進行改變,從而實現動畫效果的。所以咱們就須要在MyAnimView中定義一個color屬性,並提供它的get和set方法。那麼接下來的問題就是怎樣讓setColor()方法獲得調用了,毫無疑問,固然是要藉助ObjectAnimator類,可是在使用ObjectAnimator以前咱們還要完成一個很是重要的工做,就是編寫一個用於告知系統如何進行顏色過分的TypeEvaluator。(具體使用見Android屬性動畫徹底解析(中),ValueAnimator和ObjectAnimator的高級用法

組合動畫AnimatorSet

獨立的動畫可以實現的視覺效果畢竟是至關有限的,所以將多個動畫組合到一塊兒播放就顯得尤其重要。幸運的是,Android團隊在設計屬性動畫的時候也充分考慮到了組合動畫的功能,所以提供了一套很是豐富的API來讓咱們將多個動畫組合到一塊兒。

實現組合動畫功能主要須要藉助AnimatorSet這個類,這個類提供了一個play()方法,若是咱們向這個方法中傳入一個Animator對象(ValueAnimator或ObjectAnimator)將會返回一個AnimatorSet.Builder的實例,AnimatorSet.Builder中包括如下四個方法:

· after(Animator anim)   將現有動畫插入到傳入的動畫以後執行

· after(long delay)   將現有動畫延遲指定毫秒後執行

· before(Animator anim)   將現有動畫插入到傳入的動畫以前執行

· with(Animator anim)   將現有動畫和傳入的動畫同時執行

 

  1. ObjectAnimator moveIn = ObjectAnimator.ofFloat(textview, "translationX", -500f, 0f);    
  2. ObjectAnimator rotate = ObjectAnimator.ofFloat(textview, "rotation", 0f, 360f);    
  3. ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f);    
  4. AnimatorSet animSet = new AnimatorSet();    
  5. animSet.play(rotate).with(fadeInOut).after(moveIn);    
  6. animSet.setDuration(5000);    
  7. animSet.start();  

在屬性動畫中,AnimatorSet正是經過playTogether()、playSequentially()、animSet.play()、with()、before()、after()這些方法來控制多個動畫的協同工做方式,從而作到對動畫播放方式的精確控制。

Animator監聽器

在不少時候,咱們但願能夠監聽到動畫的各類事件,好比動畫什麼時候開始,什麼時候結束,而後在開始或者結束的時候去執行一些邏輯處理。這個功能是徹底能夠實現的,Animator類當中提供了一個addListener()方法,這個方法接收一個AnimatorListener,咱們只須要去實現這個AnimatorListener就能夠監聽動畫的各類事件了。

你們已經知道,ObjectAnimator是繼承自ValueAnimator的,而ValueAnimator又是繼承自Animator的,所以無論是ValueAnimator仍是ObjectAnimator都是可使用addListener()這個方法的。另外AnimatorSet也是繼承自Animator的,所以addListener()這個方法算是個通用的方法。

添加一個監聽器的代碼以下所示:

  1. anim.addListener(new AnimatorListener() {    
  2.     @Override    
  3.     public void onAnimationStart(Animator animation) {    
  4.     }    
  5.     
  6.     @Override    
  7.     public void onAnimationRepeat(Animator animation) {    
  8.     }    
  9.     
  10.     @Override    
  11.     public void onAnimationEnd(Animator animation) {    
  12.     }    
  13.     
  14.     @Override    
  15.     public void onAnimationCancel(Animator animation) {    
  16.     }    
  17. });  

 

能夠看到,咱們須要實現接口中的四個方法,onAnimationStart()方法會在動畫開始的時候調用,onAnimationRepeat()方法會在動畫重複執行的時候調用,onAnimationEnd()方法會在動畫結束的時候調用,onAnimationCancel()方法會在動畫被取消的時候調用。

可是也許不少時候咱們並不想要監聽那麼多個事件,可能我只想要監聽動畫結束這一個事件,那麼每次都要將四個接口所有實現一遍就顯得很是繁瑣。不要緊,爲此Android提供了一個適配器類,叫做AnimatorListenerAdapter,使用這個類就能夠解決掉實現接口繁瑣的問題了,以下所示:

 

  1. <span style="font-family:SimSun;font-size:12px;">1.anim.addListener(new AnimatorListenerAdapter() {    
  2. });</span>  

這裏咱們向addListener()方法中傳入這個適配器對象,因爲AnimatorListenerAdapter中已經將每一個接口都實現好了,因此這裏不用實現任何一個方法也不會報錯。那麼若是我想監聽動畫結束這個事件,就只須要單獨重寫這一個方法就能夠了,以下所示:

 

  1. anim.addListener(new AnimatorListenerAdapter() {    
  2.     @Override    
  3.     public void onAnimationEnd(Animator animation) {    
  4.     }    
  5. });   

TypeEvaluator

可能在大多數狀況下咱們使用屬性動畫的時候都不會用到TypeEvaluator,可是你們仍是應該瞭解一下它的用法,以防止當咱們遇到一些解決不掉的問題時可以想起來還有這樣的一種解決方案。

那麼TypeEvaluator的做用究竟是什麼呢?簡單來講,就是告訴動畫系統如何從初始值過分到結束值。咱們在上一篇文章中學到的ValueAnimator.ofFloat()方法就是實現了初始值與結束值之間的平滑過分,那麼這個平滑過分是怎麼作到的呢?其實就是系統內置了一個FloatEvaluator,它經過計算告知動畫系統如何從初始值過分到結束值,咱們來看一下FloatEvaluator的代碼實現:

能夠看到,FloatEvaluator實現了TypeEvaluator接口,而後重寫evaluate()方法。evaluate()方法當中傳入了三個參數,第一個參數fraction很是重要,這個參數用於表示動畫的完成度的,咱們應該根據它來計算當前動畫的值應該是多少,第二第三個參數分別表示動畫的初始值和結束值。那麼上述代碼的邏輯就比較清晰了,用結束值減去初始值,算出它們之間的差值,而後乘以fraction這個係數,再加上初始值,那麼就獲得當前動畫的值了。

Interpolator

Interpolator這個東西很難進行翻譯,直譯過來的話是補間器的意思,它的主要做用是能夠控制動畫的變化速率,好比去實現一種非線性運動的動畫效果。那麼什麼叫作非線性運動的動畫效果呢?就是說動畫改變的速率不是一成不變的,像加速運動以及減速運動都屬於非線性運動。

TimeInterpolator接口已經有很是多的實現類了,這些都是Android系統內置好的而且咱們能夠直接使用的Interpolator。每一個Interpolator都有它各自的實現效果,好比說AccelerateInterpolator就是一個加速運動的Interpolator,而DecelerateInterpolator就是一個減速運動的Interpolator。

 

  1. <span style="font-family:SimSun;font-size:12px;">anim.setInterpolator(new AccelerateInterpolator(2f));  </span>  

這裏調用了setInterpolator()方法,而後傳入了一個AccelerateInterpolator的實例,注意AccelerateInterpolator的構建函數能夠接收一個float類型的參數,這個參數是用於控制加速度的。

編寫自定義Interpolator:

最主要的難度都是在於數學計算方面的,所以這裏也就寫一個簡單點的Interpolator來給你們演示一下。既然屬性動畫默認的Interpolator是先加速後減速的一種方式,這裏咱們就對它進行一個簡單的修改,讓它變成先減速後加速的方式。

新建DecelerateAccelerateInterpolator類,讓它實現TimeInterpolator接口,代碼以下所示:

  1. public class DecelerateAccelerateInterpolator implements TimeInterpolator{    
  2.     
  3.     @Override    
  4.     public float getInterpolation(float input) {    
  5.         float result;    
  6.         if (input <= 0.5) {    
  7.             result = (float) (Math.sin(Math.PI * input)) / 2;    
  8.         } else {    
  9.             result = (float) (2 - Math.sin(Math.PI * input)) / 2;    
  10.         }    
  11.         return result;    
  12.     }    
  13.     
  14. }  

ViewProperty Animator

ViewPropertyAnimator其實算不上什麼高級技巧,它的用法格外的簡單,只不過和前面所學的全部屬性動畫的知識不一樣,它並非在3.0系統當中引入的,而是在3.1系統當中附增的一個新的功能,所以這裏咱們把它做爲整個屬性動畫系列的收尾部分。

咱們都知道,屬性動畫的機制已經不是再針對於View而進行設計的了,而是一種不斷地對值進行操做的機制,它能夠將值賦值到指定對象的指定屬性上。可是,在絕大多數狀況下,我相信你們主要都仍是對View進行動畫操做的。Android開發團隊也是意識到了這一點,沒有爲View的動畫操做提供一種更加便捷的用法確實是有點太不人性化了,因而在Android 3.1系統當中補充了ViewPropertyAnimator這個機制。

那咱們先來回顧一下以前的用法吧,好比咱們想要讓一個TextView從常規狀態變成透明狀態,就能夠這樣寫:

  1. ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f);    
  2. animator.start();    

咱們要將操做的view、屬性、變化的值都一塊兒傳入到ObjectAnimator.ofFloat()方法當中,雖然看上去也沒寫幾行代碼,但這不太像是咱們平時使用的面向對象的思惟。

那麼下面咱們就來看一下如何使用ViewPropertyAnimator來實現一樣的效果,ViewPropertyAnimator提供了更加易懂、更加面向對象的API,以下所示:

  1. textview.animate().alpha(0f);   

果真很是簡單!不過textview.animate()這個方法是怎麼回事呢?animate()方法就是在Android 3.1系統上新增的一個方法,這個方法的返回值是一個ViewPropertyAnimator對象,也就是說拿到這個對象以後咱們就能夠調用它的各類方法來實現動畫效果了,這裏咱們調用了alpha()方法並轉入0,表示將當前的textview變成透明狀態。

怎麼樣?比起使用ObjectAnimator,ViewPropertyAnimator的用法明顯更加簡單易懂吧。除此以外,ViewPropertyAnimator還能夠很輕鬆地將多個動畫組合到一塊兒,好比咱們想要讓textview運動到500,500這個座標點上,就能夠這樣寫:

  1. textview.animate().x(500).y(500);   

能夠看出,ViewPropertyAnimator是支持連綴用法的,咱們想讓textview移動到橫座標500這個位置上時調用了x(500)這個方法,而後讓textview移動到縱座標500這個位置上時調用了y(500)這個方法,將全部想要組合的動畫經過這種連綴的方式拼接起來,這樣所有動畫就都會一塊兒被執行。

那麼怎樣去設定動畫的運行時長呢?很簡單,也是經過連綴的方式設定便可,好比咱們想要讓動畫運行5秒鐘,就能夠這樣寫:

  1. textview.animate().x(500).y(500).setDuration(5000);   

除此以外,本篇文章第一部分所學的Interpolator技術咱們也能夠應用在ViewPropertyAnimator上面,以下所示:

  1. textview.animate().x(500).y(500).setDuration(5000)    
  2.         .setInterpolator(new BounceInterpolator());    

用法很簡單,一樣也是使用連綴的方式。相信你們如今都已經體驗出來了,ViewPropertyAnimator其實並無什麼太多的技巧可言,用法基本都是大同小異的,須要用到什麼功能就連綴一下,所以更多的用法你們只須要去查閱一下文檔,看看還支持哪些功能,有哪些接口能夠調用就能夠了。

那麼除了用法以外,關於ViewPropertyAnimator有幾個細節仍是值得你們注意一下的:

· 整個ViewPropertyAnimator的功能都是創建在View類新增的animate()方法之上的,這個方法會建立並返回一個ViewPropertyAnimator的實例,以後的調用的全部方法,設置的全部屬性都是經過這個實例完成的。

· 你們注意到,在使用ViewPropertyAnimator時,咱們自始至終沒有調用過start()方法,這是由於新的接口中使用了隱式啓動動畫的功能,只要咱們將動畫定義完成以後,動畫就會自動啓動。而且這個機制對於組合動畫也一樣有效,只要咱們不斷地連綴新的方法,那麼動畫就不會馬上執行,等到全部在ViewPropertyAnimator上設置的方法都執行完畢後,動畫就會自動啓動。固然若是不想使用這一默認機制的話,咱們也能夠顯式地調用start()方法來啓動動畫。

· ViewPropertyAnimator的全部接口都是使用連綴的語法來設計的,每一個方法的返回值都是它自身的實例,所以調用完一個方法以後能夠直接連綴調用它的另外一個方法,這樣把全部的功能都串接起來,咱們甚至能夠僅經過一行代碼就完成任意複雜度的動畫功能。

相關文章
相關標籤/搜索