動(dòng)畫(huà)可以分為三類(lèi):View動(dòng)畫(huà),幀動(dòng)畫(huà),屬性動(dòng)畫(huà)。
一、View動(dòng)畫(huà)
1.View動(dòng)畫(huà)包括四種:平移動(dòng)畫(huà),縮放動(dòng)畫(huà),旋轉(zhuǎn)動(dòng)畫(huà),透明度動(dòng)畫(huà)。分別對(duì)應(yīng)著Animation的四個(gè)子類(lèi):TranslateAnimation,ScaleAnimation,RotateAnimation,AlphaAnimation。幀動(dòng)畫(huà)其實(shí)也是View動(dòng)畫(huà)的一種。
2.View動(dòng)畫(huà)是對(duì)View的影像做動(dòng)畫(huà),并不是真正的改變View的位置等狀態(tài)。通過(guò)View動(dòng)畫(huà)將View平移后,新位置不能觸發(fā)點(diǎn)擊事件,老位置可以。
3.使用View動(dòng)畫(huà),在動(dòng)畫(huà)完成后,有時(shí)候會(huì)出現(xiàn)View無(wú)法隱藏的現(xiàn)象,即設(shè)置setVisiable(View.GONE)沒(méi)有效果,這個(gè)時(shí)候只需要調(diào)用view.clearAnimation()清除View動(dòng)畫(huà)即可解決此問(wèn)題。
4.View動(dòng)畫(huà)的使用:
通過(guò)XMl定義:Animation animation = AnimationUtils.loadAnimation(context,resId);
view.startAnimation(animation);
通過(guò)純代碼:
AlphaAnimation alphaAnimation = new AlphaAnimation(0,1);
alphaAnimation.setDuration(300);
view.startAnimation(alphaAnimation);
為動(dòng)畫(huà)設(shè)置監(jiān)聽(tīng):
animation.setAnimationListener(new Animation.AnimationListener());
5.自定義View動(dòng)畫(huà):
繼承Animation類(lèi),重寫(xiě)initialize和applyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中進(jìn)行相應(yīng)的矩陣變換。很多時(shí)候需要用到Camera來(lái)簡(jiǎn)化矩陣變換的過(guò)程,需要用到數(shù)學(xué)上面的東西。實(shí)際開(kāi)發(fā)中很少用到自定義View動(dòng)畫(huà)。
二、幀動(dòng)畫(huà)
1.幀動(dòng)畫(huà)是順序播放一組預(yù)先定義好的圖片,類(lèi)似于電影播放。對(duì)應(yīng)于AnimationDrawable類(lèi)。
XML中的定義方式如下(在drawable文件夾中):
然后將上訴Drawable作為View的背景并通過(guò)AnimationDrawable來(lái)播放:
view.setBackgroundResource(R.drawable.resid);
AnimationDrawable drawable = view.getBackground();
drawable.start();
2.幀動(dòng)畫(huà)使用比較簡(jiǎn)單,但是比較容易引起OOM,所以在使用幀動(dòng)畫(huà)時(shí)應(yīng)盡量避免使用過(guò)多尺寸較大的圖片。
3.View動(dòng)畫(huà)的一個(gè)特殊使用場(chǎng)景,就是為ViewGroup中的所有子元素添加出場(chǎng)效果,需要用到LayoutAnimation。
定義LayoutAnimation:
android:delay="0.5"
android:animationOrder="normal"
android:animation="@anim/anim_item">
其中anim_item就是具體的View動(dòng)畫(huà),然后為ViewGroup指定android:layoutAnimation屬性 android:layoutAnimation=“@anim/layout_animation”;
4.除了在XML中指定LayoutAnimation外,還可以通過(guò)LayoutAnimationController來(lái)實(shí)現(xiàn):
Animation animation = AnimationUtils.loadAnimation(context,resid);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.5f);
controller.setOrder(LayoutAnimationController.ORDER_NORMAL);
viewGroup.setLayoutAnimation(controller);
5.Activity的切換效果:
調(diào)用overridePendingTransition(resid,resid),但是必須在startActivity跟finish之后調(diào)用才有效果!
三、屬性動(dòng)畫(huà)
1.屬性動(dòng)畫(huà)是API 11(3.1) 新加入的特性,和View動(dòng)畫(huà)不同,它對(duì)作用對(duì)象進(jìn)行了擴(kuò)展,屬性動(dòng)畫(huà)可以對(duì)任何對(duì)象做動(dòng)畫(huà),甚至還可以沒(méi)有對(duì)象。
動(dòng)畫(huà)默認(rèn)時(shí)間間隔300ms,默認(rèn)幀率10ms/幀。其可以達(dá)到的效果是:在一個(gè)時(shí)間間隔內(nèi)完成對(duì)象從一個(gè)屬性值到另一個(gè)屬性值的改變。
2.比較常用的幾個(gè)動(dòng)畫(huà)類(lèi):ValueAnimator,ObjectAnimator,AnimatorSet。其中ObjectAnimator繼承自ValueAnimator,AnimatorSet和ValueAnimator都繼承自Animator。
AnimatorSet是個(gè)動(dòng)畫(huà)集合,可以定義一組動(dòng)畫(huà)。
3.Animator是個(gè)抽象類(lèi),實(shí)現(xiàn)了Cloneable接口,Cloneable接口里面什么都沒(méi)有。
4.ValueAnimator不能對(duì)具體的某個(gè)屬性做動(dòng)畫(huà),但是可以對(duì)一個(gè)值做動(dòng)畫(huà),就是實(shí)現(xiàn)值的漸變。例如:ValueAnimator.ofInt(1.100)。具體的對(duì)某個(gè)屬性值的動(dòng)畫(huà)的實(shí)現(xiàn),在它的子類(lèi)ObjectAnimator中。不過(guò)可以通過(guò)ValueAnimator對(duì)值的漸變,結(jié)合AnimatorUpdateListener可以做很多事情(包括實(shí)現(xiàn)屬性動(dòng)畫(huà))。
5.使用方法示例:
通過(guò)代碼:ObjectAnimator.ofFloat(view,"propertyName",value).start();
通過(guò)XML:屬性動(dòng)畫(huà)需要定義在res/animator/目錄下面,語(yǔ)法如下:
android:ordering="together">
android:propertyName="x"
android:duration="300"
android:valueTo="200"
android:valueType="floatType"/>
android:propertyName="y"
android:duration="300"
android:valueTo="300"
android:valueType="floatType"/>
一個(gè)下面可以包含多個(gè)。
AnimatorSet set = AnimatorSet.loadAnimator(context,resid);
set.setTarget(view);
set.start();
6.插值器與估值器
插值器:根據(jù)時(shí)間流逝的百分比來(lái)計(jì)算出當(dāng)前屬性值改變的百分比。TimeInterpolator
系統(tǒng)預(yù)置的有LinearInterpolator(線性插值器:勻速動(dòng)畫(huà))、AccelerateDecelerateInterpolator(加速減速插值器:動(dòng)畫(huà)兩頭慢,中間快)、DecelerateInterpolator(減速插值器:動(dòng)畫(huà)越來(lái)越慢)等。
這些插值器都繼承自BaseInterpolator,BaseInterpolator實(shí)現(xiàn)了Interpolator接口,而Interpolator幾口繼承自TimeInterpolator接口。TimeInterpolator接口定義一個(gè)方法叫:
public float getInterpolation(float intput); 所以自定義插值器的話,只需要實(shí)現(xiàn)Interpolator或者TimeInterpolator接口。
估值器:根據(jù)當(dāng)前屬性改變的百分比來(lái)計(jì)算改變后的屬性值。TypeEvaluator
系統(tǒng)預(yù)置的估值器有IntEvaluator(針對(duì)整型屬性)、FloatEvaluator(針對(duì)浮點(diǎn)型)、ArgbEvaluator(針對(duì)Color屬性)。
它們都實(shí)現(xiàn)了TypeEvaluator接口,TypeEvaluator接口中定義了一個(gè)方法:
public T evaluate(float fraction, T startValue, T endValue);
7.屬性動(dòng)畫(huà)要求對(duì)象的該屬性必須有set方法,get方法可選(如果提供了動(dòng)畫(huà)的初始值,則不需要get方法,若沒(méi)有提供初始值,那么系統(tǒng)會(huì)調(diào)用get方法獲取初始值)。
8.對(duì)任意屬性做動(dòng)畫(huà):如果對(duì)象沒(méi)有該屬性的set或者get方法,可以通過(guò)如下3種方式解決:
①:給對(duì)象加上get和set方法,如果有權(quán)限的話。
②:用一個(gè)類(lèi)來(lái)包裝原始對(duì)象,間接為其提供get,set方法。(此方法比較常用)
③:采用ValueAnimator,監(jiān)聽(tīng)動(dòng)畫(huà)過(guò)程,自己實(shí)現(xiàn)屬性的改變。
關(guān)于方法2的例子:
例如如果對(duì)Button的width屬性做動(dòng)畫(huà),但是Button的width的set方法是設(shè)置最大跟最小寬度,而不是改變其寬度。get方法是獲取寬度。這樣的話直接對(duì)width屬性做動(dòng)畫(huà)是沒(méi)有效果的,因?yàn)楦静皇窃O(shè)置的那個(gè)我們想改變的寬度。這樣我們可以通過(guò)一個(gè)類(lèi)去包裝Button,然后在這個(gè)類(lèi)的setWidth方法中,去
button.getLayoutParams().width = width.
button.requestLayout();
這樣就間接的給Button的寬度做了動(dòng)畫(huà)。因?yàn)橹苯訛檫@個(gè)寬度去加set方法是行不通的,Button封裝在SDK中,我們改變不了。
9.在動(dòng)畫(huà)的內(nèi)部,get,set方法的調(diào)用是通過(guò)反射來(lái)實(shí)現(xiàn)的。
注意:屬性動(dòng)畫(huà)必須運(yùn)行在有Looper的線程中,否則會(huì)拋出“Animators may only be run on Looper threads”異常。