Android 開發中,老是須要一些動畫來優化用戶的交互體驗,提升用戶滿意度。所以,Google 爲咱們提供了一些用於處理動畫效果的動畫框架。Android 的動畫框架分爲兩類:面試
既然有了傳統動畫框架,Google 爲何還要創造一個屬性動畫框架呢?性能優化
咱們下面舉個例子來講明一下傳統動畫的侷限性。架構
在佈局中加入一個 ImageView 和一個 Button,點擊 ImageView 後彈出一個 Toast,點擊 Button 後使 ImageView 展示一個向右平移的動畫效果。框架
下面是使用傳統動畫實現的代碼:異步
TranslateAnimation animation = new TranslateAnimation(0,200,0,0); // 平移動畫x軸移動200,y軸不動
animation.setDuration(1000); // 動畫時長
animation.setFillAfter(true); // 使動畫結束後停留在結束的位置
mIvPicture.startAnimation(animation);
複製代碼
運行後,ImageView 確實進行了咱們預期的平移的效果。但是當咱們嘗試點擊 ImageView 當前的位置時,卻沒有 Toast 彈出。咱們再嘗試去點擊 ImageView 開始動畫前的位置,卻成功彈出了 Toast。ide
這就是傳統動畫很大的侷限性:函數
所以,Google 爲咱們提供了一套全新的屬性動畫框架,來讓咱們實現更豐富的動畫效果。佈局
ObjectAnimator 是屬性動畫中,最簡單也最經常使用的一個對象。性能
平移優化
前文提到的使 ImageVIew 向右平移 200 像素的動畫效果,使用屬性動畫只須要很簡單的幾句代碼便可實現:
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F)
.setDuration(1000)
.start();
複製代碼
咱們來分析一下這一句代碼。咱們調用了ofFloat代碼,並傳入三個參數。
第一個參數是動畫須要操縱的目標,在這裏是咱們的 ImageView。
第二個參數是所須要操縱的目標所具有的屬性名稱。
第三個參數是這個動畫變化的取值範圍。
最後設置一下它的動畫的屬性,即可以 start 了。
此次咱們再次點擊 ImageView 目前的位置,成功地彈出了 Toast。這證明了屬性動畫是經過改變物體的屬性來達到動畫效果的理論。
當咱們須要改變 y 座標時,只須要把 "translationX" 變爲 "translationY" 便可。
其實 ,只要Google對一個對象的某個屬性提供了get和set方法,咱們就可使用這個屬性來實現動畫效果。
其實咱們還能用 X Y 兩個屬性實現以前的動畫效果,那麼對象屬性中 X 的 Y 與 translationX translationY 有什麼區別呢?
旋轉
旋轉屬性使用的是 "rotation" 屬性,後面的變換範圍的單位是角度。
好比想讓 ImageView 旋轉90度,只須要
ObjectAnimator.ofFloat(mIvPicture,"rotation",0F,90F)
.setDuration(1000)
.start();
複製代碼
其餘
其實屬性動畫能操縱的屬性,只要具備 set、get 方法,均可以進行操縱。如 scaleX、scaleY 等等...
插值器
Android 爲咱們內置了插值器,使咱們的動畫更爲天然。好比可讓咱們的平移動畫像物體的重力加速度由快到慢的 Accelerate 等等
Android中內置了七種插值器,分別是
要應用插值器,能夠調用 ObjectAnimator 的 setInterpolator 方法, new 出對應的插值器做爲參數(xxxInterpolator)。好比下面這段代碼:
animator.setInterpolator(new AccelerateInterpolator());
複製代碼
經過插值器,咱們可讓動畫的效果更佳天然。
當咱們把幾種動畫按順序寫下時,運行程序,會發現效果是三種屬性動畫的疊加。由此能夠發現,屬性動畫在調用 start 方法後,其實是一個異步的過程。所以咱們就能夠看到三個屬性動畫同時做用的效果。經過這樣的方法,其實就能夠實現多種屬性動畫同時做用的效果:
ObjectAnimator.ofFloat(mIvPicture,"translationX",0F,200F).setDuration(1000).start();
ObjectAnimator.ofFloat(mIvPicture,"rotationX",0F,360F).setDuration(1000).start();
ObjectAnimator.ofFloat(mIvPicture,"translationY",0F,200F).setDuration(1000).start();
複製代碼
其實 Google 爲咱們提供了更好的方法,來實現這樣的效果。
咱們可使用 PropertyValuesHolder 來實現。其構造函數僅僅比 ObjectAnimator 少了一個做用對象參數。以後經過ObjectAnimator 的 ofPropertyValuesHolder 方法,傳入做用對象以及要同時做用的 PropertyValuesHolder 便可執行。能夠看到下面的代碼示例:
PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("translationX",0F,200F);
PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("rotationX",0F,360F);
PropertyValuesHolder p3 = PropertyValuesHolder.ofFloat("translationY",0F,200F);
ObjectAnimator.ofPropertyValuesHolder(mIvPicture,p1,p2,p3).setDuration(1000).start();
複製代碼
運行後能夠發現,與以前的效果是相同的。
那既然兩種方法效果同樣,這樣相比以前有什麼好處麼?
咱們其實還能夠經過 AnimatorSet,來實現一樣的效果。這裏咱們調用了 set 的 playTogether 方法,使得這些方法同時執行:
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F);
set.playTogether(animator1,animator2,animator3);
set.setDuration(1000);
set.start();
複製代碼
除了 playTogether 方法外,AnimatorSet 還提供了 playSequentially 方法,它可使得動畫按順序執行。具體順序取決於調用時的參數順序。
AnimatorSet set = new AnimatorSet();
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mIvPicture, "translationX", 0F, 200F);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mIvPicture, "rotationX", 0F, 360F);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mIvPicture, "translationY", 0F, 200F);
set.playSequentially(animator1,animator2,animator3);
set.setDuration(1000);
set.start();
複製代碼
咱們除了能夠用上述方法來讓動畫按順序執行外,也能夠經過 AnimatorSet 的 play、with、after、before 等方法相組合來控制動畫播放關係。
例如以下的代碼就能夠實現先平移,再旋轉的效果
set.play(animator1).with(animator3);
set.play(animator2).after(animator1);
複製代碼
經過下面的代碼,咱們能夠實現按鈕按下後漸隱的效果。
mBtnPress.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(mBtnPress,"alpha",1F,0F);
animator.setDuration(1000);
animator.start();
}
});
複製代碼
但若是咱們想要在動畫播放完成後再執行一些操做的話,又該如何實現呢?
一個AnimatorListener,須要實現四個方法,分別是:
它們的回調時機咱們根據字面意思即可以理解。大部分時候,咱們須要實現的是onAnimationEnd方法。
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show();
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
複製代碼
若是每次監聽都須要實現這麼多方法,未免太麻煩了一點。所以 Android 爲咱們提供了另外一種方法來添加動畫的監聽事件:在添加 AnimatorListener 的時候,傳入 AnimatorListenerAdapter 便可。這樣咱們就只須要實現本身須要的方法便可。
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
Toast.makeText(MainActivity.this,"Animation End",Toast.LENGTH_SHORT).show();
}
});
複製代碼
ValueAnimator 自己不做用於任何一個屬性,也不提供任何一種動畫。它就是一個數值發生器,能夠產生想要的各類數值。Android 系統爲它提供了不少計算數值的方法,如 int、float 等等。咱們也能夠本身實現計算數值的方法。其實,在屬性動畫中,如何產生每一步的動畫效果,都是經過 ValueAnimator 計算出來的。
好比咱們要實現一個從 0-100 的位移動畫。隨着動畫時間的持續,它產生的值也會從 0-100 遞增。經過這個 ValueAnimator 產生的值,再進行屬性的設置便可。
那麼 ValueAnimator 到底是如何產生這些值的呢?
因爲 ValueAnimator 不做用於任何一個屬性,也不提供任何一種動畫。所以並無 ObjectAnimator 使用得普遍。
實際上,ObjectAnimator 就是基於 ValueAnimator 進行的一次封裝。咱們能夠查看 ObjectAnimator 的源碼,會發現它繼承自 ValueAnimator,是它的一個子類。正是 ValueAnimator 產生的變化值,才使得 ObjectAnimator 能夠將它應用於各個屬性。
咱們能夠經過 ValueAnimator 的 ofXXX 產生一個 XXX 類型的值(如ofInt),而後爲 ValueAnimator 添加一個更新的回調事件。在回調事件中,經過參數 animation 的 getAnimationValue() 方法,來獲取對應的 value。有了這個值,咱們就能夠實現咱們全部想要的動畫效果。
好比此處就經過 ValueAnimator 實現了一個計時器的動畫效果。
ValueAnimator animator = ValueAnimator.ofInt(0,100);
animator.setDuration(5000);
animator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
mButton.setText(""+value);
}
});
animator.start();
複製代碼
前面提到,ValueAnimator 能夠建立自定義的數值生成器,作法就是調用 ValueAnimator 的 ofObject 方法,建立一個 TypeEvaluator 做爲參數。以後咱們能夠經過重寫 TypeEvaluator 的 evaluate 方法,來按照本身的規則返回具體的值。
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator() {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
//計算
return null; //返回值
}
});
複製代碼
咱們來看一下 evaluate 方法的幾個參數
經過這三個值,咱們就能夠通過計算產生全部咱們想要的值。
其實,經過 TypeEvaluator,咱們不光能產生普通的數據,還能結合泛型,咱們還能定義更加複雜的數據:
咱們能夠在建立 TypeEvaluator 時指定具體類型,來達到更豐富的效果。好比這裏就用到了一個名爲 PointF 的數據類型:
ValueAnimator animator = ValueAnimator.ofObject(new TypeEvaluator<PointF>() {
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
//計算
return null; //返回值
}
});
複製代碼
最後送福利了,如今關注我而且加入羣聊能夠獲取包含源碼解析,自定義View,動畫實現,架構分享等。 內容難度適中,篇幅精煉,天天只需花上十幾分鍾閱讀便可。 你們能夠跟我一塊兒探討,歡迎加羣探討,有flutter—性能優化—移動架構—資深UI工程師 —NDK相關專業人員和視頻教學資料,還有更多面試題等你來拿~ 羣號:661841852