Android動畫進階—使用開源動畫庫nineoldandroids

pszh發表於2016-06-29

轉自:我大剛哥的部落格http://blog.csdn.net/singwhatiwanna/article/details/17639987


前言

Android系統支援原生動畫,這為應用開發者開發絢麗的介面提供了極大的方便,有時候動畫是很必要的,當你想做一個滑動的特效的時候,如果苦思冥想都搞不定,那麼你可以考慮下動畫,說不定動畫輕易就搞定了。下面再簡單回顧下Android中的動畫,本文後面會介紹一個稍微複雜點的動畫,先上效果圖


動畫分類

View動畫:也叫漸變動畫,針對View的動畫,主要支援平移、旋轉、縮放、透明度

Drawable動畫:也叫幀動畫,主要是設定View的背景,可以以動畫的形式為View設定多張背景

物件屬性動畫(Android3.0新加入):可以對物件的屬性進行動畫而不僅僅是View,動畫預設時間間隔300ms,預設幀率10ms/幀。其可以達到的效果是:在一個時間間隔內完成物件從一個屬性值到另一個屬性值的改變,因此,屬性動畫幾乎是無所不能的,只要物件有這個屬性,它都能實現動畫效果,但是屬性動畫從Android3.0才有,這就嚴重製約了屬性動畫的使用,這就是開源動畫庫nineoldandroids的作用,採用nineoldandroids,可以在3.0以前的系統上使用屬性動畫,nineoldandroids的網址是:http://nineoldandroids.com。說到屬性動畫,就不得不提到插值器(TimeInterpolator)和估值演算法(TypeEvaluator),下面介紹。

TimeInterpolator和TypeEvaluator

TimeInterpolator中文翻譯為時間插值器,它的作用是根據時間流逝的百分比來計算出當前屬性值改變的百分比,系統預置的有LinearInterpolator(線性插值器:勻速動畫)、AccelerateDecelerateInterpolator(加速減速插值器:動畫兩頭慢中間快)和DecelerateInterpolator(減速插值器:動畫越來越慢)等;TypeEvaluator的中文翻譯為型別估值演算法,它的作用是根據當前屬性改變的百分比來計算改變後的屬性值,系統預置的有IntEvaluator(針對整型屬性)、FloatEvaluator(針對浮點型屬性)和ArgbEvaluator(針對Color屬性)。可能這麼說還有點晦澀,沒關係,下面給出一個例項就很好理解了。


看上述動畫,很顯然上述動畫是一個勻速動畫,其採用了線性插值器和整型估值演算法,在40ms內,View的x屬性實現從0到40的變換,由於動畫的預設重新整理率為10ms/幀,所以該動畫將分5幀進行,我們來考慮第三幀(x=20 t=20ms),當時間t=20ms的時候,時間流逝的百分比是0.5 (20/40=0.5),意味這現在時間過了一半,那x應該改變多少呢,這個就由插值器和估值演算法來確定。拿線性插值器來說,當時間流逝一半的時候,x的變換也應該是一半,即x的改變是0.5,為什麼呢?因為它是線性插值器,是實現勻速動畫的,下面看它的原始碼:

[java] view plain copy
  1. public class LinearInterpolator implements Interpolator {  
  2.   
  3.     public LinearInterpolator() {  
  4.     }  
  5.       
  6.     public LinearInterpolator(Context context, AttributeSet attrs) {  
  7.     }  
  8.       
  9.     public float getInterpolation(float input) {  
  10.         return input;  
  11.     }  
  12. }  

很顯然,線性插值器的返回值和輸入值一樣,因此插值器返回的值是0.5,這意味著x的改變是0.5,這個時候插值器的工作就完成了。

具體x變成了什麼值,這個需要估值演算法來確定,我們來看看整型估值演算法的原始碼:

[java] view plain copy
  1. public class IntEvaluator implements TypeEvaluator<Integer> {  
  2.   
  3.     public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
  4.         int startInt = startValue;  
  5.         return (int)(startInt + fraction * (endValue - startInt));  
  6.     }  
  7. }  

