進階之路 | 奇妙的Animation之旅

前言

本文已經收錄到我的Github個人博客,歡迎大佬們光臨寒舍:

我的GIthub博客

學習清單:

  • 動畫的種類
  • 自定義View動畫
  • View動畫的特殊使用場景
  • 屬性動畫
  • 使用動畫的注意事項

一.為什麼要學習Animation?

筆者在之前進階之路 | 奇妙的View之旅中,提及View滑動的七種方式的時候簡單說到Animation,想必看過的讀者們已經對Animation有一個簡單的印象。

動畫,對於一個APP來說非常重要,現在市面上使用的用戶比較多的APP,無一不是採用了各種豐富多彩的動畫效果;在應用中善於使用動畫,不僅讓APP的體驗更上一層樓,還能牢牢抓住用戶的心!

而作為開發者的我們,一定要對動畫有一定深度的了解,在日常的學習或者工作中多多嘗試動畫,以提高應用程序的美觀度和易用性!

什麼,你不信動畫很重要….反手甩你一個對比視頻:過渡動畫有多重要?

動畫的分類

二.核心知識點歸納

2.1 View動畫

View動畫(視圖動畫)分為兩部分:

  • 補間動畫
  • 幀動畫

2.1.1 補間動畫

1 基礎知識

Q1:主要的變換效果

名稱 標籤 子類 效果
平移動畫 translate TranslateAnimation 移動View
縮放動畫 scale ScaleAnimation 放大或縮小View
旋轉動畫 rotate RotateAnimation 旋轉View
透明度動畫 alpha AlphaAnimation 改變View的透明度

注意:View動畫的View移動只是視覺效果,並不能真正的改變view的位置。

Q2:動畫的創建

對於View動畫建議採用XML來定義,因為XML可讀性更好

創建方法一:通過XML定義

  • XML文件創建在res/anim/
  • 根節點set,子節點translatescalerotatealpha,分別對應四種View動畫:
<?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android"      android:shareInterpolator="true"      android:fillAfter="true">        <translate          android:fromXDelta="float"          android:toXDelta="float"          android:fromYDelta="float"          android:toYDelta="float"/>       <scale          android:fromXScale="float"          android:toXScale="float"          android:fromYScale="float"          android:toYScale="float"          android:pivotX="float"          android:pivotY="float"/>       <rotate          android:fromDegrees="float"          android:toDegrees="float"          android:pivotY="float"          android:pivotX="float"/>      <alpha          android:fromAlpha="float"          android:toAlpha="float"/>    </set>

接下來分別解釋各個節點下屬性含義:

A.set:表示動畫集合,對應AnimationSet

  • interpolator:表示動畫集合所採用的插值器,影響動畫的速度。可以不指定,默認是accelerate_decelerate_interpolate(加速減速插值器)。下文屬性動畫會詳細介紹插值器的相關知識。
  • shareInterpolator:表示集合中的動畫是否和集合共享一個插值器。如果集合不指定插值器, 那麼子動畫就需要單獨制定所需的插值器或者使用默認值。
  • fillAfter:表示動畫結束時是否保持動畫結束時的狀態

B.translate:表示平移動畫,對應TranslateAnimation

  • android:fromXDelta:動畫起始時X坐標上的位置。
  • android:toXDelta:動畫結束時X坐標上的位置。
  • android:fromYDelta:動畫起始時Y坐標上的位置。
  • android:toYDelta:動畫結束時Y坐標上的位置。

注意:以上四個屬性以及後面幾個類似屬性的取值可能是數值、百分數、百分數p,各自含義是:

  • 50:以View左上角為原點沿坐標軸正方向偏移50px。
  • 50%:以View左上角為原點沿坐標軸正方向偏移View寬/高度的50%。
  • 50%p:以View左上角為原點沿坐標軸正方向偏移父(parent)控件寬/高度的50%。區別如圖:

各種偏移屬性取值的區別

C.scale:表示縮放動畫,對應ScaleAnimation

  • fromXScale:動畫起始時X坐標上的伸縮尺寸
  • toXScale:動畫結束時X坐標上的伸縮尺寸
  • fromYScale:動畫起始時Y坐標上的伸縮尺寸
  • toYScale:屬性為動畫結束時Y坐標上的伸縮尺寸

