Android 動畫實現

劉強東發表於2019-02-27

動畫的分類

主要分類:

  • 傳統動畫(View Animation)
  • 幀動畫(Drawable Animation)
  • 屬性動畫(Property Animation)

傳統動畫

傳統動畫即指的android3.0之前的動畫實現. 只有介面的變化, 但是位置屬性(即螢幕座標)不發生改變.

關鍵類

  • Animation.AnimationListener 動畫事件監聽器

  • Interpolator 動畫插值器

  • Animation 動畫的抽象類

  • AnimationUtils 動畫工具類

  • AnimationDrawable 幀動畫

動畫都是支援Java程式碼和XML佈局兩種方式建立動畫效果的.

java類名 xml關鍵字 描述資訊
AlphaAnimation <alpha> 放置在res/anim/目錄下 漸變透明度動畫效果
RotateAnimation <rotate> 放置在res/anim/目錄下 畫面轉移旋轉動畫效果
ScaleAnimation <scale> 放置在res/anim/目錄下 漸變尺寸伸縮動畫效果
TranslateAnimation <translate> 放置在res/anim/目錄下 畫面轉換位置移動動畫效果
AnimationSet <set> 放置在res/anim/目錄下 一個持有其它動畫元素alpha、scale、translate、rotate或者其它set元素的容器

Animation

Animation是所有的傳統動畫的抽象基類; 所以介紹下所有動畫的通用屬性和方法

屬性 方法 描述
android:detachWallpaper setDetachWallpaper(boolean) 是否允許在桌布上執行
android:duration setDuration(long) 動畫持續時間,毫秒為單位
android:fillAfter setFillAfter(boolean) 控制元件動畫結束時是否保持動畫最後的狀態
android:fillBefore setFillBefore(boolean) 控制元件動畫結束時是否還原到開始動畫前的狀態
android:fillEnabled setFillEnabled(boolean) 與android:fillBefore效果相同
android:interpolator setInterpolator(Interpolator) 設定插值器(指定的動畫效果,譬如回彈等)
android:repeatCount setRepeatCount(int) 重複次數, 如果為1會執行兩次動畫
android:repeatMode setRepeatMode(int) 重複模式
android:startOffset setStartOffset(long) 呼叫start函式之後等待開始執行的時間,單位為毫秒
android:zAdjustment setZAdjustment(int) 表示被設定動畫的內容執行時在Z軸上的位置(top/bottom/normal),預設為normal

注意

  1. 類名和在XML中的標籤名字不一樣
  2. 如果FillAfter和FillBefore同時為true, After有效. 都不設定預設是Before效果

沒有對應屬性的Animation函式

void start ()
// 開始動畫(只能在getTransition)

void cancel ()
// 結束動畫(如果當前動畫正在執行中)

void reset ()
// 如果結束了一個動畫想再次開啟動畫

void setBackgroundColor (int bg)

void setInterpolator (Context context, 
                int resID)
// 載入resource中的interpolator檔案
複製程式碼

設定動畫事件的監聽器

animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        // 動畫開始
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        // 動畫結束
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
        //動畫重複, 如果執行setRepeat方法就會在每次載入動畫的時候都回撥該方法
    }
});
複製程式碼

View

關於設定View的動畫函式

void setAnimation (Animation animation)
// 通過此函式設定的動畫需要父容器呼叫inviliData()才會執行

void startAnimation (Animation animation)
// 設定動畫並且立即執行

void clearAnimation ()

Animation getAnimation ()

void postInvalidateOnAnimation (int left, 
                int top, 
                int right, 
                int bottom)

void postInvalidateOnAnimation ()

void postOnAnimation (Runnable action)

void postOnAnimationDelayed (Runnable action, 
                long delayMillis)
複製程式碼

View自帶的動畫生命週期

void onAnimationStart ()

void onAnimationEnd ()
複製程式碼

AlphaAnimation

設定透明動畫效果

xml屬性 解釋
android:fromAlpha 動畫開始的透明度 百分比(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlpha 動畫結束的透明度,同上

建構函式建立動畫

AlphaAnimation (float fromAlpha, 
                float toAlpha)

AlphaAnimation (Context context, 
                AttributeSet attrs)
複製程式碼

載入XML動畫

 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_set);
        mButton.startAnimation(animation);
複製程式碼

RotateAnimation

設定旋轉動畫效果

xml屬性 解釋
android:fromDegrees 旋轉開始角度,正代表順時針度數,負代表逆時針度數
android:toDegrees 旋轉結束角度,正代表順時針度數,負代表逆時針度數
android:pivotX 縮放起點X座標
android:pivotY 縮放起點Y座標,同上規律

