進階之路 | 奇妙的Animation之旅
- 2020 年 3 月 3 日
- 筆記
前言
本文已經收錄到我的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
,子節點translate
、scale
、rotate
、alpha
,分別對應四種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代碼動態創建
- 具體步驟:
- 創建
TranslateAnimation
、RotateAnimation
、ScaleAnimation
或AlphaAnimation
對象。 - 設置創建的動畫對象的屬性,如動畫執行時間、延遲時間、起始位置、結束位置等
- 通過
View.startAnimation()
方法開啟動畫 - 可通過
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來簡化矩陣變換
3 特殊使用場景
View動畫除了可作用在某個View對象上, 還可以用在特殊的場景,例如:
控制
ViewGroup
的子View
的出場效果
Activity
的切換效果接下來將依次介紹:
A1:子View
出場動畫
- 常用場景:
ListView
、GridView
、RecyclerView
- 對應類:
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);
- 補充實例:感興趣的讀者可以了解下:Android動畫總結——布局動畫、轉場動畫
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.播放動畫
注意:使用幀動畫要注意不能使用尺寸過大的圖片,否則容易造成
OOM
( 內存溢出)想要更進一步了解幀動畫的讀者,可以看一下這篇文章:關於 逐幀動畫 的使用都在這裡了!
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()
。
限於篇幅,本篇文章未介紹自定義插值器和估值器的實例,想要了解的讀者,可以看下這篇文章:
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
:若propertyName
為color
,則無需設置該屬性。
B.通過ValueAnimator實現屬性動畫
- 原理:通過不斷控制值(value)的變化,再不斷手動賦給對象的屬性,從而實現動畫效果。
ObjectAnimator
與ValueAnimator
類的區別:
- 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.給對象加上
get
和set
方法
這個方法一般不可行,因為大多數的時候,我們沒有權限
B.用包裝類的方式,間接提供get
和set
方法
實例:
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
開始默認開啟硬件加速
如果文章對您有一點幫助的話,希望您能點一下贊,您的點贊,是我前進的動力
本文參考鏈接:
- 《Andorid 開發藝術探索》
- 《Android 進階之光》
- 過渡動畫有多重要?
- 自定義補間動畫
- 3D翻轉動畫
- Android 動畫:手把手帶你深入了解神秘的插值器
- dp、sp、px區別
- 要點提煉|開發藝術之Animation
- 關於 逐幀動畫 的使用都在這裡了!
- 安卓scale動畫fromDegrees toDegrees解釋
- RecyclerView使用技巧(item動畫及嵌套高度適配解決方案)