以上四個屬性值的值含義:

  • 值=0.0 :表示收縮到沒有

  • 值<1.0 :表示收縮

  • 值=1.0 :表示無伸縮

  • 值>1.0 :表示放大

  • pivotX:動畫相對於物件的X坐標的開始位置
  • pivotY:動畫相對於物件的Y坐標的開始位置

以上兩個屬性值表示縮放的軸點:從0%-100%中取值。

D.rotate:表示旋轉動畫,對應RotateAnimation類。

  • fromDegrees:動畫起始時物件的角度 (0度指X軸正方向所在方向)
  • toDegrees:動畫結束時物件旋轉的角度

以上兩個屬性共同確定旋轉方向,原則是:當角度(to-from)為數時表示逆時針旋轉,反之。

  • pivotY:動畫旋轉的軸點的X坐標
  • pivotX:動畫旋轉的軸點的Y坐標

E.alpha:表示透明度動畫,對應AlphaAnimation

  • fromAlpha:動畫起始時透明度
  • toAlpha:動畫結束時透明度

以上兩個屬性值:從0-1中取值。注意:

  • 值=0.0 :表示完全透明
  • 值=1.0 :表示完全不透明

以上四類補間動畫除了各自的特有屬性外,它們的共有屬性有:

共有屬性

XML聲明好之後,接下來只要在代碼中startAnimation(animation)開始動畫即可,代碼如下:

Animation animation = AnimationUtils.loadAnimation(this, R.anim.XXX);  mView.startAnimation(animation);

同時,可通過Animation的setAnimationListener(new AnimationListener(){...})給動畫添加過程監聽,這樣在動畫開始、結束和每一次循環時都可在回調方法中監聽到。接口代碼如下:

public static interface AnimationListener {          //動畫開始          void onAnimationStart(Animator animation);          //動畫結束          void onAnimationEnd(Animator animation);          //動畫重複          void onAnimationRepeat(Animator animation);      }

創建方法二: 通過Java代碼動態創建