注意: pivot支援三種屬性值, 其實還有一些其他動畫也支援這三種屬性值.往後看

  • 50

    表示以View的左上角偏移寬或高50畫素點為原點

  • 50%

    表示以View的左上角偏移View自身寬或高的百分之五十的畫素點為原點

  • 50%p

    表示以View的左上角偏移View的父容器寬或高的百分之五十的畫素點為原點

同樣可以直接使用構造方法在程式碼建立

RotateAnimation (Context context, 
                AttributeSet attrs)

RotateAnimation (float fromDegrees, 
                float toDegrees)

RotateAnimation (float fromDegrees, 
                float toDegrees, 
                float pivotX, 
                float pivotY)

RotateAnimation (float fromDegrees, 
                float toDegrees, 
                int pivotXType,  // 該引數就是為了實現XML屬性中的三個值, 後面如果有構造方法有這個引數不再講
                float pivotXValue, 
                int pivotYType, 
                float pivotYValue)
複製程式碼

關於pivotType值

  • Animation.RELATIVE_TO_SELF 相當與XML的50%
  • Animation.RELATIVE_TO_PARENT 相當於XML的50%p
  • Animation.ABSOLUTE 相當於50

TranslateAnimation

設定平移動畫效果

屬性:Android 動畫實現

分別對應平移的起始座標和終點座標, 同樣支援三種屬性值.

構造方法

TranslateAnimation (Context context, 
                AttributeSet attrs)

TranslateAnimation (float fromXDelta, 
                float toXDelta, 
                float fromYDelta, 
                float toYDelta)

TranslateAnimation (int fromXType, 
                float fromXValue, 
                int toXType, 
                float toXValue, 
                int fromYType, 
                float fromYValue, 
                int toYType, 
                float toYValue)
複製程式碼

關於type(和RotateAnimation類似):

  • Animation.RELATIVE_TO_SELF 相當與XML的50%
  • Animation.RELATIVE_TO_PARENT 相當於XML的50%p
  • Animation.ABSOLUTE 相當於50

ScaleAnimation

設定縮放動畫效果

屬性: Android 動畫實現

對應:

  1. 縮放起點座標
  2. 縮放中心座標
  3. 縮放終點座標
  1. XML中不支援浮點數設定百分比(除alpha以外)
  2. XML中不支援Animation的屬性程式碼補全
  3. ScaleAnimation在XML中支援DP值
  4. 在程式碼中設定百分比全部使用浮點數表示(1表示100%)

AnimationSet

可以設定多個動畫效果同時播放

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"  android:duration="500">

    <translate
        android:duration="2000"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:repeatCount="-1"
        android:toXDelta="100"
        android:toYDelta="100" >
    </translate>

    <scale
        android:duration="2000"
        android:fromXScale="0"
        android:fromYScale="0"
        android:repeatCount="-1"
        android:toXScale="1"
        android:toYScale="1" >
    </scale>


    <rotate
        android:duration="2000"
        android:fromDegrees="0"
        android:pivotX="0"
        android:pivotY="100"
        android:repeatCount="-1"
        android:toDegrees="360" >
    </rotate>

    <alpha
        android:duration="2000"
        android:fromAlpha="1"
        android:repeatCount="-1"
        android:toAlpha="0" >
    </alpha>

</set>
複製程式碼

程式碼設定動畫集合

Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_set);
mButton.startAnimation(animation);
複製程式碼

需要注意的是AnimationSet不支援repeatCount屬性(也就是說不能直接支援迴圈動畫). 不過可以通過上面這種給動畫單獨增加迴圈屬性(程式碼設定重複播放也不行);

Tip: 檢視動畫集只支援同時播放多個動畫. 不支援鏈式播放動畫(除非自己監聽動畫完成)

通過函式新增動畫

void addAnimation (Animation a)
複製程式碼

AnimationUtils

前面已經使用了該類進行載入xml動畫. 現在著重介紹

主要作用

  • 載入xml動畫/插值器
  • 提供幾種動畫效果

然後通過View.startAnimation()設定到檢視上播放

// 當前動畫已執行時間, 毫秒值單位
long currentAnimationTimeMillis () 

// 載入動畫
Animation loadAnimation (Context context, 
                int id)

// 載入插值器
Interpolator loadInterpolator (Context context, 
                int id)

// 載入佈局動畫
LayoutAnimationController loadLayoutAnimation (Context context, 
                int id)
複製程式碼

生成自帶的動畫效果(上下左右/移出動畫)

// 生成一個從左到原座標的滑動且漸隱效果的動畫
Animation makeInAnimation (Context c, 
                boolean fromLeft) // 是否從左邊進入, 否則右邊進入

// 生成一個從下到上的滑動漸隱效果動畫
Animation makeInChildBottomAnimation (Context c)

// 生成一個漸隱動畫並且向左/右移出介面的動畫
Animation makeOutAnimation (Context c, 
                boolean toRight)
複製程式碼

幀動畫

