文章主要內(nèi)容來源《Android開發(fā)藝術(shù)探索》,部分內(nèi)容來源網(wǎng)上的文章,文中會(huì)有鏈接。
Android系統(tǒng)提供了兩個(gè)動(dòng)畫框架:屬性動(dòng)畫框架和View動(dòng)畫框架。 兩個(gè)動(dòng)畫框架都是可行的選項(xiàng),但是屬性動(dòng)畫框架通常是首選的使用方法,因?yàn)樗`活,并提供更多的功能。 除了這兩個(gè)框架,還可以使用Drawable動(dòng)畫(即逐幀動(dòng)畫,AnimationDrawable),它允許你加載Drawable資源并逐幀地顯示它們。
- View動(dòng)畫框架(補(bǔ)間動(dòng)畫)
View動(dòng)畫框架中一共提供了AlphaAnimation(透明度動(dòng)畫)、RotateAnimation(旋轉(zhuǎn)動(dòng)畫)、ScaleAnimation(縮放動(dòng)畫)、TranslateAnimation(平移動(dòng)畫)四種類型的補(bǔ)間動(dòng)畫;并且View動(dòng)畫框架還提供了動(dòng)畫集合類(AnimationSet),通過動(dòng)畫集合類(AnimationSet)可以將多個(gè)補(bǔ)間動(dòng)畫以組合的形式顯示出來。補(bǔ)間動(dòng)畫的實(shí)現(xiàn),一般會(huì)采用xml文件的形式,那樣代碼會(huì)更容易書寫和閱讀,同時(shí)也更容易復(fù)用。
- 屬性動(dòng)畫框架
與屬性動(dòng)畫相比View動(dòng)畫存在一個(gè)缺陷,View動(dòng)畫改變的只是View的顯示,而沒有改變View的響應(yīng)區(qū)域,并且View動(dòng)畫只能對(duì)View做四種類型的補(bǔ)間動(dòng)畫。因此Google在Android3.0(API級(jí)別11)及其后續(xù)版本中添加了屬性動(dòng)畫框架,從名稱中就可以知道只要某個(gè)類具有屬性(即該類含有某個(gè)字段的set和get方法),那么屬性動(dòng)畫框架就可以對(duì)該類的對(duì)象進(jìn)行動(dòng)畫操作(其實(shí)就是通過反射技術(shù)來獲取和執(zhí)行屬性的get,set方法),同樣屬性動(dòng)畫框架還提供了動(dòng)畫集合類(AnimatorSet),通過動(dòng)畫集合類(AnimatorSet)可以將多個(gè)屬性動(dòng)畫以組合的形式顯示出來。作者:ForeverCy
鏈接:http://www.lxweimin.com/p/b117c974deaf
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
一、View動(dòng)畫
View動(dòng)畫的四種變換效果對(duì)應(yīng)著Animation的四個(gè)子類:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation。四種動(dòng)畫可以用這四個(gè)類通過代碼實(shí)現(xiàn),也可以使用XML文件來定義。建議使用XML來定義動(dòng)畫,因?yàn)閄ML格式可讀性好而且方便復(fù)用。
名稱 | 標(biāo)簽 | 子類 |
---|---|---|
平移動(dòng)畫 | <translate/> | TranslateAnimation |
縮放動(dòng)畫 | <scale/> | ScaleAnimation |
選中動(dòng)畫 | <rotate/> | RotateAnimation |
透明度動(dòng)畫 | <alpha/> | AlphaAnimation |
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator=""
android:shareInterpolator="true|false">
<translate
android:fromXDelta=""
android:toXDelta=""
android:fromYDelta=""
android:toYDelta="" />
<scale
android:fromXScale=""
android:toXScale=""
android:fromYScale=""
android:toYScale=""
android:pivotX=""
android:pivotY="" />
<rotate
android:fromDegrees=""
android:toDegrees=""
android:pivotX=""
android:pivotY="" />
<alpha
android:fromAlpha=""
android:toAlpha="" />
<set>
<!--set嵌套set-->
</set>
</set>
View動(dòng)畫可以是單個(gè)動(dòng)畫,也可以由一系列動(dòng)畫組成,使用<set>標(biāo)簽實(shí)現(xiàn)組合。<set>標(biāo)簽對(duì)應(yīng)AnimationSet類,內(nèi)部也可以嵌套其他動(dòng)畫集合。
android:interpolator表示動(dòng)畫集合所使用的插值器,插值器影響動(dòng)畫的速度,比如非勻速動(dòng)畫就需要通過插值器來控制動(dòng)畫的播放過程。這個(gè)屬性可以不指定,默認(rèn)是@android:anim/accelerate_decelerate_interpolator,即加速減速插值器。
android:shareInterpolator表示集合中的動(dòng)畫是否和集合共享同一個(gè)插值器。如果集合不指定插值器,那么子動(dòng)畫就需要單獨(dú)指定所需要的插值器或者使用默認(rèn)值。
<translate>
android:fromXDelta x的起始值
android:toXDelta x的結(jié)束值
android:fromYDelta y的起始值
android:toYDelta y的結(jié)束值
<scale>
android:fromXScale 水平方向縮放的起始值
android:toXScale 水平方向縮放的結(jié)束值
android:fromYScale 豎直方向縮放的起始值
android:toYScale 豎直方向縮放的結(jié)束值
android:pivotX 縮放的軸點(diǎn)的x坐標(biāo)
android:pivotY 縮放的軸點(diǎn)的y坐標(biāo)
<rotate>
android:fromDegrees 旋轉(zhuǎn)開始的角度
android:toDegrees 旋轉(zhuǎn)結(jié)束的角度
android:pivotX 旋轉(zhuǎn)的軸點(diǎn)的x坐標(biāo)
android:pivotY 選中的軸點(diǎn)的y坐標(biāo)
<alpha>
android:fromAlpha 透明度的起始值
android:toAlpha 透明度的結(jié)束值
一些其他常用屬性
android:duration 動(dòng)畫的持續(xù)時(shí)間
android:fillBefore 動(dòng)畫結(jié)束后是否回到執(zhí)行前的位置
android:fillAfter 動(dòng)畫結(jié)束后是否停留在結(jié)束位置
android:interpolator 動(dòng)畫使用的插值器
android:startOffset 動(dòng)畫執(zhí)行之前的等待時(shí)間
android:repeatCount 動(dòng)畫重復(fù)執(zhí)行的次數(shù)
xml屬性都有對(duì)應(yīng)的set方法
使用示例
首先編寫動(dòng)畫xml文件test_anim.xml
Button mButton = (Button)findViewById(R.id.button);
Animation animation = AnimationUtils.loadAnimation(context, R.anim.test_anim);
mButton.startAnimation(animation);
也可以通過代碼實(shí)現(xiàn)
AlphaAnimation animation = new AlphaAnimation(0, 1);
animation.setDuration(300);
mButton.startAnimation(animation);
使用setAnimationListener方法可以給動(dòng)畫添加過程監(jiān)聽。
View動(dòng)畫的特殊使用場景
LayoutAnimation控制ViewGroup的子View的出場效果,使用步驟如下:
- 定義LayoutAnimation,文件名res/anim/anim_layout.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="150"
android:animationOrder="normal"
android:animation="@anim/anim_item"/>
android:delay 子元素開始動(dòng)畫的時(shí)間延遲
android:animationOrder 子元素動(dòng)畫的順序。有normal(順序)、reverse(逆向)、random(隨機(jī))三種順序。
android:animation 子元素入場的具體動(dòng)畫
- 定義子元素具體的入場動(dòng)畫,文件名res/anim/anim_item.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="500"
android:toXDelta="0"/>
- 為Viewgroup指定 android:layoutAnimation屬性,比如ListView:
<ListView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutAnimation="@anim/anim_layout" />
除了通過xml中指定layoutAnimation屬性,也可以通過LayoutAnimationController來實(shí)現(xiàn):
Animation animation = AnimationUtils.loadAnimation(context, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
Activity的切換效果
使用overridePendingTransition(int enterAnim, int exitAnim)方法實(shí)現(xiàn)Activity的切換動(dòng)畫,這個(gè)方法必須在startActivity()或者finish()之后調(diào)用才能生效。
Fragment也可以添加切換動(dòng)畫,通過FragmentTransaction中的setCustomAnimations()方法實(shí)現(xiàn)。
二、幀動(dòng)畫
幀動(dòng)畫的標(biāo)簽是<animation-list>,對(duì)應(yīng)AnimationDrawable類。
示例代碼
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/img1" android:duration="500"/>
<item android:drawable="@drawable/img2" android:duration="500"/>
<item android:drawable="@drawable/img3" android:duration="500"/>
</animation-list>
Button mButton = (Button)findViewById(R.id.button1);
mButton.setBackgroundResource(R.drawable.frame_animation);
AnimationDrawable drawable = (AnimationDrawable)mButton.getBackground();
drawable.start();
注意幀動(dòng)畫容易引起OOM。
三、屬性動(dòng)畫
屬性動(dòng)畫從 API 11 才有,屬性動(dòng)畫可以對(duì)任意對(duì)象的屬性進(jìn)行動(dòng)畫而不僅僅是View,達(dá)到的效果是:在一個(gè)時(shí)間間隔內(nèi)完成對(duì)象從一個(gè)屬性值到另一個(gè)屬性值的改變。屬性動(dòng)畫常用的動(dòng)畫類:ValueAnimator、ObjectAnimator、AnimatorSet。ObjectAnimator繼承自ValueAnimator,AnimatorSet是動(dòng)畫集合。
屬性動(dòng)畫也有對(duì)應(yīng)的xml標(biāo)簽,但是建議使用代碼來實(shí)現(xiàn)屬性動(dòng)畫,因?yàn)槭褂么a比xml簡單。而且很多時(shí)候一個(gè)屬性的起始值無法提前確定。
1、通過幾個(gè)例子看如何使用屬性動(dòng)畫:
- 改變對(duì)象myObject的translationY屬性,讓其沿著Y軸向上平移一段距離:它的高度。該動(dòng)畫在默認(rèn)時(shí)間內(nèi)完成
ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).start();
- 改變一個(gè)對(duì)象的背景色屬性,讓背景色在3秒內(nèi)從 0xFFFF8080 到 0xFF8080FF 的漸變,動(dòng)畫會(huì)無限循環(huán)而且會(huì)有反轉(zhuǎn)效果
ValueAnimator anim = ObjectAnimator.ofInt(myObject, "backgroundColor",0xFFFF8080,0xFF8080FF);
anim.setDuration(3000);
anim.setEvaluator(new ArgbEvaluator());
anim.setRepeatCount(ValueAnimator.INFINITE);
anim.setRepeatMode(ValueAnimator.REVERSE);
anim.start();
- 動(dòng)畫集合,5秒內(nèi)對(duì)View的旋轉(zhuǎn),平移,縮放和透明都進(jìn)行改變。
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),
ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),
ObjectAnimator.ofFloat(myView, "rotation", 0, -90),
ObjectAnimator.ofFloat(myView, "translationX", 0, 90),
ObjectAnimator.ofFloat(myView, "translationY", 0, 90),
ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),
ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),
ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)
);
set.setDuration(5*1000).start();
2、上面的代碼總結(jié)
上面代碼用到一個(gè)主要的方法
ObjectAnimator.ofFloat(target, propertyName, values...);
target 是動(dòng)畫目標(biāo),任何對(duì)象,不一定必須是View
propertyName 是屬性名字
values 是可變參數(shù), 從v1變化到v2到vn。。。
例子:
mIv是一個(gè)imageView
//alpha 從0 到1 的動(dòng)畫
ObjectAnimator.ofFloat(mIv, "alpha", 0f,1f)
.setDuration(500)
.start();
如果要實(shí)現(xiàn)其他效果,修改propertyName和values就行了。
屬性動(dòng)畫的原理就是通過反射,以動(dòng)畫的效果多次調(diào)用set方法來改變屬性值的。所以,使用屬性動(dòng)畫時(shí),相應(yīng)的對(duì)象屬性必須有set方法,get方法可以沒有,但是如果使用動(dòng)畫的時(shí)候沒有傳遞初始值,就必須提供get方法,因?yàn)橄到y(tǒng)要通過get方法獲取屬性的默認(rèn)初始值。
在屬性動(dòng)畫中,View的常用屬性
alpha 透明度
rotation z軸旋轉(zhuǎn)
rotationX x軸旋轉(zhuǎn)
rotationY y軸旋轉(zhuǎn)
translationX x水平偏移
translationY y水平偏移
ScaleX x軸縮放
ScaleY y軸縮放
3、插值器和估值器
插值器(TimeInterpolator/Interpolator)用來修飾動(dòng)畫效果,定義動(dòng)畫的變化規(guī)率(變化趨勢(shì)),比如平移動(dòng)畫,可以勻速平移也可以加速平移,這個(gè)由插值器決定。
估值器(Evaluator)用來決定具體的數(shù)值變化,比如(勻)加速平移時(shí),“加速度”是多少由估值器決定。
插值器TimeInterpolator和Interpolator,后者是繼承前者的接口。
TimeInterpolator接口是屬性動(dòng)畫中新增的,用于兼容Interpolator接口,這使得所有過去的Interpolator實(shí)現(xiàn)類都可以直接在屬性動(dòng)畫使用。
出自:http://blog.csdn.net/carson_ho/article/details/72863901
系統(tǒng)內(nèi)置的插值器有以下幾種:
- AccelerateInterpolator 加速
- DecelerateInterpolator 減速
- LinearInterpolator 勻速
- AccelerateDecelerateInterpolator 先加速再減速(在動(dòng)畫開始與結(jié)束的地方速率改變比較慢,在中間的時(shí)候最快)
- AnticipateInterpolator 開始的時(shí)候向后然后向前甩(先退后再加速前進(jìn))
- AnticipateOvershootInterpolator 開始的時(shí)候向后然后向前甩一定值后返回最后的值(先退后再加速前進(jìn),超出終點(diǎn)后再回終點(diǎn))
- BounceInterpolator 動(dòng)畫結(jié)束的時(shí)候彈起(最后階段彈球效果)
- CycleInterpolator 動(dòng)畫循環(huán)播放特定的次數(shù),速率改變沿著正弦曲線
- OvershootInterpolator 向前甩一定值后再回到原來位置(快速完成動(dòng)畫,超出再回到結(jié)束時(shí)的位置)
以上每種插值器都有對(duì)應(yīng)的xml資源,比如:@android:anim/linear_interpolator。(注意資源名的規(guī)律)
系統(tǒng)內(nèi)置的估值器有以下三種:
IntEvaluator 以整型的形式從初始值到結(jié)束值 進(jìn)行過渡
FloatEvaluator 以浮點(diǎn)型的形式從初始值到結(jié)束值 進(jìn)行過渡
ArgbEvaluator 以Argb類型的形式從初始值到結(jié)束值 進(jìn)行過渡
如果系統(tǒng)內(nèi)置的插值器和估值器無法滿足需求,也可以自定義。
View動(dòng)畫的插值器實(shí)現(xiàn)Interpolator接口,View動(dòng)畫沒有估值器
屬性動(dòng)畫的插值器實(shí)現(xiàn)實(shí)現(xiàn)TimeInterpolator接口,估值器實(shí)現(xiàn)TypeEvaluator接口
自定義插值器和估值器參考系統(tǒng)內(nèi)置的插值器和估值器即可。
以下摘自http://blog.csdn.net/carson_ho/article/details/72863901
實(shí)現(xiàn)Interpolator接口自定義插值器的說明(TimeInterpolator接口相同)
public interface Interpolator {
// 內(nèi)部只有一個(gè)方法
float getInterpolation(float input) {
// 參數(shù)說明
// input值值變化范圍是0-1,且隨著動(dòng)畫進(jìn)度(0% - 100% )均勻變化
// 即動(dòng)畫開始時(shí),input值 = 0;動(dòng)畫結(jié)束時(shí)input = 1
// 而中間的值則是隨著動(dòng)畫的進(jìn)度(0% - 100%)在0到1之間均勻增加
...// 插值器的計(jì)算邏輯
return xxx;
// 返回的值就是用于估值器繼續(xù)計(jì)算的fraction值
}
}
實(shí)現(xiàn)TypeEvaluator接口自定義估值器的說明
public interface TypeEvaluator {
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 參數(shù)說明
// fraction:插值器getInterpolation()的返回值
// startValue:動(dòng)畫的初始值
// endValue:動(dòng)畫的結(jié)束值
....// 估值器的計(jì)算邏輯
return xxx;
// 賦給動(dòng)畫屬性的具體數(shù)值
// 使用反射機(jī)制改變屬性變化
// 特別注意
// 插值器的input值 和 估值器fraction有什么關(guān)系呢?
// 答:input的值決定了fraction的值:input值經(jīng)過計(jì)算后傳入到插值器的getInterpolation()
// 然后通過實(shí)現(xiàn)getInterpolation()中的邏輯算法,根據(jù)input值來計(jì)算出一個(gè)返回值,而這個(gè)返回值就是fraction了
}
}
如何使用插值器和估值器
View動(dòng)畫和屬性動(dòng)畫都可以使用插值器,估值器只有屬性動(dòng)畫可以用。
插值器用法
// 步驟1:創(chuàng)建需要設(shè)置動(dòng)畫的視圖View
Button mButton = (Button) findViewById(R.id.Button);
// 步驟2:創(chuàng)建透明度動(dòng)畫的對(duì)象 & 設(shè)置動(dòng)畫效果
Animation alphaAnimation = new AlphaAnimation(1,0);
alphaAnimation.setDuration(3000);
// 步驟3:創(chuàng)建對(duì)應(yīng)的插值器類對(duì)象
Interpolator overshootInterpolator = new OvershootInterpolator();
// 步驟4:給動(dòng)畫設(shè)置插值器
alphaAnimation.setInterpolator(overshootInterpolator);
// 步驟5:播放動(dòng)畫
mButton.startAnimation(alphaAnimation);
估值器用法
// 在第3個(gè)參數(shù)中傳入對(duì)應(yīng)估值器類的對(duì)象
ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new Evaluator(),1,3);
4、屬性動(dòng)畫的監(jiān)聽
有兩個(gè)監(jiān)聽接口AnimatorListener和AnimatorUpdateListener。AnimatorListener和View動(dòng)畫的AnimationListener類似,可以監(jiān)聽動(dòng)畫的開始、結(jié)束等過程。AnimatorUpdateListener可以監(jiān)聽動(dòng)畫的每一幀變化,動(dòng)畫每變化一幀,接口里的方法被調(diào)用一次。
四、動(dòng)畫相關(guān)的其他問題、知識(shí)、技巧
對(duì)于沒有set和get方法的對(duì)象如何使用屬性動(dòng)畫:
- 給對(duì)象加上相應(yīng)的set和get方法,如果有權(quán)限的話(對(duì)系統(tǒng)的View是沒有權(quán)限改源碼的)
- 用一個(gè)包裝類包裝原始對(duì)象,間接的提供set和get方法
- 使用ValueAnimator,監(jiān)聽動(dòng)畫過程,自己實(shí)現(xiàn)屬性的改變。ValueAnimator本身不作用于任何對(duì)象,直接使用它是沒有效果的。它可以對(duì)一個(gè)值做動(dòng)畫,可以監(jiān)聽其動(dòng)畫過程,在動(dòng)畫過程中自己實(shí)現(xiàn)修改對(duì)象的屬性值,也就相當(dāng)于對(duì)象做了動(dòng)畫,示例代碼如下:
private void performAnimate(final View button, final int start, final int end) {
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//持有一個(gè)IntEvaluator對(duì)象,方便下面估值的時(shí)候使用
private IntEvaluator mIntEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animator) {
//獲得當(dāng)前動(dòng)畫的進(jìn)度值,整型1-100之間
int currentValue = (int) animator.getAnimatedValue();
//獲得當(dāng)前進(jìn)度占整個(gè)動(dòng)畫過程的比例,浮點(diǎn)型,0-1之間
float fraction = animator.getAnimatedFraction();
//直接調(diào)用整型估值器,通過比例計(jì)算出寬度,然后設(shè)給Button
button.getLayoutParams().width = mIntEvaluator.evaluate(fraction, start, end);
button.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
@Override
public void onClick(View v) {
if (v == mButton) {
performAnimate(mButton, mButton.getWidth(), 500);
}
}
通過源碼可以看出,屬性動(dòng)畫需要運(yùn)行在有Looper的線程中
使用動(dòng)畫的注意事項(xiàng)
- OOM問題。主要是幀動(dòng)畫圖片數(shù)量較多且圖片較大時(shí)容易出現(xiàn)OOM。
- 內(nèi)存泄漏。在屬性動(dòng)畫中有一類無限循環(huán)的動(dòng)畫,這類動(dòng)畫需要在Activity退出時(shí)及時(shí)停止,否則將導(dǎo)致Activity無法釋放從而造成內(nèi)存泄漏。通過驗(yàn)證后發(fā)現(xiàn)View動(dòng)畫不存在此問題。
- 兼容性問題。動(dòng)畫在3.0以下的系統(tǒng)上有兼容性問題,需要做好適配。
- View動(dòng)畫問題。View動(dòng)畫是對(duì)View的影像做動(dòng)畫,并不會(huì)真正的改變View的狀態(tài)。如果出現(xiàn)動(dòng)畫完成后View無法隱藏,即setVisibility(View.GONE)失效,這個(gè)時(shí)候只要調(diào)用view.clearAnimation()清除View動(dòng)畫即可解決。
- 不要使用px。盡量使用dp,使用px會(huì)導(dǎo)致在不同設(shè)備上有不同的效果。
- View平移后:在3.0以前的系統(tǒng),View動(dòng)畫和屬性動(dòng)畫都是新位置無法點(diǎn)擊,老位置可以點(diǎn)擊;3.0以后(包括3.0),屬性動(dòng)畫的點(diǎn)擊事件觸發(fā)位置是位移后的位置,View動(dòng)畫是原位置。
- 開啟硬件加速可以提高動(dòng)畫的流暢性。
五、補(bǔ)充屬性動(dòng)畫
View動(dòng)畫可以設(shè)置ViewGroup的子View的出場動(dòng)畫,屬性動(dòng)畫可以為ViewGroup的子View的顯示和隱藏設(shè)置過渡動(dòng)畫。出場動(dòng)畫文中已經(jīng)介紹,屬性動(dòng)畫實(shí)現(xiàn)的過度動(dòng)畫詳細(xì)見ForeverCy的文章 Android中的View動(dòng)畫和屬性動(dòng)畫 中的相關(guān)部分。