  • 具體步驟:
    1. 創建TranslateAnimationRotateAnimationScaleAnimationAlphaAnimation對象。
    2. 設置創建的動畫對象的屬性,如動畫執行時間、延遲時間、起始位置、結束位置等
    3. 通過View.startAnimation()方法開啟動畫
    4. 可通過Animation.setAnimationListener()設置動畫的監聽器

Q3:綜合實例

A1:平移

//法一:xml定義  <?xml version="1.0" encoding="utf-8"?>  <translate xmlns:android="http://schemas.android.com/apk/res/android"             android:duration="2000"             android:fromXDelta="0"             android:fromYDelta="0"             android:toXDelta="100%"             android:toYDelta="100%">  </translate>    //在MainActivity中調用    Animation translateAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_translate);      mImageView.startAnimation(translateAnim);
//法二:java代碼創建 RELATIVE_TO_SELF表示相對自身View  TranslateAnimation translateAnimation = new TranslateAnimation(              Animation.RELATIVE_TO_SELF, 0,              Animation.RELATIVE_TO_SELF, 1,              Animation.RELATIVE_TO_SELF, 0,              Animation.RELATIVE_TO_SELF, 1);      translateAnimation.setDuration(2000);      mImageView.startAnimation(translateAnimation);  }

平移效果

A2:縮放

//法一:xml定義  <?xml version="1.0" encoding="utf-8"?>  <scale xmlns:android="http://schemas.android.com/apk/res/android"         android:duration="2000"         android:fromXScale="1.0"         android:fromYScale="1.0"         android:pivotX="50%"         android:pivotY="50%"         android:toXScale="0.5"         android:toYScale="0.5">  </scale>    //在MainActivity中調用     Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_scale);      mImage.startAnimation(scaleAnim);
//法二:java代碼創建  ScaleAnimation scaleAnimation = new ScaleAnimation(              1, 0.5f,              1, 0.5f,              Animation.RELATIVE_TO_SELF, 0.5f,              Animation.RELATIVE_TO_SELF, 0.5f);      scaleAnimation.setDuration(2000);      mImageView.startAnimation(scaleAnimation);

縮放效果

A3: 旋轉

//法一:xml定義  <?xml version="1.0" encoding="utf-8"?>  <rotate xmlns:android="http://schemas.android.com/apk/res/android"          android:duration="2000"          android:fillAfter="true"          android:fromDegrees="0"          android:toDegrees="360"          android:pivotX="50%"          android:pivotY="50%">  </rotate>    //在MainActivity中調用  Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_rotate);      mImageView.startAnimation(rotateAnim);
//法二:java代碼創建  RotateAnimation rotateAnimation = new RotateAnimation(              0, 360,              Animation.RELATIVE_TO_SELF, 0.5f,              Animation.RELATIVE_TO_SELF, 0.5f);      rotateAnimation.setDuration(2000);      mImageView.startAnimation(rotateAnimation);

效果:圖片在2s內以圖片中心為軸點,順時針旋轉360°,即完整轉一圈。

A4:透明度:

//法一:xml定義  <?xml version="1.0" encoding="utf-8"?>  <alpha xmlns:android="http://schemas.android.com/apk/res/android"         android:duration="2000"         android:fromAlpha="1.0"         android:toAlpha="0">  </alpha>    //在MainActivity中調用      Animation alphaAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_alpha);      mImageView.startAnimation(alphaAnim);
//法二:java代碼創建  AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0);      alphaAnimation.setDuration(2000);      mImageView.startAnimation(alphaAnimation);

效果:圖片在2s內從有到無。

A5:動畫集合:

//法一:xml定義  <?xml version="1.0" encoding="utf-8"?>  <set xmlns:android="http://schemas.android.com/apk/res/android"      android:shareInterpolator="true" >        <translate          android:duration="2000"          android:fromXDelta="0"          android:fromYDelta="0"          android:toXDelta="100%"          android:toYDelta="100%"> />      <scale         android:duration="2000"         android:fromXScale="1.0"         android:fromYScale="1.0"         android:pivotX="50%"         android:pivotY="50%"         android:toXScale="0.5"         android:toYScale="0.5" />      <rotate          android:duration="2000"          android:fromDegrees="0"          android:toDegrees="360"          android:pivotX="50%"          android:pivotY="50%"/>       <alpha         android:duration="2000"         android:fromAlpha="1.0"         android:toAlpha="0"/>  </set>    //在MainActivity中調用      Animation setAnim = AnimationUtils.loadAnimation(this, R.anim.view_anim_set);      mImageView.startAnimation(setAnim);

效果:以上四種動畫效果的疊加。圖片在2s內邊向右下角移動、邊縮小、邊旋轉、邊降低透明度至消失。


2 自定義動畫

實際項目中以上幾種動畫並不能滿足我們的需求,這時就需要自定義補間動畫

  • 步驟:

    1.繼承Animation

    2.重寫initialize()—>用於初始化

    3.重寫applyTransformation()—>用於進行矩陣變換

    經常需要藉助Camera來簡化矩陣變換

  • 實例:自定義補間動畫3D翻轉動畫


3 特殊使用場景

View動畫除了可作用在某個View對象上, 還可以用在特殊的場景,例如:

  • 控制ViewGroup子View出場效果

  • Activity切換效果

    接下來將依次介紹:

A1:子View出場動畫

  • 常用場景:ListViewGridViewRecyclerView
  • 對應類:LayoutAnimation
  • XML文件創建在res/anim/
  • 根節點layoutAnimation,常用屬性:
layoutAnimation      |- delay="float"      |- animationOrder="[normal|reverse | random]"      |- animation="[@anim/res_id]"

delay:表示子元素開始動畫的延遲時間

比如,子元素入場動畫的時間周期是300ms,那麼該屬性值=0.5就表示每個子元素都需要延遲150ms才能播放入場動畫。

animationOrder :表示子元素動畫的播放順序。可選模式:normal (正常順序)、random(隨機順序)、reverse(倒序)。

animation :為子元素指定具體的入場動畫。

  • 創建方法:

法一:xml定義,分兩步

step1:定義layoutAnimation動畫

// res/anim/anim_layout.xml  <layoutAnimation      xmlns:android="http://schemas.android.com/apk/res/android"      android:animation="@anim/anim_item"      android:delay="0.5"      android:animationOrder="normal">  </layoutAnimation>    // res/anim/anim_item.xml    效果:子項從右邊進入  <set      xmlns:android="http://schemas.android.com/apk/res/android"      android:duration="500"      android:shareInterpolator="true"      android:interpolator="@android:anim/accelerate_interpolator">      <alpha          android:fromAlpha="0"          android:toAlpha="1" />      <translate          android:fromXDelta="100%"          android:toXDelta="0"          />  </set>

step2:為ViewGroup設置android:layoutAnimation屬性, 這裡假設為Listview