幀動畫屬於載入 Drawable 資源來播放動畫,就像Gif圖片一樣一幀一幀載入多個連貫圖片形成動畫效果. 由於需要載入多張圖片一般都體積佔用比較大的且容易影響流暢度, 但是製作起來很方便效果也可以做的很複雜. 注意OOM記憶體溢位.

一幀:就是一張圖片(Drawable)

幀動畫對應類即AnimationDrawable, 是Drawable的子類, 所以可以作為View的背景設定(BackgroundDrawable).

示例

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  ButterKnife.bind(this);
mAnimationDrawable = new AnimationDrawable(); // 建立動畫

// 新增幀圖片
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_20_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_30_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_50_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_60_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_80_black_24dp), 200);
mAnimationDrawable.addFrame(getDrawable(R.drawable.ic_battery_90_black_24dp), 200);

// 將動畫物件設定給imageView物件作為背景, 你在佈局檔案中設定背景也行
mIvFrame.setBackground(mAnimationDrawable);

}

@OnClick({R.id.btn_start_animation, R.id.btn_stop_animation})
public void onClick(View view) {
  switch (view.getId()) {
    case R.id.btn_start_animation:
    	mAnimationDrawable.start(); // 開始動畫
    break;
    case R.id.btn_stop_animation:
    	mAnimationDrawable.stop(); // 停止動畫, 該方法會自動判斷是否在執行動畫
    break;
  }
}
複製程式碼

除了程式碼中建立還可以通過在Drawable目錄中建立xml檔案. 使用節點animation-list.

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_battery_20_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_30_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_50_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_60_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_80_black_24dp" android:duration="200"/>
    <item android:drawable="@drawable/ic_battery_90_black_24dp" android:duration="200"/>
</animation-list>
複製程式碼

通過xml建立後還是需要在程式碼中顯示動畫的.

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  ButterKnife.bind(this);

  // AnimationDrawable是Drawable的子類, 可以把它看做一張圖片. 就像Gif由多張圖片組成一張圖片一樣
  mAnimationList = (AnimationDrawable) getDrawable(R.drawable.animation_frame);
  mIvFrame.setBackground(mAnimationList); // 還是一樣把動畫當作圖片設為背景
  // mIvFrame.setImageDrawable(mAnimationDrawable); 當然不止是背景

}

@OnClick({R.id.btn_start_animation, R.id.btn_stop_animation})
public void onClick(View view) {
  switch (view.getId()) {
    case R.id.btn_start_animation:
      mAnimationList.start();
      break;
    case R.id.btn_stop_animation:
      mAnimationList.stop();
      break;
  }
}
複製程式碼

AnimationDrawable

新增幀
void addFrame (Drawable frame,  // 一幀的圖片
                int duration) // 一幀所持續時間, 毫秒單位
複製程式碼
得到持續時間
int getDuration (int i)
複製程式碼
得到幀圖片
Drawable getFrame (int index) // 得到逐幀動畫中指定索引的圖片Drawable物件, 索引從0開始
複製程式碼
得到總幀數
int getNumberOfFrames () // 就是你給逐幀動畫設定了幾張圖片. 6張就是返回6.並不是從0開始
複製程式碼
填充xml

該方法是將xml檔案填充進物件. 在Android5.0(API21)新增,用的很少.而且直接將xml設為背景更加好用. 幾個引數涉及到自定義屬性我會另開文章講解.

void inflate (Resources r,  
                XmlPullParser parser, 
                AttributeSet attrs,  
                Resources.Theme theme)
複製程式碼
執行持續時間
void setOneShot (boolean oneShot) // 設定是否單次播放, 預設為false. 即永久迴圈執行
boolean isOneShot () // 是否是單次播放
複製程式碼
是否正在播放動畫
boolean isRunning ()	
複製程式碼
開始和停止
void start () // 官方不建議在Activity的onCreate()內執行, 但是我執行沒出啥問題
void stop () 
void run ()	// 也可以開始動畫, 不過官方建議用start()
複製程式碼
重啟和暫停動畫

雖然名字是顯示和隱藏, 但是動畫的隱藏類似於暫停動畫一樣, 畢竟幀動畫依附於View上終究還是會顯示一幀在螢幕上. 所以我覺得更像暫停動畫.

boolean setVisible (boolean visible, // 是否顯示或者隱藏
                boolean restart) // 是否重新開始動畫
複製程式碼

注意的是: restart為true或者從隱藏到顯示都會導致逐幀動畫的重新開始. 所以暫停後繼續播放也不能實現.

布林返回值: 如果當前狀態與上次狀態不一致則會返回true, 反正false. 例如: 從顯示到隱藏為false, 重新開始為false.

插值器

Interpolator 動畫插值器是用來控制動畫的速度變化.

屬性動畫和檢視動畫都相容同樣的動畫插值器.

