進階之路 | 奇妙的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"?>

<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"/>
複製代碼接下來分別解釋各個節點下屬性含義: 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"?>


//在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"?>


//在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"?>


//在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"?>


//在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"?>

<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"/>   

//在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出場動畫

常用場景: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

// res/anim/anim_item.xml 效果:子項從右邊進入




複製代碼step2:爲ViewGroup設置android:layoutAnimation屬性, 這裏假設爲Listview:

//activity_main.xml

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

// res/anim/anim_item.xml



複製代碼 //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"?>







複製代碼在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()。

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

//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效果改變)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章