  //activity_main.xml  <ListView          android:layout_width="match_parent"          android:layout_height="match_parent"          android:layoutAnimation="@anim/anim_layout"/>

法二:java代碼創建,通過LayoutAnimation類綁定

    // res/anim/anim_item.xml  <set      xmlns:android="http://schemas.android.com/apk/res/android"      android:duration="500"      android:shareInterpolator="true"      android:interpolator="@android:anim/accelerate_interpolator">      <alpha          android:fromAlpha="0"          android:toAlpha="1" />      <translate          android:fromXDelta="100%"          android:toXDelta="0"          />  </set>              
 //main.java  //和上述xml定義方法的效果相同  Animation animation = AnimationUtils.loadLayoutAnimation(this, R.anim.anim_item);  LayoutAnimationController controller = new LayoutAnimationController(animation);//對應android:animation屬性  controller.setDelay(0.5);//對應android:delay屬性  controller.setOrder(LayoutAnimationController.ORDER_NORMAL); //對應android:animationOrder屬性  listView.setLayoutAnimation(controller);//對應android:layoutAnimation屬性

A2:Activity的切換效果

  • 該xml文件創建在res/anim/
  • Activity默認是有切換效果的,若需要自定義切換效果,需要用到overridePendingTransition(int inAnim, int outAnim)方法
  • 參數含義:(進入的Activity所需進行的動畫id,退出的Activity所需進行的動畫id)
  • 該方法調用在startActivity()finish()才生效。例如:
startActivity(intent);  overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);

2.1.2 幀動畫

  • 幀動畫也是View動畫的一種,它會按照順序播放一組預先定義好的圖片。對應類AnimationDrawable

  • 其中AnimationDrawable,筆者在進階之路 | 奇妙的Drawable之旅的文章末尾處也提到過。

Q1:幀動畫的創建

A1:通過xml定義:

  • 該xml文件創建在res/drawable/ 下。
  • 根節點animation-list,屬性android:oneshot表示是否執行一次;子節點item 下可設置輪播的圖片資源id和持續時間。例如:
<?xml version="1.0" encoding="utf-8"?>  <animation-list  xmlns:android="http://schemas.android.com/apk/res/android"      android:oneshot="false">      <item android:drawable="@drawable/xxx1" android:duration="500"/>      <item android:drawable="@drawable/xxx2" android:duration="500"/>      <item android:drawable="@drawable/xxx3" android:duration="500"/>      <item android:drawable="@drawable/xxx4" android:duration="500"/>  </animation-list>

XML聲明好之後,將它作為View的背景並通過AnimationDrawable來播放即可。代碼如下:

mView.setBackgroundResource(R.drawable.XXX);  AnimationDrawable animationDrawable = (AnimationDrawable)mView.getBackground();  animationDrawable.start();

A2:通過Java代碼動態創建

//和上述xml定義方法的效果相同  AnimationDrawable ad = new AnimationDrawable();//1.創建AnimationDrawable對象      for (int i = 0; i < 4; i++) {//2.添加Drawable對象及其持續時間          Drawable drawable = getResources().getDrawable(getResources().getIdentifier("xxx" + i, "drawable", getPackageName()));          ad.addFrame(drawable, 500);      }      ad.setOneShot(false);//3.設置是否執行一次      mView.setBackgroundResource(ad);//4.將幀動畫作為view背景      ad.start();//5.播放動畫

2.2 屬性動畫

2.2.1 插值器和估值器

用途:屬性動畫中的插值器和估值器可以實現非勻速動畫

1 插值器(Interpolator)
  • 作用:根據時間流逝的百分比計算出當前屬性值改變的百分比。確定了動畫效果變化的模式,如勻速變化、加速變化、減速變化等等。

  • 常用的系統內置插值器:

    a.線性插值器(LinearInterpolator):勻速動畫

    b.加速減速插值器(AccelerateDecelerateInterpolator):動畫兩頭慢中間快

    c.減速插值器(DecelerateInterpolator):動畫越來越慢

  • 可針對的對象:

    a.View動畫:插值器對應的屬性是android:interpolator

    b.屬性動畫:是實現非勻速動畫的重要手段。

  • 自定義插值器方法:實現 Interpolator / TimeInterpolator接口 ,然後複寫getInterpolation()

  • 補間動畫實現 Interpolator接口、屬性動畫實現TimeInterpolator接口。
  • TimeInterpolator接口是屬性動畫中新增的,用於兼容Interpolator接口。

