Android系統支持原生動畫,這爲應用開發者開發絢麗的界面提供了極大的方便,有時候動畫是很必要的,當你想作一個滑動的特效的時候,若是苦思冥想都搞不定,那麼你能夠考慮下動畫,說不定動畫輕易就搞定了。下面再簡單回顧下Android中的動畫,本文後面會介紹一個稍微複雜點的動畫,先上效果圖html
View動畫:也叫漸變更畫,針對View的動畫,主要支持平移、旋轉、縮放、透明度java
Drawable動畫:也叫幀動畫,主要是設置View的背景,能夠以動畫的形式爲View設置多張背景android
對象屬性動畫(Android3.0新加入):能夠對對象的屬性進行動畫而不只僅是View,動畫默認時間間隔300ms,默認幀率10ms/幀。其能夠達到的效果是:在一個時間間隔內完成對象從一個屬性值到另外一個屬性值的改變,所以,屬性動畫幾乎是無所不能的,只要對象有這個屬性,它都能實現動畫效果,可是屬性動畫從Android3.0纔有,這就嚴重製約了屬性動畫的使用,這就是開源動畫庫nineoldandroids的做用,採用nineoldandroids,能夠在3.0之前的系統上使用屬性動畫,nineoldandroids的網址是:http://nineoldandroids.com。說到屬性動畫,就不得不提到插值器(TimeInterpolator)和估值算法(TypeEvaluator),下面介紹。算法
看上述動畫,很顯然上述動畫是一個勻速動畫,其採用了線性插值器和整型估值算法,在40ms內,View的x屬性實現從0到40的變換,因爲動畫的默認刷新率爲10ms/幀,因此該動畫將分5幀進行,咱們來考慮第三幀(x=20 t=20ms),當時間t=20ms的時候,時間流逝的百分比是0.5 (20/40=0.5),意味這如今時間過了一半,那x應該改變多少呢,這個就由插值器和估值算法來肯定。拿線性插值器來講,當時間流逝一半的時候,x的變換也應該是一半,即x的改變是0.5,爲何呢?由於它是線性插值器,是實現勻速動畫的,下面看它的源碼:ide
public class LinearInterpolator implements Interpolator { public LinearInterpolator() { } public LinearInterpolator(Context context, AttributeSet attrs) { } public float getInterpolation(float input) { return input; } }
很顯然,線性插值器的返回值和輸入值同樣,所以插值器返回的值是0.5,這意味着x的改變是0.5,這個時候插值器的工做就完成了。佈局
具體x變成了什麼值,這個須要估值算法來肯定,咱們來看看整型估值算法的源碼:動畫
public class IntEvaluator implements TypeEvaluator<Integer> { public Integer evaluate(float fraction, Integer startValue, Integer endValue) { int startInt = startValue; return (int)(startInt + fraction * (endValue - startInt)); } }
上述算法很簡單,evaluate的三個參數分別表示:估值小數、開始值和結束值,對應於咱們的例子就分別是:0.5,0,40。根據上述算法,整型估值返回給咱們的結果是20,這就是(x=20 t=20ms)的由來。ui
說明:屬性動畫要求該屬性有set方法和get方法(可選);插值器和估值算法除了系統提供的外,咱們還能夠自定義,實現方式也很簡單,由於插值器和估值算法都是一個接口,且內部都只有一個方法,咱們只要派生一個類實現接口就能夠了,而後你就能夠作出千奇百怪的動畫效果。具體一點就是:自定義插值器須要實現Interpolator或者TimeInterpolator,自定義估值算法須要實現TypeEvaluator。還有就是若是你對其餘類型(非int、float、color)作動畫,你必需要自定義類型估值算法。this
其功能和android.animation.*中的類的功能徹底一致,使用方法徹底同樣,只要咱們用nineoldandroids來編寫動畫,就能夠在全部的Android系統上運行。比較經常使用的幾個動畫類是:ValueAnimator、ObjectAnimator和AnimatorSet,其中ObjectAnimator繼承自ValueAnimator,AnimatorSet是動畫集,能夠定義一組動畫。使用起來也是及其簡單的,下面舉幾個小栗子。lua
栗子1:改變一個對象(myObject)的 translationY屬性,讓其沿着Y軸向上平移一段距離:它的高度,該動畫在默認時間內完成,動畫的完成時間是能夠定義的,想要更靈活的效果咱們還能夠定義插值器和估值算法,可是通常來講咱們不須要自定義,系統已經預置了一些,可以知足經常使用的動畫。
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
栗子2:改變一個對象的背景色屬性,典型的情形是改變View的背景色,下面的動畫可讓背景色在3秒內實現從0xFFFF8080到0xFF8080FF的漸變,而且動畫會無限循環並且會有反轉的效果
ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", /*Red*/0xFFFF8080, /*Blue*/0xFF8080FF); colorAnim.setDuration(3000); colorAnim.setEvaluator(new ArgbEvaluator()); colorAnim.setRepeatCount(ValueAnimator.INFINITE); colorAnim.setRepeatMode(ValueAnimator.REVERSE); colorAnim.start();
栗子3:動畫集合,5秒內對View的旋轉、平移、縮放和透明度都進行了改變
AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(myView, "rotationX", 0, 360), ObjectAnimator.ofFloat(myView, "rotationY", 0, 180), ObjectAnimator.ofFloat(myView, "rotation", 0, -90), ObjectAnimator.ofFloat(myView, "translationX", 0, 90), ObjectAnimator.ofFloat(myView, "translationY", 0, 90), ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f), ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f), ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1) ); set.setDuration(5 * 1000).start();
栗子4:下面是個簡單的調用方式,其animate方法是nineoldandroids特有的
Button myButton = (Button)findViewById(R.id.myButton); //Note: in order to use the ViewPropertyAnimator like this add the following import: // import static com.nineoldandroids.view.ViewPropertyAnimator.animate; animate(myButton).setDuration(2000).rotationYBy(720).x(100).y(100);
栗子5:一個採用nineoldandroids實現的稍微複雜點的動畫
佈局xml以下:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <Button android:id="@+id/menu" style="@style/MenuStyle" android:background="@drawable/menu" /> <Button android:id="@+id/item1" style="@style/MenuItemStyle" android:background="@drawable/circle1" android:visibility="gone" /> <Button android:id="@+id/item2" style="@style/MenuItemStyle" android:background="@drawable/circle2" android:visibility="gone" /> <Button android:id="@+id/item3" style="@style/MenuItemStyle" android:background="@drawable/circle3" android:visibility="gone" /> <Button android:id="@+id/item4" style="@style/MenuItemStyle" android:background="@drawable/circle4" android:visibility="gone" /> <Button android:id="@+id/item5" style="@style/MenuItemStyle" android:background="@drawable/circle5" android:visibility="gone" /> </FrameLayout>
代碼以下:
public class MainActivity extends Activity implements OnClickListener { private static final String TAG = "MainActivity"; private Button mMenuButton; private Button mItemButton1; private Button mItemButton2; private Button mItemButton3; private Button mItemButton4; private Button mItemButton5; private boolean mIsMenuOpen = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @TargetApi(Build.VERSION_CODES.HONEYCOMB) private void initView() { mMenuButton = (Button) findViewById(R.id.menu); mMenuButton.setOnClickListener(this); mItemButton1 = (Button) findViewById(R.id.item1); mItemButton1.setOnClickListener(this); mItemButton2 = (Button) findViewById(R.id.item2); mItemButton2.setOnClickListener(this); mItemButton3 = (Button) findViewById(R.id.item3); mItemButton3.setOnClickListener(this); mItemButton4 = (Button) findViewById(R.id.item4); mItemButton4.setOnClickListener(this); mItemButton5 = (Button) findViewById(R.id.item5); mItemButton5.setOnClickListener(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { mMenuButton.performClick(); getMenuInflater().inflate(R.menu.main, menu); return false; } @Override public void onClick(View v) { if (v == mMenuButton) { if (!mIsMenuOpen) { mIsMenuOpen = true; doAnimateOpen(mItemButton1, 0, 5, 300); doAnimateOpen(mItemButton2, 1, 5, 300); doAnimateOpen(mItemButton3, 2, 5, 300); doAnimateOpen(mItemButton4, 3, 5, 300); doAnimateOpen(mItemButton5, 4, 5, 300); } else { mIsMenuOpen = false; doAnimateClose(mItemButton1, 0, 5, 300); doAnimateClose(mItemButton2, 1, 5, 300); doAnimateClose(mItemButton3, 2, 5, 300); doAnimateClose(mItemButton4, 3, 5, 300); doAnimateClose(mItemButton5, 4, 5, 300); } } else { Toast.makeText(this, "你點擊了" + v, Toast.LENGTH_SHORT).show(); } } /** * 打開菜單的動畫 * @param view 執行動畫的view * @param index view在動畫序列中的順序 * @param total 動畫序列的個數 * @param radius 動畫半徑 */ private void doAnimateOpen(View view, int index, int total, int radius) { if (view.getVisibility() != View.VISIBLE) { view.setVisibility(View.VISIBLE); } double degree = Math.PI * index / ((total - 1) * 2); int translationX = (int) (radius * Math.cos(degree)); int translationY = (int) (radius * Math.sin(degree)); Log.d(TAG, String.format("degree=%f, translationX=%d, translationY=%d", degree, translationX, translationY)); AnimatorSet set = new AnimatorSet(); //包含平移、縮放和透明度動畫 set.playTogether( ObjectAnimator.ofFloat(view, "translationX", 0, translationX), ObjectAnimator.ofFloat(view, "translationY", 0, translationY), ObjectAnimator.ofFloat(view, "scaleX", 0f, 1f), ObjectAnimator.ofFloat(view, "scaleY", 0f, 1f), ObjectAnimator.ofFloat(view, "alpha", 0f, 1)); //動畫週期爲500ms set.setDuration(1 * 500).start(); } /** * 關閉菜單的動畫 * @param view 執行動畫的view * @param index view在動畫序列中的順序 * @param total 動畫序列的個數 * @param radius 動畫半徑 */ private void doAnimateClose(final View view, int index, int total, int radius) { if (view.getVisibility() != View.VISIBLE) { view.setVisibility(View.VISIBLE); } double degree = Math.PI * index / ((total - 1) * 2); int translationX = (int) (radius * Math.cos(degree)); int translationY = (int) (radius * Math.sin(degree)); Log.d(TAG, String.format("degree=%f, translationX=%d, translationY=%d", degree, translationX, translationY)); AnimatorSet set = new AnimatorSet(); //包含平移、縮放和透明度動畫 set.playTogether( ObjectAnimator.ofFloat(view, "translationX", translationX, 0), ObjectAnimator.ofFloat(view, "translationY", translationY, 0), ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f), ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f), ObjectAnimator.ofFloat(view, "alpha", 1f, 0f)); //爲動畫加上事件監聽,當動畫結束的時候,咱們把當前view隱藏 set.addListener(new AnimatorListener() { @Override public void onAnimationStart(Animator animator) { } @Override public void onAnimationRepeat(Animator animator) { } @Override public void onAnimationEnd(Animator animator) { view.setVisibility(View.GONE); } @Override public void onAnimationCancel(Animator animator) { } }); set.setDuration(1 * 500).start(); } }代碼下載: http://download.csdn.net/detail/singwhatiwanna/6782865