1. android動(dòng)畫(huà)分為view動(dòng)畫(huà)腹备、幀動(dòng)畫(huà)和屬性動(dòng)畫(huà)衬潦,屬性動(dòng)畫(huà)是API 11(Android 3.0)的新特性,幀動(dòng)畫(huà)一般也認(rèn)為是view動(dòng)畫(huà)
1.1 view動(dòng)畫(huà)
- 平移動(dòng)畫(huà) TranslateAnimation
- 縮放動(dòng)畫(huà) ScaleAnimation
- 旋轉(zhuǎn)動(dòng)畫(huà) RotateAnimation
- 透明度動(dòng)畫(huà) AlphaAnimation
AnimationSet
的兩個(gè)屬性
android:shareInterpolator
:表示集合中的動(dòng)畫(huà)是否共享同一個(gè)插值器植酥,如果集合不指定插值器镀岛,那么子動(dòng)畫(huà)需要單獨(dú)指定所需的插值器或者使用默認(rèn)值。
android:interpolator
:插值器
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:interpolator="@android:anim/accelerate_interpolator"
android:shareInterpolator="true" >
<!-- 透明度起始值友驮,結(jié)束值 -->
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<!-- 平移起始值漂羊,結(jié)束值 -->
<translate
android:fromXDelta="500"
android:toXDelta="0" />
<!-- 縮放起始值,結(jié)束值卸留,縮放軸點(diǎn)x走越,y坐標(biāo) -->
<scale
android:fromXScale="0.5"
android:toXScale="1.2"
android:pivotX="1"
android:pivotY="1" />
<!-- 旋轉(zhuǎn)起始角度,結(jié)束角度耻瑟,旋轉(zhuǎn)軸點(diǎn)x旨指,y坐標(biāo) -->
<rotate
android:fromDegrees="0"
android:toDegrees="180"
android:pivotX="1"
android:pivotY="1"/>
</set>
調(diào)用方法
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_animation);
mView.startAnimation(animation);
設(shè)置監(jiān)聽(tīng)
anim.setAnimationListener(new AnimationListener(){
@Override
public void onAnimationEnd(Animation arg0) {}
@Override
public void onAnimationRepeat(Animation animation) {}
@Override
public void onAnimationStart(Animation animation) {}
});
其他常用屬性
// 持續(xù)時(shí)間
android:duration="100"
// 是否停留在結(jié)束位置
android:fillAfter="true"
activity的切換效果要設(shè)置在startactivity和finish之后
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
1.2 布局動(dòng)畫(huà)(LayoutAnimation
)屬性分析
<layoutAnimation
xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="0.5"
android:animationOrder="reverse"
android:animation="@anim/anim_item"/>
android:delay
:表示子元素開(kāi)始動(dòng)畫(huà)的時(shí)間延遲赏酥,比如子元素入場(chǎng)動(dòng)畫(huà)的時(shí)間周期是300ms,那么0.5表示每個(gè)子元素都需要延遲150ms才能播放入場(chǎng)動(dòng)畫(huà)淤毛。
給ViewGroup指定LayoutAnimation的兩種方式
//xml
android:layoutAnimation="xxx"
//java
Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_item);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
listView.setLayoutAnimation(controller);
1.3 屬性動(dòng)畫(huà)
View動(dòng)畫(huà)的xml文件放于res/anim/目錄下今缚,而屬性動(dòng)畫(huà)的xml文件則放于res/animator/目錄下。
屬性動(dòng)畫(huà)主要有三個(gè)元素:
<animator> <objectAnimator> <set>
相對(duì)應(yīng)的有三個(gè)類:ValueAnimator低淡、ObjectAnimator姓言、AnimatorSet。
< animator >
< animator >標(biāo)簽與對(duì)應(yīng)的ValueAnimator類提供了屬性動(dòng)畫(huà)的核心功能蔗蹋,包括計(jì)算動(dòng)畫(huà)值何荚、動(dòng)畫(huà)時(shí)間細(xì)節(jié)、是否重復(fù)等猪杭。執(zhí)行屬性動(dòng)畫(huà)分兩個(gè)步驟:
- 計(jì)算動(dòng)畫(huà)值
- 將動(dòng)畫(huà)值應(yīng)用到對(duì)象和屬性上
ValueAnimiator只完成第一步餐塘,即只計(jì)算值,要實(shí)現(xiàn)第二步則需要在值變化的監(jiān)聽(tīng)器里自行更新對(duì)象屬性皂吮。
通過(guò)<animator>標(biāo)簽可以很方便的對(duì)ValueAnimiator進(jìn)行設(shè)置戒傻,可設(shè)置的屬性如下:
android:duration 動(dòng)畫(huà)從開(kāi)始到結(jié)束持續(xù)的時(shí)長(zhǎng),單位為毫秒
android:startOffset 設(shè)置動(dòng)畫(huà)執(zhí)行之前的等待時(shí)長(zhǎng)蜂筹,單位為毫秒
android:repeatCount 設(shè)置動(dòng)畫(huà)重復(fù)執(zhí)行的次數(shù)需纳,默認(rèn)為0,即不重復(fù)艺挪;可設(shè)為-1或infinite不翩,表示無(wú)限重復(fù)
-
android:repeatMode 設(shè)置動(dòng)畫(huà)重復(fù)執(zhí)行的模式,可設(shè)為以下兩個(gè)值其中之一:
- restart 動(dòng)畫(huà)重復(fù)執(zhí)行時(shí)從起點(diǎn)開(kāi)始麻裳,默認(rèn)為該值
- reverse 動(dòng)畫(huà)會(huì)反方向執(zhí)行
android:valueFrom 動(dòng)畫(huà)開(kāi)始的值口蝠,可以為int值、float值或color值
android:valueTo 動(dòng)畫(huà)結(jié)束的值津坑,可以為int值妙蔗、float值或color值
-
android:valueType 動(dòng)畫(huà)值類型,若為color值国瓮,則無(wú)需設(shè)置該屬性
- intType 指定動(dòng)畫(huà)值灭必,即以上兩個(gè)value屬性的值為整型
- floatType 指定動(dòng)畫(huà)值,即以上兩個(gè)value屬性的值為浮點(diǎn)型乃摹,默認(rèn)值
android:interpolator 設(shè)置動(dòng)畫(huà)速率的變化
在這個(gè)例子里,將一個(gè)按鈕的寬度進(jìn)行縮放跟衅,從100%縮放到20%孵睬。
<!-- res/animator/value_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
可看到,值的變化從100到20伶跷,動(dòng)畫(huà)時(shí)長(zhǎng)3000毫秒掰读,以下則是目標(biāo)按鈕的xml代碼:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_btn_normal"
android:onClick="onScaleWidth"
android:text="點(diǎn)我"
android:textColor="@android:color/white" />
按鈕默認(rèn)是填充屏幕寬度的秘狞,點(diǎn)擊時(shí)的執(zhí)行方法為onScaleWidth,以下則是onScaleWidth方法的代碼:
public void onScaleWidth(final View view) {
// 獲取屏幕寬度
final int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
ValueAnimator valueAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this, R.animator.value_animator);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animator) {
// 當(dāng)前動(dòng)畫(huà)值蹈集,即為當(dāng)前寬度比例值
int currentValue = (Integer) animator.getAnimatedValue();
// 根據(jù)比例更改目標(biāo)view的寬度
view.getLayoutParams().width = maxWidth * currentValue / 100;
view.requestLayout();
}
});
valueAnimator.start();
}
屬性動(dòng)畫(huà)是通過(guò)AnimatorInflater類的loadAnimation()方法獲取相應(yīng)的Animator類實(shí)例烁试。
另外,ValueAnimator通過(guò)添加AnimatorUpdateListener監(jiān)聽(tīng)器監(jiān)聽(tīng)值的變化拢肆,從而再手動(dòng)更新目標(biāo)對(duì)象的屬性减响。
最后,通過(guò)調(diào)用valueAnimator.start()方法啟動(dòng)動(dòng)畫(huà)郭怪。
< objectAnimator >
< objectAnimator >標(biāo)簽對(duì)應(yīng)的類為ObjectAnimator支示,為ValueAnimator的子類。< objectAnimator >標(biāo)簽與< animator >標(biāo)簽不同的是鄙才,< objectAnimator >可以直接指定動(dòng)畫(huà)的目標(biāo)對(duì)象的屬性颂鸿。標(biāo)簽可設(shè)置的屬性除了和< animator >一樣的那些,另外多了一個(gè):
- android:propertyName 目標(biāo)對(duì)象的屬性名攒庵,要求目標(biāo)對(duì)象必須提供該屬性的setter方法嘴纺,如果動(dòng)畫(huà)的時(shí)候沒(méi)有初始值,還需要提供getter方法
還是用上面的例子浓冒,將一個(gè)按鈕的寬度進(jìn)行縮放栽渴,從100%縮放到20%,但這次改用<objectAnimator>實(shí)現(xiàn)裆蒸。
以下為xml文件的代碼:
<!-- res/animator/object_animator.xml -->
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:propertyName="width"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
與< animator >的例子相比熔萧,就只是多了一個(gè)android:propertyName的屬性,設(shè)置值為width僚祷。也就是說(shuō)佛致,動(dòng)畫(huà)改變的屬性為width,值將從100逐漸減到20辙谜。另外俺榆,值是從setWidth()傳遞過(guò)去的,再?gòu)膅etWidth()獲取装哆。而且罐脊,這里設(shè)置的值代表的是比例值,因此蜕琴,還需要進(jìn)行計(jì)算轉(zhuǎn)化為實(shí)際的寬度值萍桌。最后,對(duì)象實(shí)際的寬度值為view.getLayoutParams().width凌简。因此上炎,我將用一個(gè)包裝類來(lái)包裝原始的view對(duì)象,對(duì)其提供setWidth()和getWidth()方法雏搂,代碼如下:
private static class ViewWrapper {
private View target; //目標(biāo)對(duì)象
private int maxWidth; //最長(zhǎng)寬度值
public ViewWrapper(View target, int maxWidth) {
this.target = target;
this.maxWidth = maxWidth;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int widthValue) {
//widthValue的值從100到20變化
target.getLayoutParams().width = maxWidth * widthValue / 100;
target.requestLayout();
}
}
上面setWidth()的代碼里藕施,根據(jù)比例值轉(zhuǎn)化為了實(shí)際的寬度值寇损。最后,動(dòng)畫(huà)處理的代碼如下:
public void onScaleWidth(View view) {
// 獲取屏幕寬度
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
// 將目標(biāo)view進(jìn)行包裝
ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
// 將xml轉(zhuǎn)化為ObjectAnimator對(duì)象
ObjectAnimator objectAnimator = (ObjectAnimator) AnimatorInflater.loadAnimator(this, R.animator.object_animator);
// 設(shè)置動(dòng)畫(huà)的目標(biāo)對(duì)象為包裝后的view
objectAnimator.setTarget(wrapper);
// 啟動(dòng)動(dòng)畫(huà)
objectAnimator.start();
}
ObjectAnimator提供了屬性的設(shè)置裳食,但相應(yīng)的需要有該屬性的setter和getter方法矛市。而ValueAnimator則只是定義了值的變化,并不指定目標(biāo)屬性诲祸,所以也不需要提供setter和getter方法浊吏,但只能在AnimatorUpdateListener監(jiān)聽(tīng)器里手動(dòng)更新屬性。不過(guò)烦绳,也因?yàn)闆](méi)有指定屬性卿捎,所以其實(shí)更具靈活性了,你可以在監(jiān)聽(tīng)器里根據(jù)值的變化做任何事情径密,比如更新多個(gè)屬性午阵,比如在縮放寬度的同時(shí)做垂直移動(dòng)。
為了對(duì)View更方便的設(shè)置屬性動(dòng)畫(huà)享扔,Android系統(tǒng)也提供了View的一些屬性和相應(yīng)的setter和getter方法:
- alpha:透明度底桂,默認(rèn)為1,表示不透明惧眠,0表示完全透明
- pivotX 和 pivotY:旋轉(zhuǎn)的軸點(diǎn)和縮放的基準(zhǔn)點(diǎn)籽懦,默認(rèn)是View的中心點(diǎn)
- scaleX 和 scaleY:基于pivotX和pivotY的縮放,1表示無(wú)縮放氛魁,小于1表示收縮暮顺,大于1則放大
- rotation、rotationX 和 rotationY:基于軸點(diǎn)(pivotX和pivotY)的旋轉(zhuǎn)秀存,rotation為平面的旋轉(zhuǎn)着裹,rotationX和rotationY為立體的旋轉(zhuǎn)
- translationX 和 translationY:View的屏幕位置坐標(biāo)變化量岳服,以layout容器的左上角為坐標(biāo)原點(diǎn)
- x 和 y:View在父容器內(nèi)的最終位置茎杂,是左上角坐標(biāo)和偏移量(translationX鲸沮,translationY)的和
< set >
<set>標(biāo)簽對(duì)應(yīng)于AnimatorSet類,可以將多個(gè)動(dòng)畫(huà)組合成一個(gè)動(dòng)畫(huà)集澳盐,如上面提到的在縮放寬度的同時(shí)做垂直移動(dòng)祈纯,可以將一個(gè)縮放寬度的動(dòng)畫(huà)和一個(gè)垂直移動(dòng)的動(dòng)畫(huà)組合在一起。
<set>標(biāo)簽有一個(gè)屬性可以設(shè)置動(dòng)畫(huà)的時(shí)序關(guān)系:
-
android:ordering 設(shè)置動(dòng)畫(huà)的時(shí)序關(guān)系叼耙,取值可為以下兩個(gè)值之一:
- together 動(dòng)畫(huà)同時(shí)執(zhí)行腕窥,默認(rèn)值
-
sequentially 動(dòng)畫(huà)按順序執(zhí)行
那如果想有些動(dòng)畫(huà)同時(shí)執(zhí)行,有些按順序執(zhí)行筛婉,該怎么辦呢油昂?因?yàn)?strong><set>標(biāo)簽是可以嵌套其他<set>標(biāo)簽的,也就是說(shuō)可以將同時(shí)執(zhí)行的組合在一個(gè)<set>標(biāo)簽倾贰,再嵌在按順序執(zhí)行的<set>標(biāo)簽內(nèi)冕碟。
示例代碼:
<!-- res/animator/animator_set.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:duration="3000"
android:propertyName="width"
android:valueFrom="100"
android:valueTo="20"
android:valueType="intType" />
<objectAnimator
android:duration="3000"
android:propertyName="marginTop"
android:valueFrom="0"
android:valueTo="100"
android:valueType="intType" />
</set>
以上代碼可實(shí)現(xiàn)兩個(gè)同時(shí)執(zhí)行的動(dòng)畫(huà),一個(gè)將width從100縮放到20匆浙,一個(gè)將marginTop從0增加到100安寺。多了一個(gè)marginTop屬性,那么首尼,在ViewWrapper添加setMarginTop()方法挑庶,添加后的ViewWrapper類代碼如下:
private static class ViewWrapper {
private View target;
private int maxWidth;
public ViewWrapper(View target, int maxWidth) {
this.target = target;
this.maxWidth = maxWidth;
}
public int getWidth() {
return target.getLayoutParams().width;
}
public void setWidth(int widthValue) {
target.getLayoutParams().width = maxWidth * widthValue / 100;
target.requestLayout();
}
public void setMarginTop(int margin) {
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) target.getLayoutParams();
layoutParams.setMargins(0, margin, 0, 0);
target.setLayoutParams(layoutParams);
}
}
最后,動(dòng)畫(huà)處理的代碼:
public void onScaleWidth(View view) {
// 獲取屏幕寬度
int maxWidth = getWindowManager().getDefaultDisplay().getWidth();
// 將目標(biāo)view進(jìn)行包裝
ViewWrapper wrapper = new ViewWrapper(view, maxWidth);
// 將xml轉(zhuǎn)化為ObjectAnimator對(duì)象
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.animator_set);
// 設(shè)置動(dòng)畫(huà)的目標(biāo)對(duì)象為包裝后的view
animatorSet.setTarget(wrapper);
// 啟動(dòng)動(dòng)畫(huà)
animatorSet.start();
}
1.4 插值器和估值器:屬性動(dòng)畫(huà)實(shí)現(xiàn)非勻速動(dòng)畫(huà)的重要手段
時(shí)間插值器(TimeInterpolator
)的作用是根據(jù)時(shí)間流逝的百分比計(jì)算出當(dāng)前屬性值改變的百分比软能,系統(tǒng)內(nèi)置的插值器有線性插值器(LinearInterpolator
)迎捺、加速減速插值器(AccelerateDecelerateInterpolator
)和減速插值器(DecelerateInterpolator
)。
類型估值器(TypeEvaluator
)的作用是根據(jù)當(dāng)前屬性改變的百分比計(jì)算出改變后的屬性值查排,系統(tǒng)內(nèi)置的估值器有IntEvaluator
凳枝、FloatEvaluator
和ArgbEvaluator
(針對(duì)顏色)。
1.5 使用動(dòng)畫(huà)的注意事項(xiàng)
(1)OOM:盡量避免使用幀動(dòng)畫(huà)跋核,使用的話應(yīng)盡量避免使用過(guò)多尺寸較大的圖片岖瑰;
(2)內(nèi)存泄露:屬性動(dòng)畫(huà)中的無(wú)限循環(huán)動(dòng)畫(huà)需要在Activity退出的時(shí)候及時(shí)停止(clearAnimation),否則將導(dǎo)致Activity無(wú)法釋放而造成內(nèi)存泄露砂代。view動(dòng)畫(huà)不存在這個(gè)問(wèn)題蹋订;
(3)兼容性問(wèn)題:某些動(dòng)畫(huà)在3.0以下系統(tǒng)上有兼容性問(wèn)題;
(4)view動(dòng)畫(huà)的問(wèn)題:view動(dòng)畫(huà)是對(duì)view的影像做動(dòng)畫(huà)刻伊,并不是真正的改變view的狀態(tài)露戒,因此有時(shí)候動(dòng)畫(huà)完成之后view無(wú)法隱藏,即setVisibility(View.GONE)
失效了捶箱,此時(shí)需要調(diào)用view.clearAnimation()
清除view動(dòng)畫(huà)才行智什。
(5)不要使用px;
(6)動(dòng)畫(huà)元素的交互:在android3.0以前的系統(tǒng)上讼呢,view動(dòng)畫(huà)和屬性動(dòng)畫(huà)撩鹿,新位置均無(wú)法觸發(fā)點(diǎn)擊事件,同時(shí)悦屏,老位置仍然可以觸發(fā)單擊事件节沦。從3.0開(kāi)始,屬性動(dòng)畫(huà)的單擊事件觸發(fā)位置為移動(dòng)后的位置础爬,view動(dòng)畫(huà)仍然在原位置甫贯;
(7)硬件加速:使用動(dòng)畫(huà)的過(guò)程中,建議開(kāi)啟硬件加速看蚜,這樣會(huì)提高動(dòng)畫(huà)的流暢性叫搁。
參考:
http://keeganlee.me/post/android/20151026
《android 開(kāi)發(fā)藝術(shù)探索》——任玉剛