插值器效果圖

2 類型估值器(TypeEvaluator)
  • 作用:根據當前屬性改變的百分比計算出改變後的屬性值
  • 常用的系統內置的估值器:
  • 整型估值器(IntEvaluator)
  • 浮點型估值器(FloatEvaluator)
  • Color屬性估值器(ArgbEvaluator)
  • 僅針對於屬性動畫,View動畫不需要類型估值器。是屬性動畫實現非勻速動畫的重要手段。
  • 自定義估值器方法:實現TypeEvaluator接口,然後複寫evaluate()

限於篇幅,本篇文章未介紹自定義插值器和估值器的實例,想要了解的讀者,可以看下這篇文章:

Android 動畫:手把手帶你深入了解神秘的插值器

2.2.2 屬性動畫與View動畫異同

View動畫 屬性動畫
實現方式 通過不斷圖形變換 通過動態改變對象屬性
作用對象 View 任何對象,甚至沒有對象
存放位置 anim animator
狀態變化 未真正改變View位置 真正改變View位置

2.2.3 實現方式

1 通過XML

res/animator/下可創建屬性動畫的XML文件。其中,根節點set對應AnimatorSet類,子節點objectAnimator對應ObjectAnimator類、animator對應ValueAnimator類。常用屬性:

//animator/XX.xml  <set    android:ordering=["together" | "sequentially"]>        <objectAnimator          android:propertyName="string"          android:duration="int"          android:valueFrom="float | int | color"          android:valueTo="float | int | color"          android:startOffset="int"          android:repeatCount="int"          android:repeatMode=["repeat" | "reverse"]          android:valueType=["intType" | "floatType"]/>        <animator          android:duration="int"          android:valueFrom="float | int | color"          android:valueTo="float | int | color"          android:startOffset="int"          android:repeatCount="int"          android:repeatMode=["repeat" | "reverse"]          android:valueType=["intType" | "floatType"]/>        <set>          ...      </set>  </set>    //java  AnimatorSet set= AnimatorInflater.loadAnimator(myContext,R.anim.xxx);  set.setTarget(mBtn);  set.start();

首先介紹set標籤下的常用屬性:

  • ordering:設置動畫的時序關係。可選值:
  • together:默認值。表示動畫集合中的子動畫同時播放
  • equentially:表示動畫集合中的子動畫按照書寫的先後順序依次播放

接下來具體介紹屬性動畫的實現方式:

A.通過ObjectAnimator實現屬性動畫

  • 原理:通過直接對對象object)的屬性值進行改變操作,從而實現動畫效果。
  • 對應根節點objectAnimator
  • 常用屬性介紹:
  • propertyName:屬性動畫作用的屬性名稱

  • duration: 動畫持續時長

  • startOffset:設置動畫執行之前的等待時長

  • repeatCount:動畫重複執行的次數;默認為0,表示只播放一次。設置為-1或infinite,表示無限重複。

  • repeatMode:動畫重複執行的模式。可選值:

    a.restart:表示連續重複,為默認值。

    b.reverse :表示逆向重複

  • valueFrom:動畫初始值

  • valueTo:動畫結束值

  • valueType:表示propertyName指定的屬性值類型。可選值:

    a.intType :以上兩個value屬性值為整型。

    b.floatType:即以上兩個value屬性值為浮點型,為默認值。

    c.color:若propertyNamecolor,則無需設置該屬性。

B.通過ValueAnimator實現屬性動畫

  • 原理:通過不斷控制(value)的變化,再不斷手動賦給對象的屬性,從而實現動畫效果。

ObjectAnimatorValueAnimator類的區別:

  • ValueAnimator 類是先改變值,然後手動賦值給對象的屬性從而實現動畫;是間接對對象屬性進行操作
  • ObjectAnimator 類是先改變值,然後自動賦值給對象的屬性從而實現動畫;是直接對對象屬性進行操作
  • 對應根節點animator
  • 常用屬性比objectAnimator標籤少一個android:propertyName屬性,其他相同
