1. 分類
動畫必不可少,因為它可以讓交互更加流暢、自然陕贮,增強用戶滿意度。在Android中動畫主要有以下4類:
- 幀動畫
- 補間動畫
- 屬性動畫(Android3.0)
- VectorDrawable(Android5.0)(后期會單獨更新博客講解)
2. 原理
動畫實際上就是在指定的時間段內(nèi)持續(xù)地修改某個屬性的值潘飘,使得該值在在指定的范圍之內(nèi)平滑的過渡肮之。
看圖可知:動畫就是在某個時間點根據(jù)一定的計算方式計算出屬性的取值,并且設(shè)置給目標(biāo)對象卜录。在動畫的執(zhí)行周期內(nèi)持續(xù)執(zhí)行這個過程戈擒,形成動畫的效果。
3. 幀動畫(Frame動畫)
幀動畫是一系列圖片按照一定的順序展示的過程艰毒。它的原理是在一定的時間段內(nèi)切換多張有細微差異的圖片從而達到動畫的效果筐高。
幀動畫可以在xml中定義,也可以編碼實現(xiàn)丑瞧。如果再xml文件中定義的話柑土,可以放置在res下的anim或drawable目錄中,文件名可以作為資源id在代碼中引用绊汹,如果完全編碼實現(xiàn)稽屏,需要使用到AnimationDarawable對象。
在xml中定義幀動畫時灸促,<animation-list>元素必須為根元素诫欠,它可以包含一或多個<item>元素。
android:onshot如果定義為true時浴栽,此動畫只會執(zhí)行一次,如果為false則一直循環(huán)轿偎。
<item>元素代表一幀動畫典鸡,android:drawable指定此幀動畫所對應(yīng)的圖片資源,android:duration代表此幀持續(xù)的時間坏晦,單位為毫秒萝玷。
3.1 栗子:首先在XML中定義名為test_drawable.xml的文件,這里就不再講解在代碼中定義幀動畫了昆婿,推薦XML中定義球碉,更容易維護,減少代碼耦合度:
<?xml version="1.0" encoding="utf-8"?>
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item
android:drawable="@drawable/cancel"
android:duration="1000"></item>
<item
android:drawable="@drawable/hiss"
android:duration="1000"></item>
<item
android:drawable="@drawable/night"
android:duration="1000"></item>
<item
android:drawable="@drawable/ok"
android:duration="1000"></item>
</animation-list>
- 然后把動畫設(shè)置給某個View:例如:將該動畫設(shè)置為某個ImageView的背景:
<ImageView
android:layout_centerInParent="true"
android:id="@+id/iv_anim"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="@drawable/test_drawable"
/>
- 然后還需要通過Java代碼啟動該動畫:
((AnimationDrawable) mImageView.getBackground()).start();
4. 補間動畫(tween動畫或者View動畫)
補間動畫就是操作某個控件讓其展示出旋轉(zhuǎn)仓蛆、漸變睁冬、移動、縮放的一種轉(zhuǎn)換過程看疙,這成為補間動畫豆拨。
可以在XML中定義直奋,也可以編碼實現(xiàn),后者就不講解了施禾,推薦XML定義脚线。
在XML中定義并放置于/res/anim目錄下,文件名可以作為資源的id被引用弥搞;如果編碼實現(xiàn)邮绿,需要使用到Animation對象或者AnimationSet。
Animation對象有四個:AlphaAnimation攀例、RotateAnimation斯碌、TranslateAnimation、ScaleAnimation四種動畫方式肛度,這里只講解AnimationSet動畫集合傻唾。
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/cycle_interpolator"
android:shareInterpolator="true">
<alpha
android:duration="5000"
android:fromAlpha="1.0"
android:toAlpha="0.0"></alpha>
<scale
android:duration="5000"
android:fromXScale="0.2"
android:fromYScale="0.2"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.5"
android:toYScale="1.5"></scale>
<translate
android:duration="5000"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="500"></translate>
<rotate
android:duration="5000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:repeatMode="restart"
android:startOffset="0"
android:toDegrees="360">
</rotate>
</set>
AnimationSet animationSet = (AnimationSet) AnimationUtils.loadAnimation(this, R.anim.test_tween);
mImageView.startAnimation(animationSet);
說明:
<set>是一個動畫容器,管理多個動畫的數(shù)組承耿,與之相對應(yīng)的Java對象是AnimationSet冠骄。
它有兩個屬性:android:interpolator代表的是一個插值器的資源,可以引用系統(tǒng)自帶的插值器資源加袋,也可以自定義插值器資源凛辣,默認值死活勻速插值器。android:shareInterpolator代表<set>里面的多個動畫是否要共享插值器职烧,默認值是true扁誓,即共享插值器,如果是false蚀之,那么<set>插值器就不再起作用蝗敢,我們要在每個動畫中加入插值器。
alpha:透明度 淡入淡出效果 fromAlpha:開始透明度 toAlpha結(jié)尾透明度 浮點值足删。
scale:縮放 調(diào)整空間尺寸 fromXScale:起始的X方向上相對自身的縮放比例:比如1.0代表無變化 0.5代表起始時縮小一倍 2.0代表放大一倍寿谴。toXScale fromYScale toYScale 不再講解 pivotX代表縮放的中軸點X坐標(biāo),pivotY代表縮放的中軸點Y坐標(biāo)失受。 如果想以中軸點為圖像的中心讶泰,可以把兩個屬性值定義為0.5或者50%。 浮點值 拂到。
translate:移動 水平痪署、垂直的位移 fromXDelta代表起始X方向的位置 fromYDelta代表起始Y方向的位置 toXDelta代表結(jié)尾X方向的位置 toYDelta代表結(jié)尾Y方向的位置。 3種表示方式:浮點數(shù)(相對自身原始位置的像素值)兄旬、num%(代表相對于自己的百分比)狼犯、num%p(代表相對于父類控件的百分比) 以num%為例:toXDelta定義為100% 就表示在X方向上移動自己的1倍距離。
rotate: 旋轉(zhuǎn)動畫 fromDegrees起始角度 toDegrees結(jié)尾的角度 pivotX 旋轉(zhuǎn)中心的X坐標(biāo) pivotY旋轉(zhuǎn)中心的Y坐標(biāo) 坐標(biāo)有3種表示方式:數(shù)字方式代表相對于自身左邊緣的像素值,num%方式代表相對于自身左邊緣或頂邊緣的百分比辜王,num%p代表相對于父容器的左邊緣或頂邊緣的百分比劈狐。
注意:
- 補間動畫只能運用在View上,功能較為局限:比如旋轉(zhuǎn)只能在x呐馆、y軸肥缔,不能在z軸。
- 另外補間動畫的View的位置汹来,系統(tǒng)認定始終在起始位置续膳。
5. 屬性動畫
補間動畫的最大缺陷就是動畫改變的只是顯示,但View位置并沒有發(fā)生變化收班,View移動后不能響應(yīng)點擊事件坟岔。
屬性動畫解決了補間動畫的缺陷,其經(jīng)常使用的兩個類是ObjectAnimator和AnimatorSet摔桦。
屬性動畫通過調(diào)用屬性get社付、set方法真實地控制一個View的屬性值。
5.1 ObjectAnimator
ObjectAnimator是屬性動畫最重要的類:創(chuàng)建一個ObjectAnimator只需通過靜態(tài)工廠類直接返還一個ObjectAnimator對象邻耕。
參數(shù)包括一個對象和對象的屬性名字鸥咖,但這個屬性必須有g(shù)et和set方法,其內(nèi)部會通過Java發(fā)射機制調(diào)用set方法修改對象的屬性值兄世。
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mImageView,"translationX",200);
objectAnimator.setDuration(3000);
objectAnimator.start();
說明:通過ObjectAnimator的靜態(tài)方法啼辣,創(chuàng)建一個ObjectAnimator對象,查看ObjectAnimator.java的靜態(tài)方法ofFloat():
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
說明:第一個參數(shù)是Object的目標(biāo)View御滩;第二個參數(shù)是要操作的屬性鸥拧;最后一個參數(shù)是一個可變的float類型的數(shù)組,需要傳進去該屬性變化的取值過程削解,這里設(shè)置了一個參數(shù)富弦,變化到200.
- 屬性動畫也可以設(shè)置市場、插值器等屬性钠绍。下面是一些常用的屬性動畫的屬性值:
translationX 和 translationY : 用來沿著X軸或者Y軸進行平移舆声。
rotation、rotationX柳爽、rotationY:用來圍繞View的支點進行旋轉(zhuǎn)。
PrivotX 和 PrivotY :控制VIew對象的支點位置碱屁,圍繞這個支點進行旋轉(zhuǎn)和縮放變換處理磷脯。默認該支點位置是View對象的中心點。
alpha :透明度娩脾,默認是1(不透明)赵誓,0代表完全透明
x和y: 描述View對象在其容器中的最終位置。
- 需要注意ObjectAnimator使用時,要操作的屬性必須有set和get方法俩功,不然失效幻枉。當(dāng)然可以自定義一個屬性類或者包裝類來間接地給這個屬性增加get和set方法:
private static class MyView {
private View mTarget;
private MyView(View mTarget) {
this.mTarget = mTarget;
}
public int getWidth() {
return mTarget.getLayoutParams().width;
}
public void setWidth(int width) {
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
MyView myView = new MyView(mImageView);
ObjectAnimator.ofInt(myView, "width", 5000).setDuration(5000).start();
- ObjectAnimator 動畫的監(jiān)聽
objectAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
}
@Override
public void onAnimationEnd(Animator animator) {
}
@Override
public void onAnimationCancel(Animator animator) {
}
@Override
public void onAnimationRepeat(Animator animator) {
}
});
說明:大部分的時候我們只關(guān)心onAnimationEnd事件,Android也提供了AnimatorListenerAdapter來讓我們選擇必要的事件進行監(jiān)聽诡蜓。
objectAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
}
});
5.2 ValueAnimator
ValueAnimator是在一定的時間段內(nèi)不斷地修改對象的某個屬性值熬甫。
我們只需要將屬性的取值范圍、運行時長提供給ValueAnimator蔓罚,那么它就會自動幫我們計算屬性值在各個動畫運行時段的取值椿肩,這些值會按照一定的計算方式來實現(xiàn)平滑過渡。
此外:ValueAnimator還負責(zé)管理動畫的播放次數(shù)豺谈、播放模式郑象、以及對動畫設(shè)置監(jiān)聽器等。
ValueAnimator 不僅功能強大茬末,API設(shè)計也非常簡單:通過ofFloat厂榛、ofInt等靜態(tài)工廠函數(shù)構(gòu)建ValueAnimator:例:下面就是我們將數(shù)值從1.0 過渡到0.0的動畫:
//ValueAnimator
private void startValueAnimation() {
final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f);
animator.setDuration(5000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
Float newValue = (Float) valueAnimator.getAnimatedValue();
Log.e("","startValueAnimation newValue :"+newValue);
mImageView.setAlpha(newValue);
}
});
animator.start();
}
說明:
- ValueAnimator不提供任何動畫效果,它更像一個數(shù)值發(fā)生器丽惭,用來產(chǎn)生一定規(guī)律的數(shù)字击奶,從而讓調(diào)用者控制動畫的實現(xiàn)過程。
- 通常:在ValueAnimator的AnimatorUpdateListener中監(jiān)聽數(shù)值的變化吐根,從而完成動畫的變化正歼。
強調(diào):
- 啟動動畫后,每次更新屬性值時就會調(diào)用AnimatorUpdate函數(shù)拷橘,在這里獲取新的屬性值局义。
- 這里并沒有將這個值運動到具體的對象上,它只操作屬性值本身冗疮,這個值不屬于某個具體的對象萄唇,所以它的優(yōu)勢在于可以運用到任意的對象上。
5.3 AnimatorSet
- 先看代碼:
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mImageView, "translationX", 0.0f, 200.0f, 0f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mImageView,"scaleX",1.0f,2.0f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(mImageView,"rotationX",0.0F,90.0F,0.0F);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.play(animator1).with(animator2).after(animator3);
說明:
- 首先創(chuàng)建了3個ObjectAnimator术幔,分別是animator1 animator2 animator3另萤,然后創(chuàng)建了AnimatorSet。
- 先執(zhí)行animator3诅挑,然后同時執(zhí)行animator1和animator2四敞。
- 也可以調(diào)用set.playTogether(animator1,animator2)來使者兩種動畫同時執(zhí)行。
- AnimatorSet類提供了一個play()方法拔妥,我們將Animator對象(ValueAnimator或ObjectAnimator)忿危,將會返回一個AnimatorSet.Builder實例,我們看看play方法的源碼:
public Builder play(Animator anim) {
if (anim != null) {
mNeedsSort = true;
return new Builder(anim);
}
return null;
}
- 我們可以看到play方法中創(chuàng)建了一個AnimatorSet.Builder類没龙,這個Builder類是AnimatorSet的內(nèi)部類铺厨,我們看看Builder類中有什么:
public class Builder {
private Node mCurrentNode;
Builder(Animator anim) {
mCurrentNode = mNodeMap.get(anim);
if (mCurrentNode == null) {
mCurrentNode = new Node(anim);
mNodeMap.put(anim, mCurrentNode);
mNodes.add(mCurrentNode);
}
}
public Builder with(Animator anim) {
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
mNodeMap.put(anim, node);
mNodes.add(node);
}
Dependency dependency = new Dependency(mCurrentNode, Dependency.WITH);
node.addDependency(dependency);
return this;
}
public Builder before(Animator anim) {
mReversible = false;
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
mNodeMap.put(anim, node);
mNodes.add(node);
}
Dependency dependency = new Dependency(mCurrentNode, Dependency.AFTER);
node.addDependency(dependency);
return this;
}
public Builder after(Animator anim) {
mReversible = false;
Node node = mNodeMap.get(anim);
if (node == null) {
node = new Node(anim);
mNodeMap.put(anim, node);
mNodes.add(node);
}
Dependency dependency = new Dependency(node, Dependency.AFTER);
mCurrentNode.addDependency(dependency);
return this;
}
public Builder after(long delay) {
// setup dummy ValueAnimator just to run the clock
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(delay);
after(anim);
return this;
}
}
- 從Builder源碼中我們可以看出來它采用了建造者模式缎玫,每次調(diào)用方法時都返回Builder自身用于繼續(xù)構(gòu)建。AnimatorSet.Builder中包括以下4個方法:
after(Animator anim):將現(xiàn)有的動畫插入到傳入的動畫之后執(zhí)行解滓。
after(long delay):將現(xiàn)有動畫延遲指定毫秒之后執(zhí)行赃磨。
before(Animator anim):將現(xiàn)有動畫插入到傳入的動畫之前執(zhí)行。
with(Animator anim):將現(xiàn)有的動畫和傳入的動畫同時執(zhí)行洼裤。