Android 動畫實現

關鍵類:

TypeEvaluator(型別估值器)

所有的插值器有一個頂級的介面: Interpolator

xml 描述
AccelerateDecelerateInterpolator @android:anim/accelerate_decelerate_interpolator 動畫始末速率較慢,中間加速
AccelerateInterpolator @android:anim/accelerate_interpolator 動畫開始速率較慢,之後慢慢加速
AnticipateInterpolator @android:anim/anticipate_interpolator 類似彈弓, 先往後拉然後移動到指定座標;
AnticipateOvershootInterpolator @android:anim/anticipate_overshoot_interpolator 類似上面, 但是向後拉的時候有阻尼效果;
BounceInterpolator @android:anim/bounce_interpolator 到達指定值時會有彈起效果;
CycleInterpolator @android:anim/cycle_interpolator 先到達指定位置然後, 後退等值, 然後再恢復到原有值;
DecelerateInterpolator @android:anim/decelerate_interpolator 動畫開始快然後慢
LinearInterpolator @android:anim/linear_interpolator 動畫勻速改變
OvershootInterpolator @android:anim/overshoot_interpolator 向前彈出一定值之後回到原來位置
PathInterpolator 路徑 新增,定義路徑座標後按照路徑座標來跑。

在android5.0後新增三個interpolator

  • FastOutLinearInInterpolator(加速插值器)

    和AccelerateInterpolator的區別在於初始加速更快

    Android 動畫實現

  • FastOutSlowInInterpolator(加速減速插值器)

    起始速度更快

    Android 動畫實現

  • LinearOutSlowInInterpolator(持續減速)

    Android 動畫實現

如果你不滿足於系統自帶的插值器可以實現TimeInterpolator介面自定義

插值器曲線圖

關鍵詞:

  • Anticipate 回拉, 即在開始進入動畫軌跡之前先回拉以下
  • Overshoot 越界, 超出動畫軌跡一定距離
  • Decelerate 減速
  • Accelerate 加速
  • Linear 均速
  • Bounce 彈力
  • Cycle 反覆 相當於AccelerateDecelerateInterpolator效果反覆執行

屬性動畫

之前不是說了傳統動畫播放位置不變只是檢視發生改變嗎. 在Android3.0以後為了解決這個問題Google就引入了屬性動畫. 動畫效果的變化會導致控制元件的位置屬性發生改變.

屬性動畫是基於反射的原理獲取物件的屬性欄位修改(偏移,透明,旋轉). 例如修改View的setXX()方法就需要傳入XX作為引數(PropertyName), 所以並沒有類似Animation的ScaleAnimation或者TranslateAnimation;

關鍵類:

  • Animator 抽象類
    • AnimatorSet 屬性動畫集
      • AnimatorSet.Builder 構建更復雜的動畫集
    • ValueAnimator 屬性動畫
      • ObjectAnimator 物件動畫
      • TimeAnimator 時間動畫

其他類

  • AnimatorInflater 動畫填充器
  • AnimatorListener 屬性動畫監聽器
    • AnimatorListenerAdapter 屬性動畫監聽器的介面卡
  • AnimatorPauseListener 動畫可見監聽器

示例

需要在res/animator目錄中建立XML檔案

實際上屬性動畫放在anim目錄下依舊能夠載入執行, 但是會報警告

屬性動畫XML支援三種標籤:(xml標籤及對應的Java類)

  1. objectAnimator (ObjectAnimator)
  2. animator (ValueAnimator)
  3. set (AnimatorSet)

XML建立動畫

屬性動畫的XML屬性支援不多

Android 動畫實現

但是物件動畫支援的屬性很多, 並且可以直接針對View進行動畫播放;

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="2000"
                android:propertyName="rotationY"
                android:valueFrom="0"
                android:valueTo="360"
                android:valueType="floatType">
</objectAnimator>
複製程式碼

全部屬性(全部支援程式碼補全, 是不是比Animation的XML用起來方便):

Android 動畫實現

依次含義:

  1. 持續時間

  2. 插值器

  3. pathData 要求必須api21以上

  4. 屬性名

    字串型別的值, 填入View的setXXX方法中的XXX字串

  5. 屬性值

  6. propertyXName 要求必須api21以上

  7. propertyYName 要求必須api21以上

  8. 重複次數

  9. 重複模式

  10. 延遲時間

  11. 屬性起始值

  12. 屬性結束值

  13. 屬性型別 (ColorType/FloatType/IntType/PathType)

pathData/propertyXName/propertyYName這三個屬性是搭配使用; 同時都要求Api21以上才有效;

具體用法我也不知道;

載入XML檔案到檢視上

Animator animator = AnimatorInflater.loadAnimator(getApplicationContext(), 
                                                  R.animator.objectAnimator);
animator.setTarget(mImageView);
animator.start();
複製程式碼