上述演算法很簡單,evaluate的三個引數分別表示:估值小數、開始值和結束值,對應於我們的例子就分別是:0.5,0,40。根據上述演算法,整型估值返回給我們的結果是20,這就是(x=20 t=20ms)的由來。

說明:屬性動畫要求該屬性有set方法和get方法(可選);插值器和估值演算法除了系統提供的外,我們還可以自定義,實現方式也很簡單,因為插值器和估值演算法都是一個介面,且內部都只有一個方法,我們只要派生一個類實現介面就可以了,然後你就可以做出千奇百怪的動畫效果。具體一點就是:自定義插值器需要實現Interpolator或者TimeInterpolator,自定義估值演算法需要實現TypeEvaluator。還有就是如果你對其他型別(非int、float、color)做動畫,你必須要自定義型別估值演算法。

nineoldandroids介紹

其功能和android.animation.*中的類的功能完全一致,使用方法完全一樣,只要我們用nineoldandroids來編寫動畫,就可以在所有的Android系統上執行。比較常用的幾個動畫類是:ValueAnimator、ObjectAnimator和AnimatorSet,其中ObjectAnimator繼承自ValueAnimator,AnimatorSet是動畫集,可以定義一組動畫。使用起來也是及其簡單的,下面舉幾個小栗子。

栗子1:改變一個物件(myObject)的 translationY屬性,讓其沿著Y軸向上平移一段距離:它的高度,該動畫在預設時間內完成,動畫的完成時間是可以定義的,想要更靈活的效果我們還可以定義插值器和估值演算法,但是一般來說我們不需要自定義,系統已經預置了一些,能夠滿足常用的動畫。

[java] view plain copy
  1. ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();  

栗子2:改變一個物件的背景色屬性,典型的情形是改變View的背景色,下面的動畫可以讓背景色在3秒內實現從0xFFFF8080到0xFF8080FF的漸變,並且動畫會無限迴圈而且會有反轉的效果

[java] view plain copy
  1. ValueAnimator colorAnim = ObjectAnimator.ofInt(this"backgroundColor"/*Red*/0xFFFF8080/*Blue*/0xFF8080FF);  
  2. colorAnim.setDuration(3000);  
  3. colorAnim.setEvaluator(new ArgbEvaluator());  
  4. colorAnim.setRepeatCount(ValueAnimator.INFINITE);  
  5. colorAnim.setRepeatMode(ValueAnimator.REVERSE);  
  6. colorAnim.start();  

栗子3:動畫集合,5秒內對View的旋轉、平移、縮放和透明度都進行了改變

[java] view plain copy
  1. AnimatorSet set = new AnimatorSet();  
  2. set.playTogether(  
  3.     ObjectAnimator.ofFloat(myView, "rotationX"0360),  
  4.     ObjectAnimator.ofFloat(myView, "rotationY"0180),  
  5.     ObjectAnimator.ofFloat(myView, "rotation"0, -90),  
  6.     ObjectAnimator.ofFloat(myView, "translationX"090),  
  7.     ObjectAnimator.ofFloat(myView, "translationY"090),  
  8.     ObjectAnimator.ofFloat(myView, "scaleX"11.5f),  
  9.     ObjectAnimator.ofFloat(myView, "scaleY"10.5f),  
  10.     ObjectAnimator.ofFloat(myView, "alpha"10.25f, 1)  
  11. );  
  12. set.setDuration(5 * 1000).start();  

栗子4:下面是個簡單的呼叫方式,其animate方法是nineoldandroids特有的

[java] view plain copy
  1. Button myButton = (Button)findViewById(R.id.myButton);  
  2.   
  3. //Note: in order to use the ViewPropertyAnimator like this add the following import:  
  4. //  import static com.nineoldandroids.view.ViewPropertyAnimator.animate;  
  5. animate(myButton).setDuration(2000).rotationYBy(720).x(100).y(100);  

栗子5:一個採用nineoldandroids實現的稍微複雜點的動畫