2 通過JAVA

實際開發中建議用JAVA的方式來實現屬性動畫,原因:

  • 通過代碼來實現比較簡單
  • 很多時候屬性的起始值無法提前確定

A.ObjectAnimator

注意:這裡ObjectAnimator 作用的屬性必須有set方法

get方法可選;當動畫沒有設置初始值的時候,get必須存在)

  • 方法:
//第一個參數是對象,第二個是對象的屬性名字,第3個是值的變化,可以是ofFloat或者是ofInt,根據參數的類型直接寫  ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tv,"alpha",1,0,1);  //設置持續時間  objectAnimator.setDuration(2000);   objectAnimator.start();
  • 源碼分析:
//ofFloat直接返還一個 ObjectAnimator對象  public static ObjectAnimator ofFloat (Object target,String propertyName,float... values){              ObjectAnimator anim=new ObjectAnimator (target,propertyName);              anim.setFloatValues(values);          return anim;  }

B.ValueAnimator

  • ValueAnimator不提供任何動畫效果,更像一個數值發生器,用來產生有一定規律的數字,從而讓調用者控制動畫的實現過程。
  • 一般在AnimatorUpdateListener/AnimatorListenerAdapter(在下文會詳細介紹)中監聽數值的變化,而完成動畫的變換

測試實例:

  //實現顏色的漸變      ValueAnimator valueAnimator = ValueAnimator.ofArgb(0xFFFF5454, 0xFF5DDE5D, 0xFF5DBEDE);      valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {          @Override          public void onAnimationUpdate(ValueAnimator animation) {              int color = (int) animation.getAnimatedValue();              button.setBackgroundColor(color);          }      });      valueAnimator.setDuration(2000);      //記得start      valueAnimator.start();

C.組合動畫

C1.AnimatorSet

可以實現有先後順序的組合動畫

  • 重點方法:
  • play:傳入一個 Animator 對象 ,會返回一個AnimatorSet.Builder的實例

  • Builder中有4個方法:

    a.after(Animator):將現有動畫插入到傳入的動畫之後執行

    b.before(Animator):將現有動畫插入到傳入的動畫之前執行

    c.with(Animator):將現有動畫和傳入的動畫同時執行。

    d.after(Long):將現有動畫延遲指定毫秒後執行。

執行順序

  • 實例使用:
ObjectAnimator alphaAnimator =ObjectAnimator.ofFloat(tv, "alpha", 1, 0, 1);  ObjectAnimator rotateAnimator =ObjectAnimator.ofFloat(tv, "rotation", 0, 360, 0);  rotateAnimator.setDuration(15000);  ObjectAnimator scaleAnimator =ObjectAnimator.ofFloat(tv, "scaleX", 1, 2, 1);  ObjectAnimator translateAnimator =ObjectAnimator.ofFloat(tv, "translationX", 0, 100, 0);    AnimatorSet animatorSet = new AnimatorSet();  animatorSet.play(alphaAnimator)  .with(rotateAnimator)  .after(scaleAnimator)  .before(translateAnimator);  animatorSet.setDuration(2000).start();

實例執行順序

C2:PropertyValuesHolder

可以實現同時執行的組合動畫

  • 實例使用:
//新建動畫類  PropertyValuesHolder valuesHolder1=PropertyValuesHolder.ofFloat("scaleX",1.0f,1.5f);  PropertyValuesHolder valuesHolder2=PropertyValuesHolder.ofFloat("rotationX",0f,90.0f);  //新建ObjectAnimator  ObjectAnimator animator=ObjectAnimator.ofPropertyValuesHolder(view,valuesHolder1,valuesHolder2);  //開啟動畫  animator.setDuration(200).start();

2.2.4 監聽器

屬性動畫主要使用兩個接口:AnimatorUpdateListener&AnimatorListener來監聽動畫的播放過程。

  • AnimatorListener:監聽動畫的開始、結束、取消以及重複播放。如下:
public static interface AnimatorListener {      void onAnimationStart(Animator animation); //動畫開始      void onAnimationEnd(Animator animation); //動畫結束      void onAnimationCancel(Animator animation); //動畫取消      void onAnimationRepeat(Animator animation); //動畫重複播放  }

為方便開發,系統提供了AnimatorListenerAdapter類,它是AnimatorListener的適配器,如此可有選擇複寫上述四個方法。