程式碼

ObjectAnimator anim = ObjectAnimator.ofFloat(v, "scaleX", 2f);//x方向放大兩倍
anim.setDuration(1000);//時長
anim.setRepeatCount(2);//重複次數
anim.setRepeatMode(ValueAnimator.REVERSE);//重複時相反的方向恢復到原來的樣子
anim.setInterpolator(new AccelerateInterpolator());//設定插值器為加速度
anim.start();
複製程式碼

ValueAnimator同樣支援在XML中建立動畫配置, 用法一致, 但是支援的屬性比較少;

Android 動畫實現

Animator

這是所有屬性動畫的父抽象類, 並不能直接使用.

// 複製動畫
Animator clone ()

// 延遲時間
void setStartDelay (long startDelay)
long getStartDelay ()

// 動畫單次執行耗時
Animator setDuration (long duration)

// 動畫全部完成所需時間,迴圈次數*單次耗時 如果是無限迴圈動畫返回-1(欄位:DURATION_INFINITE)
long getTotalDuration ()

// 設定插值器
void setInterpolator (TimeInterpolator value)

// 設定動畫執行的目標
void setTarget (Object target)
複製程式碼

控制動畫播放

控制動畫的生命週期比Animation更加自由;

// 開始動畫
void start ()
    
// 暫停動畫
void pause ()

// 繼續動畫
void resume ()

void end ()
// 結束動畫

void cancel ()
// 取消動畫和結束動畫的區別在於會多執行一個函式onCancel
複製程式碼

動畫狀態

// 是否不可見狀態
boolean isPaused ()

// 動畫當前是否正在執行
boolean isRunning ()

boolean isStarted ()
複製程式碼

生命週期監聽器

// 新增動畫監聽器
void addListener (Animator.AnimatorListener listener)

// 新增動畫可見監聽器
void addPauseListener (Animator.AnimatorPauseListener listener)
    
// 得到全部的監聽器
ArrayList<Animator.AnimatorListener> getListeners ()

// 刪除全部監聽器
void removeAllListeners ()
void removeListener (Animator.AnimatorListener listener)
void removePauseListener (Animator.AnimatorPauseListener listener)
複製程式碼

示例

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) {
	// 動畫重複
  }
});
複製程式碼

暫停和繼續監聽器

(AnimatorPauseListener) 在動畫處於暫停或者繼續狀態回撥的監聽器

void onAnimationPause (Animator animation)

void onAnimationResume (Animator animation)
複製程式碼

新增監聽器.看Animato的如下方法

void addPauseListener (Animator.AnimatorPauseListener listener)
複製程式碼

動畫開始值和結束值

這兩個方法會根據執行的具體是哪個類而有所區別.

例如ValueAnimator會無效,因為其沒有屬性資訊. 而AnimationSet則會傳遞給子動畫(當子動畫沒有設定值時).

ObjectAnimator則會根據屬性名改變該屬性值.

// 動畫開始和結束值
void setupStartValues ()
void setupEndValues ()
複製程式碼

結束或者取消動畫, 只是回撥方法存在不一致而已

// 結束動畫
void end ()

// 取消動畫
void cancel ()
複製程式碼

都可以取消動畫, 但是在動畫監聽器的回撥方法不一致

  • end

    回撥onAnimationEnd方法

  • cancel

    回撥onAnimationEnd和onAnimtionCacel方法

ValueAnimator

這只是一種值運算, 提供更加靈活的過渡效果

例如在1000毫秒內 由開始值0到結束值300的過渡變化(變化速率遵守預設插值器AccelerateDecelerateInterpolator) 當然你也可以自己設定插值器.

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 100f, 300f);

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override public void onAnimationUpdate(ValueAnimator animation) {
    Log.i("日誌", "值變化: "+ animation.getAnimatedValue());
  }
});

valueAnimator.start();
複製程式碼

輸出結果:

Android 動畫實現

ValueAnimator可以理解為擁有動畫的特點(duration/interpolator), 只是提供計算一個平滑的動畫過渡值而已, 但是如果改變物件的什麼屬性(邊距/透明度/顏色值/寬高)就全憑你自定義了. 這就是為什麼說屬性動畫可以控制的內容是任意物件了.

ofFloat方法填入引數是一個可變引數, 只要填入固定值, 必定會以第一個引數開始最後一個引數結尾. 然後中間的引數會執行到.

通過ofxx()等方法來建立ValueAnimator例項. 這一系列方法會產生預設的值過渡效果.

ValueAnimator ofArgb (int... values)

ValueAnimator ofFloat (float... values)

ValueAnimator ofInt (int... values)

ValueAnimator ofObject (TypeEvaluator evaluator, 
                Object... values)

// 通過屬性值固定器來建立
ValueAnimator ofPropertyValuesHolder (PropertyValuesHolder... values)
複製程式碼