佈局xml如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent" >  
  5.   
  6.     <Button  
  7.         android:id="@+id/menu"  
  8.         style="@style/MenuStyle"  
  9.         android:background="@drawable/menu" />  
  10.   
  11.     <Button  
  12.         android:id="@+id/item1"  
  13.         style="@style/MenuItemStyle"  
  14.         android:background="@drawable/circle1"  
  15.         android:visibility="gone" />  
  16.   
  17.     <Button  
  18.         android:id="@+id/item2"  
  19.         style="@style/MenuItemStyle"  
  20.         android:background="@drawable/circle2"  
  21.         android:visibility="gone" />  
  22.   
  23.     <Button  
  24.         android:id="@+id/item3"  
  25.         style="@style/MenuItemStyle"  
  26.         android:background="@drawable/circle3"  
  27.         android:visibility="gone" />  
  28.   
  29.     <Button  
  30.         android:id="@+id/item4"  
  31.         style="@style/MenuItemStyle"  
  32.         android:background="@drawable/circle4"  
  33.         android:visibility="gone" />  
  34.   
  35.     <Button  
  36.         android:id="@+id/item5"  
  37.         style="@style/MenuItemStyle"  
  38.         android:background="@drawable/circle5"  
  39.         android:visibility="gone" />  
  40. </FrameLayout>  

程式碼如下:

