筆者在之前進階之路 | 奇妙的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動畫:
<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定義
//在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定義
//在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定義
//在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定義
//在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定義
<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聲明好之後,將它作爲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效果改變)