屬性值變化監聽器

void addUpdateListener (ValueAnimator.AnimatorUpdateListener listener)

void removeUpdateListener (ValueAnimator.AnimatorUpdateListener listener)

void removeAllUpdateListeners ()
複製程式碼

ValueAnimator的監聽器中會回撥引數ValueAnimator可以通過此來獲取動畫不斷變化的屬性值;

// 得到動畫執行過程中不斷變化的屬性值
Object getAnimatedValue ()

// 根據屬性名來獲取屬性值
Object getAnimatedValue (String propertyName)

// 得到一個屬性值固定器
PropertyValuesHolder[] getValues ()
複製程式碼

百分比(fraction)這個在ValueAnimator中代表完成度.


// 範圍0~1, 當前時間在總時間的進度百分比
float getAnimatedFraction ()
複製程式碼

動畫播放時長

long getCurrentPlayTime ()
// 當前動畫已播放時長

long getTotalDuration ()
// 動畫全部播放完所需時長

long getFrameDelay ()
複製程式碼

TypeEvaluator

通過ValueAnimator的setEvaluator()方法設定TypeEvaluator

void setEvaluator (TypeEvaluator value)
複製程式碼

和插值器interpolator控制的速度變化不同的是 , 型別估值器, 主要是控制ValueAnimator的開始值和起始值的具體值.

其內部方法返回值決定了getAnimatedValue()的值.

預設提供的TypeEvaluator

  • TypeEvaluator:使用者自定義屬性值需要實現的介面
  • IntEvaluator:整數屬性值
  • FloatEvaluator:浮點數屬性值
  • ArgbEvaluator:十六進位制color屬性值
  • RectEvaluator: 矩形屬性值

在api21後新增型別估值器

  • PointFEvaluator: 座標屬性值
  • FloatArrayEvaluator
  • IntArrayEvaluator
// 自定義 HslEvaluator
private class HsvEvaluator implements TypeEvaluator<Integer> {  
   float[] startHsv = new float[3];
   float[] endHsv = new float[3];
   float[] outHsv = new float[3];

   @Override
   public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
       // 把 ARGB 轉換成 HSV
       Color.colorToHSV(startValue, startHsv);
       Color.colorToHSV(endValue, endHsv);

       // 計算當前動畫完成度(fraction)所對應的顏色值
       if (endHsv[0] - startHsv[0] > 180) {
           endHsv[0] -= 360;
       } else if (endHsv[0] - startHsv[0] < -180) {
           endHsv[0] += 360;
       }
       outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
       if (outHsv[0] > 360) {
           outHsv[0] -= 360;
       } else if (outHsv[0] < 0) {
           outHsv[0] += 360;
       }
       outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
       outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;

       // 計算當前動畫完成度(fraction)所對應的透明度
       int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);

       // 把 HSV 轉換回 ARGB 返回
       return Color.HSVToColor(alpha, outHsv);
   }
}

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xff00ff00);  
// 使用自定義的 HslEvaluator
animator.setEvaluator(new HsvEvaluator());  
animator.start();  
複製程式碼

ObjectAnimator

由於ValueAnimator並不能直接控制View的動畫(只能在監聽器中手動設定); 而View載入動畫時最常見的的操作, 所以Google重寫了ValueAnimator就出現了ObjectAnimator. 一個可以通過反射直接修改View物件的屬性的類;

正是因為其反射的原理所以需要set<PropertyName>()以及get<PropertyName>()的屬性才能修改.

如果沒有setter和getter函式有三種解決辦法:

  • 繼承View新增這兩個函式
  • 使用一個包裝類
  • 使用ValueAnimator替代

Tip: 如果指定了開始值可以不用擁有get<PropertyName>()方法

Android 動畫實現

建立動畫

同樣是ofxx()函式建立動畫但是和ValueAniamtor很大的區別就是支援直接setTarget設定目標;

顏色

該函式限制api21

ObjectAnimator ofArgb (Object target, 
                String propertyName, 
                int... values)

ObjectAnimator ofArgb (T target, 
                Property<T, Integer> property, 
                int... values)
複製程式碼

ObjectAnimator設定屬性值的時候還有一個型別問題. 就是物件的該屬性是什麼型別, 你在ObjectAnimator中指定開始值和起始值的時候就要用什麼型別. 否則將沒有效果.

自定義物件
ObjectAnimator ofObject (T target, 
                Property<T, V> property, 
                TypeEvaluator<V> evaluator, 
                V... values)

ObjectAnimator ofObject (Object target, 
                String propertyName, 
                TypeConverter<PointF, ?> converter, 
                Path path)
// 限制api21

ObjectAnimator ofObject (T target, 
                Property<T, V> property, 
                TypeConverter<PointF, V> converter, 
                Path path)