[java] view plain copy
  1. public class MainActivity extends Activity implements OnClickListener {  
  2.   
  3.     private static final String TAG = "MainActivity";  
  4.   
  5.     private Button mMenuButton;  
  6.     private Button mItemButton1;  
  7.     private Button mItemButton2;  
  8.     private Button mItemButton3;  
  9.     private Button mItemButton4;  
  10.     private Button mItemButton5;  
  11.   
  12.     private boolean mIsMenuOpen = false;  
  13.   
  14.     @Override  
  15.     protected void onCreate(Bundle savedInstanceState) {  
  16.         super.onCreate(savedInstanceState);  
  17.         setContentView(R.layout.activity_main);  
  18.   
  19.         initView();  
  20.     }  
  21.   
  22.     @TargetApi(Build.VERSION_CODES.HONEYCOMB)  
  23.     private void initView() {  
  24.         mMenuButton = (Button) findViewById(R.id.menu);  
  25.         mMenuButton.setOnClickListener(this);  
  26.   
  27.         mItemButton1 = (Button) findViewById(R.id.item1);  
  28.         mItemButton1.setOnClickListener(this);  
  29.   
  30.         mItemButton2 = (Button) findViewById(R.id.item2);  
  31.         mItemButton2.setOnClickListener(this);  
  32.   
  33.         mItemButton3 = (Button) findViewById(R.id.item3);  
  34.         mItemButton3.setOnClickListener(this);  
  35.   
  36.         mItemButton4 = (Button) findViewById(R.id.item4);  
  37.         mItemButton4.setOnClickListener(this);  
  38.   
  39.         mItemButton5 = (Button) findViewById(R.id.item5);  
  40.         mItemButton5.setOnClickListener(this);  
  41.     }  
  42.   
  43.     @Override  
  44.     public boolean onCreateOptionsMenu(Menu menu) {  
  45.         mMenuButton.performClick();  
  46.         getMenuInflater().inflate(R.menu.main, menu);  
  47.         return false;  
  48.     }  
  49.   
  50.     @Override  
  51.     public void onClick(View v) {  
  52.         if (v == mMenuButton) {  
  53.             if (!mIsMenuOpen) {  
  54.                 mIsMenuOpen = true;  
  55.                 doAnimateOpen(mItemButton1, 05300);  
  56.                 doAnimateOpen(mItemButton2, 15300);  
  57.                 doAnimateOpen(mItemButton3, 25300);  
  58.                 doAnimateOpen(mItemButton4, 35300);  
  59.                 doAnimateOpen(mItemButton5, 45300);  
  60.             } else {  
  61.                 mIsMenuOpen = false;  
  62.                 doAnimateClose(mItemButton1, 05300);  
  63.                 doAnimateClose(mItemButton2, 15300);  
  64.                 doAnimateClose(mItemButton3, 25300);  
  65.                 doAnimateClose(mItemButton4, 35300);  
  66.                 doAnimateClose(mItemButton5, 45300);  
  67.             }  
  68.         } else {  
  69.             Toast.makeText(this"你點選了" + v, Toast.LENGTH_SHORT).show();  
  70.         }  
  71.   
  72.     }  
  73.   
  74.     /** 
  75.      * 開啟選單的動畫 
  76.      * @param view 執行動畫的view 
  77.      * @param index view在動畫序列中的順序 
  78.      * @param total 動畫序列的個數 
  79.      * @param radius 動畫半徑 
  80.      */  
  81.     private void doAnimateOpen(View view, int index, int total, int radius) {  
  82.         if (view.getVisibility() != View.VISIBLE) {  
  83.             view.setVisibility(View.VISIBLE);  
  84.         }  
  85.         double degree = Math.PI * index / ((total - 1) * 2);  
  86.         int translationX = (int) (radius * Math.cos(degree));  
  87.         int translationY = (int) (radius * Math.sin(degree));  
  88.         Log.d(TAG, String.format("degree=%f, translationX=%d, translationY=%d",  
  89.                 degree, translationX, translationY));  
  90.         AnimatorSet set = new AnimatorSet();  
  91.         //包含平移、縮放和透明度動畫  
  92.         set.playTogether(  
  93.                 ObjectAnimator.ofFloat(view, "translationX"0, translationX),  
  94.                 ObjectAnimator.ofFloat(view, "translationY"0, translationY),  
  95.                 ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f),  
  96.                 ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f),  
  97.                 ObjectAnimator.ofFloat(view, "alpha", 0f, 1));  
  98.         //動畫週期為500ms  
  99.         set.setDuration(1 * 500).start();  
  100.     }  
  101.   
  102.     /** 
  103.      * 關閉選單的動畫 
  104.      * @param view 執行動畫的view 
  105.      * @param index view在動畫序列中的順序 
  106.      * @param total 動畫序列的個數 
  107.      * @param radius 動畫半徑 
  108.      */  
  109.     private void doAnimateClose(final View view, int index, int total,  
  110.             int radius) {  
  111.         if (view.getVisibility() != View.VISIBLE) {  
  112.             view.setVisibility(View.VISIBLE);  
  113.         }  
  114.         double degree = Math.PI * index / ((total - 1) * 2);  
  115.         int translationX = (int) (radius * Math.cos(degree));  
  116.         int translationY = (int) (radius * Math.sin(degree));  
  117.         Log.d(TAG, String.format("degree=%f, translationX=%d, translationY=%d",  
  118.                 degree, translationX, translationY));  
  119.         AnimatorSet set = new AnimatorSet();  
  120.       //包含平移、縮放和透明度動畫  
  121.         set.playTogether(  
  122.                 ObjectAnimator.ofFloat(view, "translationX", translationX, 0),  
  123.                 ObjectAnimator.ofFloat(view, "translationY", translationY, 0),  
  124.                 ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f),  
  125.                 ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f),  
  126.                 ObjectAnimator.ofFloat(view, "alpha", 1f, 0f));  
  127.         //為動畫加上事件監聽,當動畫結束的時候,我們把當前view隱藏  
  128.         set.addListener(new AnimatorListener() {  
  129.             @Override  
  130.             public void onAnimationStart(Animator animator) {  
  131.             }  
  132.   
  133.             @Override  
  134.             public void onAnimationRepeat(Animator animator) {  
  135.             }  
  136.   
  137.             @Override  
  138.             public void onAnimationEnd(Animator animator) {  
  139.                 view.setVisibility(View.GONE);  
  140.             }  
  141.   
  142.             @Override  
  143.             public void onAnimationCancel(Animator animator) {  
  144.             }  
  145.         });  
  146.   
  147.         set.setDuration(1 * 500).start();  
  148.     }  
  149. }  
程式碼下載:http://download.csdn.net/detail/singwhatiwanna/6782865

相關文章