  • AnimatorUpdateListener:監聽整個動畫過程。每播放一幀,onAnimationUpdate()就會被調用一次,如下:
public interface AnimatorUpdateListener {    void onAnimationUpdate(ValueAnimator var1);//在屬性動畫的屬性值變化是回調。  }

2.2.5 對任意屬性做動畫

  • 需滿足的條件:
  • 對象必須提供set方法,若未傳遞初始值給動畫,還需提供get方法(因為系統需要去取初始值)
  • set方法對屬性所做的改變必須能通過某種方式反映出來(例如:UI效果改變)
  • 解決方法:

    A.給對象加上getset方法

這個方法一般不可行,因為大多數的時候,我們沒有權限

​ B.用包裝類的方式,間接提供getset方法

實例:

class MyView{  private View mTarget;  private MyView (View view){  mTarget =view;  }    //屬性的get方法  public int getWidth(){  return mTarget.getLayoutParams().width;  }    //屬性的set方法  public void setWidth(int width){  mTarget.getLayoutParams().width=width;  mTarget.requestLayout();  }  }
MyView myView = new MyView(mButton);  ObjectAnimator.ofInt(myView, "width", 500).setDuration(500).start();

C.用ValueAnimator監聽動畫過程,自己改變屬性

測試實例:

    /**       * 改變對象的寬度       * @param target 對象       * @param start 起始值       * @param end 目標值       */      private void performAnimate(final View target, final int start, final int end) {          ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);          valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {              @Override              public void onAnimationUpdate(ValueAnimator animation) {                  //持有一個IntEvaluator對象,方便下面估值的時候使用                  IntEvaluator evaluator = new IntEvaluator();                  //得到當前動畫的進度值                  float fraction = animation.getAnimatedFraction();                  //調用整型估值器,通過比例計算出寬度,然後設給Btn                  target.getLayoutParams().width = evaluator.evaluate(fraction, start, end);                  target.requestLayout();              }          });          valueAnimator.setDuration(5000).start();      }

2.2.6 工作原理

  • 總體思路:在一定時間間隔內,通過不斷對值進行改變,並不斷將該值賦給對象的屬性,從而實現該對象在該屬性上的動畫效果。

具體體現在 :

  • 創建屬性動畫時,若未設初值,則系統會通過該屬性的get()方法獲取初始值。故屬性動畫要求必須提供屬性的get()方法
  • 在動畫播放的過程中,利用時間插值器和類型估值器獲取改變後的屬性值
  • 將改變後的屬性值通過set()方法設置到對象中。故屬性動畫要求必須提供屬性的set()方法
  • 具體流程:

get/set方法是通過反射調用的,筆者將帶你深入屬性動畫的源碼,探究其原因:

屬性動畫源碼解析

三.注意事項

恭喜你!已經看完了前面的文章,相信你對Animation已經有一定深度的了解!

在使用過程中,也有一些事項是需要我們注意的:

  • OOM問題

這個問題主要出現在幀動畫中,當圖片數量較多且圖片較大時易出現OOM,所以日常開發中盡量避免使用幀動畫

  • 內存泄漏
  • 屬性動畫中有一類無限循環的動畫(repeatCount=-1),這類動畫需要在Activity退出時及時停止,否則導致Activity無法釋放造成內存泄漏
  • View動畫不存在這個問題
  • View動畫的問題

View動畫是對View的影像做動畫,不是真正改變View的狀態,有時候出現動畫完成後View無法隱藏的現象(setVisibility(View.GONE)失效),需要調用view.clearAnimation()清除View動畫

  • 不要使用px

在進行動畫的過程中,盡量使用dp,使用px會導致在不同的設備上有不同的效果

想要了解具體原因的讀者,筆者給您推薦一篇文章:dp、sp、px區別

  • 動畫元素的交互

Android3.0之後,屬性動畫的單擊事件觸發位置為移動後的位置,但是View動畫仍在原位置

  • 硬件加速

Android4.0開始默認開啟硬件加速


如果文章對您有一點幫助的話,希望您能點一下贊,您的點贊,是我前進的動力

本文參考鏈接: