1、概述
在3.0系統之前,Android給我們提供了逐幀動畫Frame Animation和補間動畫Tween Animation兩種動畫:
- 逐幀動畫的原理很簡單,就是將一個完整的動畫拆分成一張張單獨的圖片,然后將它們連貫起來進行播放;
- 補間動畫是專門為View提供的動畫,可以實現View的透明度、縮放、平移和旋轉四種效果。
補間動畫有兩個個缺陷:
- 補間動畫只能對View設置動畫,對非View的對象不能設置動畫;
- 補間動畫只是改變了View的顯示效果而沒有真正的改變View的屬性。例如,我們想使用補間動畫將一個按鈕從一個位置移動到一個新的位置,那么當移動完成之后我們點擊這個按鈕,是不會觸發其點擊事件的,而當我們點擊移動前的位置時,會觸發其點擊事件,即補間動畫只是在另一個地方重新繪制了這個View,其他的東西都沒有改變。
在3.0系統之后,Android為我們提供了一種新的動畫——Animator屬性動畫。在屬性動畫中,我們不僅可以像補間動畫那樣設置控件的透明度、縮放、平移或旋轉的動畫,還可以做到將這些動畫聯合起來播放、將一組動畫按順序播放、控制動畫的播放速度,甚至可以對非View設置動畫等等。
屬性動畫,顧名思義,是對對象的屬性設置的動畫。簡單的說,只要一個對象的某個屬性有set和get方法,就可以對其設置屬性動畫。一句話概括,屬性動畫就是不斷的改變一個對象的某個屬性。我們只需要告訴系統動畫的運行時長,需要執行哪種類型的動畫,以及動畫的初始值和結束只,剩下的工作就可以全部交給系統去完成了。
正是因為屬性動畫是對屬性的動畫,因此補間動畫的第二個缺陷就不復存在了。使用屬性動畫移動一個按鈕,那么這個按鈕就是真的被移動了,而不僅僅是在另一個地方重新繪制了自己那么簡單。
2、ObjectAnimator
ObjectAnimator是屬性動畫中最常用的一個類,我們可以通過它直接控制一個對象的屬性。使用ObjectAnimator時,我們只需要我們想修改哪個對象的哪個屬性、屬性的起始值和結束值,以及動畫的持續時間,系統就可以為我們運行動畫了。ObjectAnimator的使用方法如下。
執行單個動畫:
ObjectAnimator.ofFloat(image, "translationX", 0f, 500f) // 初始化動畫,設置各個參數 .setDuration(3000) // 設置動畫持續時間 .start(); // 開始運行動畫
上面代碼中,ofFloat()方法的第一個參數是動畫作用的對象,這里是一個ImageView;第二個參數是屬性名稱,這里指定的是X軸的平移;第三個參數是一個不定長參數,指定屬性的起始值和結束值;setDuration()方法指定的是動畫執行的時長,這里是3秒鍾;最后調用start()方法,動畫就開始執行了。
ObjectAnimator不僅有ofFloat()方法,還有很多其他方法,例如ofInt()、ofObject()、ofPropertyValuesHolder()等,這些方法會在后面詳細介紹。
如果想要同時執行多個動畫,只需要指定多個動畫后分別調用start()方法即可,代碼如下:
ObjectAnimator.ofFloat(image, "translationX", 0f, 500f).setDuration(3000).start(); ObjectAnimator.ofFloat(image, "translationY", 0f, 500f).setDuration(3000).start(); ObjectAnimator.ofFloat(image, "rotation", 0f, 360f).setDuration(3000).start();
為了加深對ObejctAnimator的理解,這里我們使用ObjectAnimator實現一個衛星菜單的效果,效果圖如下:
如上圖所示,開始的時候七個菜單圖標是隱藏在右下角的紅色按鈕底下的,如左圖所示;當我們點擊了紅色按鈕的時候,其他菜單圖標就會彈出,像衛星一樣圍繞早紅色按鈕外層,如右圖所示。實現這個效果的核心代碼如下:
for (int i = 0; i < menus.size(); i++) { ImageView menu = menus.get(i); double angle = Math.toRadians(i * (90 * 1.0 / (menus.size() - 1))); // 角度 double radius = 450; // 半徑 float distanceX = (float) (Math.cos(angle) * radius); // X坐標偏移量 float distanceY = (float) (Math.sin(angle) * radius); // Y坐標偏移量 ObjectAnimator animatorX; ObjectAnimator animatorY; if (isOpen) { // 如果菜單是打開的則關閉菜單 animatorX = ObjectAnimator.ofFloat(menu, "translationX", -distanceX, 0f); animatorY = ObjectAnimator.ofFloat(menu, "translationY", -distanceY, 0f); } else { // 如果菜單是關閉的則打開菜單 animatorX = ObjectAnimator.ofFloat(menu, "translationX", 0f, -distanceX); animatorY = ObjectAnimator.ofFloat(menu, "translationY", 0f, -distanceY); } AnimatorSet set = new AnimatorSet(); // X、Y軸同時移動 set.playTogether(animatorX, animatorY); set.setDuration(500); set.setInterpolator(new BounceInterpolator()); set.start(); }
3、組合動畫
所謂的組合動畫,就是將多個動畫同時播放,或將一組動畫按順序播放。在上面介紹ObjectAnimator時我們已經介紹了一種方式,就是指定多個動畫后分別調用start()方法。下面介紹其他幾種方法:
方法二:使用PropertyValuesHolder:
PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("translationX", 0f, 500f); PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("translationY", 0f, 500f); PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("rotation", 0f, 360f); ObjectAnimator.ofPropertyValuesHolder(image, holder1, holder2, holder3).setDuration(3000).start();
方法三:使用AnimatorSet:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f); ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f); ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "rotation", 0f, 360f); AnimatorSet set = new AnimatorSet(); set.setDuration(3000); set.playTogether(animator1, animator2, animator3); set.start();
上面說到,屬性動畫不光可以讓一組動畫同時播放,還可以按一定順序播放動畫。按順序播放動畫的幾種方式代碼如下:
方法一:使用AnimatorSet對象中的API實現:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f); ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f); ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "rotationX", 0f, 360f); ObjectAnimator animator4 = ObjectAnimator.ofFloat(image, "rotationY", 0f, 360f); AnimatorSet set = new AnimatorSet(); set.play(animator3).before(animator2).after(animator1).with(animator4); set.setDuration(3000); set.start();
方法二:設置動畫監聽事件AnimatorListener:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "alpha", 0f, 1f); final ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f); final ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f); animator1.setDuration(3000); animator2.setDuration(3000); animator3.setDuration(3000); // 設置屬性動畫的監聽事件(使用AnimatorListener必須要監聽所有四個事件) animator1.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { animator2.start(); } @Override public void onAnimationEnd(Animator animation) { animator3.start(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); animator1.start();
方法三:設置動畫監聽事件AnimatorListenerAdapter:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(image, "alpha", 0f, 1f); final ObjectAnimator animator2 = ObjectAnimator.ofFloat(image, "translationX", 0f, 500f); final ObjectAnimator animator3 = ObjectAnimator.ofFloat(image, "translationY", 0f, 500f); animator1.setDuration(3000); animator2.setDuration(3000); animator3.setDuration(3000); // 設置屬性動畫的監聽事件(使用AnimatorListenerAdapter可以選擇不監聽所有事件) animator1.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { animator3.start(); // 在animator1執行完后執行animator3 } @Override public void onAnimationStart(Animator animation) { animator2.start(); //在animator1執行的同時執行animator2 } }); animator1.start();
4、ValueAnimator
ValueAnimator是整個屬性動畫最核心的類,ObjectAnimator類就是ValueAnimator類的一個子類。前面我們說過,屬性動畫的原理就是通過不斷改變對象的屬性值來實現過渡的,而這種過渡就是通過ValueAnimator類來負責計算的。除此之外,ValueAnimator還負責管理動畫的播放次數、播放模式和動畫監聽等。
ValueAnimator的使用方法和ObjectAnimator的使用方法基本類似,不同的是ValueAnimator中不能指定運行動畫的對象,因此ValueAnimator往往需要設置一個動畫監聽,通過不斷監聽當前動畫運行到的屬性值來動態的進行處理。代碼如下:
ValueAnimator animator = ValueAnimator.ofInt(0, 100); // 產生一個從0到100變化的整數的動畫 animator.setDuration(2000); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Integer value = (Integer) animation.getAnimatedValue(); // 動態的獲取當前運行到的屬性值 ((Button) view).setText(value + ""); } }); animator.start(); // 開始播放動畫
和ObjectAnimator一樣,除了ofFloat()之外,ValueAnimator也有一些其他的方法,如ofPropertyValuesHolder()、ofObject()、ofInt()等方法。
除了上面的功能,ValueAnimator還可以通過setRepeatCount()和setRepeatMode()方法來設置動畫重復的次數和播放模式、通過setStartDelay()方法來設置動畫延遲播放的時間。這些方法也都可以在ObjectAnimator中使用。
5、Animator監聽器
在“組合動畫”章節中我們已經接觸過Animator的監聽器了。常用的Animator監聽器有AnimatorListener和AnimatorListenerAdapter,都是通過Animator對象的addListener()方法設置的。
如果添加的監聽器是AnimatorListener,那么就必須要實現onAnimationStart()、onAnimationRepeat()、onAnimationEnd()和onAnimationCancel()四個方法,示例代碼如下:
animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { // 動畫開始的監聽事件 } @Override public void onAnimationEnd(Animator animation) { // 動畫結束的監聽事件 } @Override public void onAnimationCancel(Animator animation) { // 動畫取消的監聽事件 } @Override public void onAnimationRepeat(Animator animation) { // 動畫重播的監聽事件 } });
如果添加的是AnimatorListenerAdapter,那么就不必一一實現這四個方法,而是可以根據自己的需要自定義實現,示例代碼如下:
animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { // 動畫結束的監聽事件 } @Override public void onAnimationStart(Animator animation) { // 動畫開始的監聽事件 } // 其他兩個事件可以選擇不實現 });
6、使用XML編寫動畫
過去的補間動畫可以通過XML的方式編寫,屬性動畫也可以編寫到XML文件中。編寫到XML文件中的一個好處是可以方便的實現動畫的重用。
我們需要在項目的res目錄下創建一個名為animator的文件夾,在這個文件夾中定義動畫。animator動畫XML文件中可以包括以下三種標簽:
- <animator>:相當於JAVA代碼中的ValueAnimator;
- <objectAnimator>:相當於JAVA代碼中的ObjectAnimator;
- <set>:相當於JAVA代碼中的AnimatorSet。
如果我們想直接定義一個ValueAnimator,可以這樣寫:
<?xml version="1.0" encoding="utf-8"?> <animator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="5000" android:interpolator="@android:anim/bounce_interpolator" android:repeatCount="2" android:repeatMode="restart" android:valueFrom="0" android:valueTo="100" android:valueType="intType" />
如果我們想直接定義一個ObjectAnimator,可以這樣寫:
<?xml version="1.0" encoding="utf-8"?> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:duration="5000" android:interpolator="@android:anim/accelerate_interpolator" android:propertyName="scaleX" android:repeatCount="2" android:repeatMode="reverse" android:valueFrom="0" android:valueTo="360" android:valueType="floatType" />
如果我們想要定義一系列動畫,可以這樣寫:
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially"> <objectAnimator android:duration="5000" android:propertyName="translationX" android:valueFrom="0" android:valueTo="500" android:valueType="floatType" /> <objectAnimator android:duration="5000" android:propertyName="translationY" android:valueFrom="0" android:valueTo="500" android:valueType="floatType" /> <set android:ordering="together"> <objectAnimator android:duration="5000" android:propertyName="scaleX" android:valueFrom="0" android:valueTo="360" android:valueType="floatType" /> <objectAnimator android:duration="5000" android:propertyName="scaleY" android:valueFrom="0" android:valueTo="360" android:valueType="floatType" /> </set> </set>
在JAVA代碼中調用這些XML文件中定義的動畫時這樣寫:
Animator animator = AnimatorInflater.loadAnimator(MainActivity.this,R.animator.object_animator);
animator.setTarget(view);
animator.start();
7、TypeEvaluator
TypeEvaluator的作用是告訴動畫如何從初始值過渡到結束值。
我們一直在使用的ObjectAnimator.ofFloat()方法中其實就封裝了一個FloatEvaluator,FloatEvaluator是TypeEvaluator的一個子類,可以在指定的float類型的初始值與結束值之間進行平滑的過渡,源碼如下:
public class FloatEvaluator implements TypeEvaluator<Number> { public Float evaluate(float fraction, Number startValue, Number endValue) { float startFloat = startValue.floatValue(); return startFloat + fraction * (endValue.floatValue() - startFloat); } }
可以看到,FloatEvaluator實現了TypeEvaluator接口並實現了器evaluate方法,在方法中第一個參數是進度,相當於百分比;第二個和第三個參數是開始值和結束值,通過這三個值來確定當前需要的屬性值並返回。
前面說過,ValueAnimator和ObjectAnimator除了常用的ofFloat()、ofInt()方法之外,還有一個ofObject()方法,這個方法是對任意對象的屬性進行動畫操作的,但是Android不知道我們的任意對象是什么對象,因此也不確定我們的屬性是怎樣從初始值過渡到結束值的,這個時候我們就需要實現一個自己的TypeEvaluator來告訴系統我們的對象的屬性是如何過渡的。
例如,我們自定義一個動畫,讓一個TextView中的文本從“0%”變化到“100%”,核心代碼如下:
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator () { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { int start = Integer.parseInt((String) startValue); int end = Integer.parseInt((String) endValue); int result = (int) ((start + fraction * (end - start))); return result + "%"; } }, "0", "100"); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { String result = (String) animation.getAnimatedValue(); text.setText(result); } }); animator.setDuration(5000); animator.start();
8、Interpolator
Interpolator的作用是可以控制動畫的變化速率。
Android系統中默認為我們提供了多種已經實現好的Interpolator,常用的如下:
1) BounceInterpolator:彈跳效果;
2) AccelerateInterpolator:逐漸加速;
3) DecelerateInterpolator:逐漸減速;
4) AccelerateDecelerateInterpolator:先加速后減速;
5) OvershootInterpolator:到達目標點時“跑過頭了”,再返回到目標點;
6) AnticipateInterpolator:移動之前先向后“助跑”;
7) AnticipateOvershootInterpolator:OvershootInterpolator和AnticipateInterpolator的組合效果;
8) CycleInterpolator:對於指定的動畫,正向做一遍,反響做一遍;
Android中還為我們提供了一個接口TimeInterpolator,供我們自定義插值器。我們也可以實現TimeInterpolator的一些已有實現類(如BaseInterpolator、Interpolator)來自定義插值器。
TimeInterpolator接口中又一個抽象方法setInterpolation(),方法中又一個參數input,這個參數的值在整個動畫過程中從0勻速變化到1,也就是相當於一個百分比,指示當前動畫播放到哪了。
如果我們在這個方法中直接返回input參數,那么這個插值器就是一個勻速插值器,Android默認給我們提供的LinearInterpolator就是這樣寫的,源碼如下:
public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { public float getInterpolation(float input) { return input; //勻速 } }
當然,如果我們修改input的值並返回,就會得到加速或減速的效果,如Android默認提供的AccelerateDecelerateInterpolator中的源碼:
public class AccelerateDecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory { public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; //先加速后減速 } }
有了以上的源碼作為參考,我們就可以寫自定義的插值器了。例如,我們寫一個先減速后加速的插值器DecelerateAccelerateInterpolator,代碼如下:
public class DecelerateAccelerateInterpolator implements TimeInterpolator { @Override public float getInterpolation(float input) { float result; if (input <= 0.5) { result = (float) (Math.sin(Math.PI * input)) / 2; //前半部分減速 } else { result = (float) (2 - Math.sin(Math.PI * input)) / 2; //后半部分加速 } return result; } }
9、ViewPropertyAnimator
ViewPropertyAnimator是在Android 3.1的API中新加入的更加易懂、更加便於使用的動畫API。
屬性動畫雖然可以對所有對象添加動畫效果,但我們在使用過程中還是以對View使用動畫為多,因此Google為我們提供了一個ViewPropertyAnimator,讓我們可以簡單的調用這套API來單獨為View設置動畫。
ViewPropertyAnimator讓我們可以使用鏈式編程,一行代碼為一個View對象設置屬性,示例代碼如下:
textView.animate() .translationX(200) .translationY(200) .setDuration(2000) .setInterpolator(new BounceInterpolator()) .start();
當我們對一個View對象使用animate()方法時,就返回了一個ViewPropertyAnimator對象,這個對象中支持所有補間動畫的API以及屬性動畫的API。ViewPropertyAnimator是默認執行的動畫,也就是說,我們不需要在最后調用start()方法,動畫也會自動播放。
到此為止,屬性動畫相關的知識點就基本上介紹完了,給自己留個筆記,也希望對大家有幫助~~
參考資料:
- ObjectAnimator、ValueAnimator:http://blog.csdn.net/guolin_blog/article/details/43536355
- TypeEvaluator:http://blog.csdn.net/guolin_blog/article/details/43816093
- Interpolator和ViewPropertyAnimator:http://blog.csdn.net/guolin_blog/article/details/44171115