ObjectAnimator ofObject (T target, 
                Property<T, P> property, 
                TypeConverter<V, P> converter, 
                TypeEvaluator<V> evaluator, 
                V... values)

ObjectAnimator ofObject (Object target, 
                String propertyName, 
                TypeEvaluator evaluator, 
                Object... values)
複製程式碼
ObjectAnimator ofFloat (Object target, 
                String xPropertyName, 
                String yPropertyName, 
                Path path)

ObjectAnimator ofFloat (T target, 
                Property<T, Float> property, 
                float... values)

ObjectAnimator ofFloat (T target, 
                Property<T, Float> property, 
                float... values)

ObjectAnimator ofFloat (Object target, 
                String propertyName, 
                float... values)
複製程式碼
int值
ObjectAnimator ofInt (T target, 
                Property<T, Integer> xProperty, 
                Property<T, Integer> yProperty, 
                Path path)

ObjectAnimator ofInt (T target, 
                Property<T, Integer> property, 
                int... values)

ObjectAnimator ofInt (Object target, 
                String propertyName, 
                int... values)

ObjectAnimator ofInt (Object target, 
                String xPropertyName, 
                String yPropertyName, 
                Path path)
複製程式碼
Multi

所有函式限制api21

ObjectAnimator ofMultiFloat (Object target, 
                String propertyName, 
                float[][] values)

ObjectAnimator ofMultiFloat (Object target, 
                String propertyName, 
                Path path)

ObjectAnimator ofMultiFloat (Object target, 
                String propertyName, 
                TypeConverter<T, float[]> converter, 
                TypeEvaluator<T> evaluator, 
                T... values)

ObjectAnimator ofMultiInt (Object target, 
                String propertyName, 
                int[][] values)

ObjectAnimator ofMultiInt (Object target, 
                String propertyName, 
                Path path)

ObjectAnimator ofMultiInt (Object target, 
                String propertyName, 
                TypeConverter<T, int[]> converter, 
                TypeEvaluator<T> evaluator, 
                T... values)
複製程式碼

PropertyValuesHolder

ObjectAnimator和ValueAnimator都擁有一個設定PropertyValuesHolder方法;

能儲存多個動畫屬性並且動畫播放時可以同時執行多個動畫;

ObjectAnimator ofPropertyValuesHolder (Object target, 
                PropertyValuesHolder... values)

ValueAnimator ofPropertyValuesHolder (PropertyValuesHolder... values)
複製程式碼

介紹下PropertyValuesHolder方法. 可變引數的使用和ObjectAnimator一樣.

PropertyValuesHolder ofFloat (String propertyName, 
                float... values)

PropertyValuesHolder ofInt (String propertyName, 
                int... values)

PropertyValuesHolder ofMultiFloat (String propertyName, 
                float[][] values)

PropertyValuesHolder ofMultiInt (String propertyName, 
                int[][] values)
複製程式碼

示例:

PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(pvh1, pvh2, pvh3).setDuration(1000).start();
複製程式碼

PropertyValueHolder還可以在XML標籤中定義

<animator xmlns:android="http://schemas.android.com/apk/res/android"
                android:duration="1000"
                android:repeatCount="1"
                android:repeatMode="reverse">
    <propertyValuesHolder android:propertyName="x" android:valueTo="400"/>
    <propertyValuesHolder android:propertyName="y" android:valueTo="200"/>
</animator>
複製程式碼

KeyFrame

PropertyValuesHolder還可以設定關鍵幀來控制動畫播放的進度;

可以將屬性拆成多段執行;

PropertyValuesHolder ofKeyframe (String propertyName, 
                Keyframe... values)

PropertyValuesHolder ofKeyframe (Property property, 
                Keyframe... values)

void setKeyframes (Keyframe... values)
複製程式碼

Property屬於包含name和type的屬性物件

XML設定

<propertyValuesHolder android:propertyName="progress" >
    <keyframe/>
    <keyframe android:fraction="0.2"
              android:interpolator="@android:anim/accelerate_interpolator"
              android:value="300"/>
    <keyframe android:interpolator="@android:anim/accelerate_interpolator"
              android:value="1000" />
</propertyValuesHolder>
複製程式碼

KeyFrame函式

static Keyframe	ofFloat(float fraction)

static Keyframe	ofInt(float fraction)
    
static Keyframe	ofObject(float fraction)
    
    
static Keyframe	ofFloat(float fraction, float value)

static Keyframe	ofInt(float fraction, int value)

static Keyframe	ofObject(float fraction, Object value)
複製程式碼
abstract Keyframe	clone()

float	getFraction()

Class	getType()
// 值型別

abstract Object	getValue()
// 取值

boolean	hasValue()
// 是否存在值
    
void	setFraction(float fraction)
// 設定完成百分比

void	setInterpolator(TimeInterpolator interpolator)
TimeInterpolator	getInterpolator()

abstract void	setValue(Object value)
複製程式碼

路徑動畫

ObjectAnimator還可以實現動畫播放路徑控制

根據繪製的路徑Path移動控制元件動畫. 屬於ObjectAnimator動畫的擴充套件.

Android 動畫實現
// 觸發點選事件
public void startAnimator(View view) {
    // 繪製路徑
    Path path = new Path();
    path.moveTo(100, 100);
    path.lineTo(200, 100);
    path.lineTo(200, 200);
    path.lineTo(300, 300);
    path.lineTo(50, 50);
    
    // 建立浮動屬性動畫
    ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.X, View.Y, path);
    animator.setInterpolator(new AccelerateDecelerateInterpolator());
    animator.setDuration(3000);
    animator.start();
}
複製程式碼

AnimatorSet

屬性動畫集合控制子動畫的播放時機比檢視動畫集合更加靈活自如; 繼承Animator擁有屬性動畫基本功能;

XML標籤

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially">
    <objectAnimator
        android:duration="3000"
        android:propertyName="rotationX"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:startOffset="0"
        android:valueFrom="0"
        android:valueTo="90"
        android:valueType="intType" />

</set>
複製程式碼

set標籤只擁有一個屬性, android:ordering 表示集合中的動畫執行的時機, sequentially 依次執行 together 同時執行

然後引用標籤

Animator animator = AnimatorInflater.loadAnimator(this, R.animator.animator_set);
animator.setTarget(mButton);
animator.start();
複製程式碼

程式碼中同樣可以如此設定

void playTogether (Animator... items)

void playSequentially (Animator... items)
複製程式碼

這兩個方法的引數都是可變引數, 可以傳入N個動畫物件來播放.

以下是兩個屬性動畫集示例

同時播放

    AnimatorSet set = new AnimatorSet();
    set.playTogether(
            ObjectAnimator.ofFloat(v, "x", startBounds.left, finalBounds.left))
            .with(ObjectAnimator.ofFloat(v, "y", startBounds.top, finalBounds.top))
            .with(ObjectAnimator.ofFloat(v, "scaleX", startScale, 1f))
            .with(ObjectAnimator.ofFloat(v, "scaleY", startScale, 1f));

    set.setDuration(200);
    set.setInterpolator(new DecelerateInterpolator());
    set.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            //
        }

        @Override
        public void onAnimationCancel(Animator animation) {
            //
        }
    });
    set.start();
複製程式碼

順序播放

    Animator scaleXAnimator = ObjectAnimator.ofFloat(mImageView, "scaleX", 1, 0.5f);
    scaleXAnimator.setDuration(2000);
    Animator scaleYAnimator = ObjectAnimator.ofFloat(mImageView, "scaleY", 1, 0.5f);
    scaleYAnimator.setDuration(2000);
    Animator rotationXAnimator = ObjectAnimator.ofFloat(mImageView, "rotationX", 0, 360);
    rotationXAnimator.setDuration(2000);
    Animator rotationYAnimator = ObjectAnimator.ofFloat(mImageView, "rotationY", 0, 360);
    rotationYAnimator.setDuration(2000);
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.play(scaleXAnimator)
            .with(scaleYAnimator)
            .before(rotationXAnimator)
            .after(rotationYAnimator);
    animatorSet.start();
複製程式碼

全部函式

// 依次播放和順序播放N個動畫
void playSequentially (List<Animator> items)
void playSequentially (Animator... items)
void playTogether (Collection<Animator> items)
void playTogether (Animator... items)



void setupStartValues ()
void setupEndValues ()
複製程式碼

AnimatorSet.Builder

屬性動畫集合有一個構造器, 可以用於建立順序執行的動畫集合

AnimatorSet.Builder play (Animator anim) // 建立一個構造器
複製程式碼

構造器函式

AnimatorSet.Builder	after(long delay)
// 延遲播放 

AnimatorSet.Builder	after(Animator anim)
// 向後新增動畫

AnimatorSet.Builder	before(Animator anim)
// 向前新增動畫

AnimatorSet.Builder	with(Animator anim)
// 同時播放動畫
複製程式碼

下面是官方文件示例:

執行順序:

  1. 最先播放 bounceAnim.
  2. 同時播放 squashAnim1, squashAnim2, stretchAnim1, and stretchAnim2
  3. 然後播放 bounceBackAnim.
  4. 最後播放 fadeAnim.
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();
複製程式碼

AnimatorInflater

和View動畫(使用一個AnimationUtils工具類)不同的是屬性動畫使用一個填充器來載入XML動畫資源.

只有兩個靜態方法.

Animator loadAnimator (Context context, 
                int id)

StateListAnimator loadStateListAnimator (Context context, 
                int id)
複製程式碼

相關文章