Android應(yīng)用開(kāi)發(fā)之所有動(dòng)畫(huà)使用詳解

轉(zhuǎn)載一篇高質(zhì)量博文,原地址請(qǐng)戳這里轉(zhuǎn)載下來(lái)方便今后查看。
1 背景
不能只分析源碼呀,分析的同時(shí)也要整理歸納基礎(chǔ)知識(shí),剛好有人微博私信讓全面說(shuō)說(shuō)Android的動(dòng)畫(huà),所以今天來(lái)一發(fā)Android應(yīng)用的各種Animation大集合。英文厲害的請(qǐng)直接移步參考Android Developer
Android系統(tǒng)提供了很多豐富的API去實(shí)現(xiàn)UI的2D與3D動(dòng)畫(huà),最主要的劃分可以分為如下幾類:
View Animation: 視圖動(dòng)畫(huà)在古老的Android版本系統(tǒng)中就已經(jīng)提供了,只能被用來(lái)設(shè)置View的動(dòng)畫(huà)。

Drawable Animation: 這種動(dòng)畫(huà)(也叫Frame動(dòng)畫(huà)、幀動(dòng)畫(huà))其實(shí)可以劃分到視圖動(dòng)畫(huà)的類別,專門用來(lái)一個(gè)一個(gè)的顯示Drawable的resources,就像放幻燈片一樣。

Property Animation: 屬性動(dòng)畫(huà)只對(duì)Android 3.0(API 11)以上版本的Android系統(tǒng)才有效,這種動(dòng)畫(huà)可以設(shè)置給任何Object,包括那些還沒(méi)有渲染到屏幕上的對(duì)象。這種動(dòng)畫(huà)是可擴(kuò)展的,可以讓你自定義任何類型和屬性的動(dòng)畫(huà)。

可以看見(jiàn),當(dāng)前應(yīng)用程序開(kāi)發(fā)涉及的主要?jiǎng)赢?huà)也就這三大類,我們接下來(lái)以類別為基礎(chǔ)來(lái)慢慢展開(kāi)說(shuō)明。
【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載請(qǐng)注明出處。點(diǎn)我開(kāi)始Android技術(shù)交流
2 View Animation(視圖動(dòng)畫(huà))使用詳解
2-1 視圖動(dòng)畫(huà)概述
視圖動(dòng)畫(huà),也叫Tween(補(bǔ)間)動(dòng)畫(huà)可以在一個(gè)視圖容器內(nèi)執(zhí)行一系列簡(jiǎn)單變換(位置、大小、旋轉(zhuǎn)、透明度)。譬如,如果你有一個(gè)TextView對(duì)象,您可以移動(dòng)、旋轉(zhuǎn)、縮放、透明度設(shè)置其文本,當(dāng)然,如果它有一個(gè)背景圖像,背景圖像會(huì)隨著文本變化。
補(bǔ)間動(dòng)畫(huà)通過(guò)XML或Android代碼定義,建議使用XML文件定義,因?yàn)樗呖勺x性、可重用性。
如下是視圖動(dòng)畫(huà)相關(guān)的類繼承關(guān)系:

這里寫圖片描述

java類名 xml關(guān)鍵字 描述信息
AlphaAnimation <alpha> 放置在res/anim/目錄下漸變透明度動(dòng)畫(huà)效果
RotateAnimation <rotate> 放置在res/anim/目錄下畫(huà)面轉(zhuǎn)移旋轉(zhuǎn)動(dòng)畫(huà)效果
ScaleAnimation <scale> 放置在res/anim/目錄下漸變尺寸伸縮動(dòng)畫(huà)效果
TranslateAnimation <translate> 放置在res/anim/目錄下畫(huà)面轉(zhuǎn)換位置移動(dòng)動(dòng)畫(huà)效果
AnimationSet <set> 放置在res/anim/目錄下一個(gè)持有其它動(dòng)畫(huà)元素alpha、scale、translate、rotate或者其它set元素的容器

通過(guò)上圖和上表可以直觀的看出來(lái)補(bǔ)間動(dòng)畫(huà)的關(guān)系及種類了吧,接下來(lái)我們就詳細(xì)一個(gè)一個(gè)的介紹一下各種補(bǔ)間動(dòng)畫(huà)。
2-2 視圖動(dòng)畫(huà)詳細(xì)說(shuō)明
可以看出來(lái)Animation抽象類是所有補(bǔ)間動(dòng)畫(huà)類的基類,所以基類會(huì)提供一些通用的動(dòng)畫(huà)屬性方法,如下我們就來(lái)詳細(xì)看看這些屬性,關(guān)于這些屬性詳細(xì)官方解釋翻墻點(diǎn)擊我或者翻墻點(diǎn)擊我
2-2-1 Animation屬性詳解

xml屬性 java方法 解釋
android:detachWallpaper setDetachWallpaper(boolean) 是否在壁紙上運(yùn)行
android:duration setDuration(long) 動(dòng)畫(huà)持續(xù)時(shí)間,毫秒為單位
android:fillAfter setFillAfter(boolean) 控件動(dòng)畫(huà)結(jié)束時(shí)是否保持動(dòng)畫(huà)最后的狀態(tài)
android:fillBefore setFillBefore(boolean) 控件動(dòng)畫(huà)結(jié)束時(shí)是否還原到開(kāi)始動(dòng)畫(huà)前的狀態(tài)
android:fillEnabled setFillEnabled(boolean) 與android:fillBefore效果相同
android:interpolator setInterpolator(Interpolator) 設(shè)定插值器(指定的動(dòng)畫(huà)效果,譬如回彈等)
android:repeatCount setRepeatCount(int) 重復(fù)次數(shù)
android:repeatMode setRepeatMode(int) 重復(fù)類型有兩個(gè)值,reverse表示倒序回放,restart表示從頭播放
android:startOffset setStartOffset(long) 調(diào)用start函數(shù)之后等待開(kāi)始運(yùn)行的時(shí)間,單位為毫秒
android:zAdjustment setZAdjustment(int) 表示被設(shè)置動(dòng)畫(huà)的內(nèi)容運(yùn)行時(shí)在Z軸上的位置(top/bottom/normal),默認(rèn)為normal

也就是說(shuō),無(wú)論我們補(bǔ)間動(dòng)畫(huà)的哪一種都已經(jīng)具備了這種屬性,也都可以設(shè)置使用這些屬性中的一個(gè)或多個(gè)。
那接下來(lái)我們就看看每種補(bǔ)間動(dòng)畫(huà)特有的一些屬性說(shuō)明吧。
2-2-2 Alpha屬性詳解

xml屬性 java方法 解釋
android:fromAlpha AlphaAnimation(float fromAlpha, …) 動(dòng)畫(huà)開(kāi)始的透明度(0.0到1.0,0.0是全透明,1.0是不透明)
android:toAlpha AlphaAnimation(…, float toAlpha) 動(dòng)畫(huà)結(jié)束的透明度,同上

2-2-3 Rotate屬性詳解

xml屬性 java方法 解釋
android:fromDegrees RotateAnimation(float fromDegrees, …) 旋轉(zhuǎn)開(kāi)始角度,正代表順時(shí)針度數(shù),負(fù)代表逆時(shí)針度數(shù)
android:toDegrees RotateAnimation(…, float toDegrees, …) 旋轉(zhuǎn)結(jié)束角度,正代表順時(shí)針度數(shù),負(fù)代表逆時(shí)針度數(shù)
android:pivotX RotateAnimation(…, float pivotX, …) 縮放起點(diǎn)X坐標(biāo)(數(shù)值、百分?jǐn)?shù)、百分?jǐn)?shù)p,譬如50表示以當(dāng)前View左上角坐標(biāo)加50px為初始點(diǎn)、50%表示以當(dāng)前View的左上角加上當(dāng)前View寬高的50%做為初始點(diǎn)、50%p表示以當(dāng)前View的左上角加上父控件寬高的50%做為初始點(diǎn))
android:pivotY RotateAnimation(…, float pivotY) 縮放起點(diǎn)Y坐標(biāo),同上規(guī)律

2-2-4 Scale屬性詳解

xml屬性 java方法 解釋
android:fromXScale ScaleAnimation(float fromX, …) 初始X軸縮放比例,1.0表示無(wú)變化
android:toXScale ScaleAnimation(…, float toX, …) 結(jié)束X軸縮放比例
android:fromYScale ScaleAnimation(…, float fromY, …) 初始Y軸縮放比例
android:toYScale ScaleAnimation(…, float toY, …) 結(jié)束Y軸縮放比例
android:pivotX ScaleAnimation(…, float pivotX, …) 縮放起點(diǎn)X軸坐標(biāo)(數(shù)值、百分?jǐn)?shù)、百分?jǐn)?shù)p,譬如50表示以當(dāng)前View左上角坐標(biāo)加50px為初始點(diǎn)、50%表示以當(dāng)前View的左上角加上當(dāng)前View寬高的50%做為初始點(diǎn)、50%p表示以當(dāng)前View的左上角加上父控件寬高的50%做為初始點(diǎn))
android:pivotY ScaleAnimation(…, float pivotY) 縮放起點(diǎn)Y軸坐標(biāo),同上規(guī)律

2-2-5 Translate屬性詳解

xml屬性 java方法 解釋
android:fromXDelta TranslateAnimation(float fromXDelta, …) 起始點(diǎn)X軸坐標(biāo)(數(shù)值、百分?jǐn)?shù)、百分?jǐn)?shù)p,譬如50表示以當(dāng)前View左上角坐標(biāo)加50px為初始點(diǎn)、50%表示以當(dāng)前View的左上角加上當(dāng)前View寬高的50%做為初始點(diǎn)、50%p表示以當(dāng)前View的左上角加上父控件寬高的50%做為初始點(diǎn))
android:fromYDelta TranslateAnimation(…, float fromYDelta, …) 起始點(diǎn)Y軸從標(biāo),同上規(guī)律
android:toXDelta TranslateAnimation(…, float toXDelta, …) 結(jié)束點(diǎn)X軸坐標(biāo),同上規(guī)律
android:toYDelta TranslateAnimation(…, float toYDelta) 結(jié)束點(diǎn)Y軸坐標(biāo),同上規(guī)律

2-2-6 AnimationSet詳解
AnimationSet繼承自Animation,是上面四種的組合容器管理類,沒(méi)有自己特有的屬性,他的屬性繼承自Animation,所以特別注意,當(dāng)我們對(duì)set標(biāo)簽使用Animation的屬性時(shí)會(huì)對(duì)該標(biāo)簽下的所有子控件都產(chǎn)生影響。
2-3 視圖動(dòng)畫(huà)使用方法
通過(guò)上面對(duì)于動(dòng)畫(huà)的屬性介紹之后我們來(lái)看看在Android中這些動(dòng)畫(huà)如何使用(PS:這里直接演示xml方式,至于Java方式太簡(jiǎn)單了就不說(shuō)了),如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@[package:]anim/interpolator_resource"
    android:shareInterpolator=["true" | "false"] >
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
    <set>
        ...
    </set>
</set>
ImageView spaceshipImage = (ImageView) findViewById(R.id.spaceshipImage);
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_jump);
spaceshipImage.startAnimation(hyperspaceJumpAnimation);

上面就是一個(gè)標(biāo)準(zhǔn)的使用我們定義的補(bǔ)間動(dòng)畫(huà)的模板。至于補(bǔ)間動(dòng)畫(huà)的使用,Animation還有如下一些比較實(shí)用的方法介紹:

Animation類的方法 解釋
reset() 重置Animation的初始化
cancel() 取消Animation動(dòng)畫(huà)
start() 開(kāi)始Animation動(dòng)畫(huà)
setAnimationListener(AnimationListener listener) 給當(dāng)前Animation設(shè)置動(dòng)畫(huà)監(jiān)聽(tīng)
hasStarted() 判斷當(dāng)前Animation是否開(kāi)始
hasEnded() 判斷當(dāng)前Animation是否結(jié)束

既然補(bǔ)間動(dòng)畫(huà)只能給View使用,那就來(lái)看看View中和動(dòng)畫(huà)相關(guān)的幾個(gè)常用方法吧,如下:

View類的常用動(dòng)畫(huà)操作方法 解釋
startAnimation(Animation animation) 對(duì)當(dāng)前View開(kāi)始設(shè)置的Animation動(dòng)畫(huà)
clearAnimation() 取消當(dāng)View在執(zhí)行的Animation動(dòng)畫(huà)

到此整個(gè)Android的補(bǔ)間動(dòng)畫(huà)常用詳細(xì)屬性及方法全部介紹完畢,如有特殊的屬性需求可以訪問(wèn)Android Developer查閱即可。如下我們就來(lái)個(gè)綜合大演練。
2-4 視圖動(dòng)畫(huà)注意事項(xiàng)
關(guān)于視圖動(dòng)畫(huà)(補(bǔ)間動(dòng)畫(huà))的例子我就不介紹了,網(wǎng)上簡(jiǎn)直多的都泛濫了。只是強(qiáng)調(diào)在使用補(bǔ)間動(dòng)畫(huà)時(shí)注意如下一點(diǎn)即可:
特別特別注意:補(bǔ)間動(dòng)畫(huà)執(zhí)行之后并未改變View的真實(shí)布局屬性值。切記這一點(diǎn),譬如我們?cè)贏ctivity中有一個(gè)Button在屏幕上方,我們?cè)O(shè)置了平移動(dòng)畫(huà)移動(dòng)到屏幕下方然后保持動(dòng)畫(huà)最后執(zhí)行狀態(tài)呆在屏幕下方,這時(shí)如果點(diǎn)擊屏幕下方動(dòng)畫(huà)執(zhí)行之后的Button是沒(méi)有任何反應(yīng)的,而點(diǎn)擊原來(lái)屏幕上方?jīng)]有Button的地方卻響應(yīng)的是點(diǎn)擊Button的事件。
2-5 視圖動(dòng)畫(huà)Interpolator插值器詳解
2-5-1 插值器簡(jiǎn)介
介紹補(bǔ)間動(dòng)畫(huà)插值器之前我們先來(lái)看一幅圖,如下:

這里寫圖片描述

可以看見(jiàn)其實(shí)各種插值器都是實(shí)現(xiàn)了Interpolator接口而已,同時(shí)可以看見(jiàn)系統(tǒng)提供了許多已經(jīng)實(shí)現(xiàn)OK的插值器,具體如下:

java類 xml id值 描述
AccelerateDecelerateInterpolator @android:anim/accelerate_decelerate_interpolator 動(dòng)畫(huà)始末速率較慢,中間加速
AccelerateInterpolator @android:anim/accelerate_interpolator 動(dòng)畫(huà)開(kāi)始速率較慢,之后慢慢加速
AnticipateInterpolator @android:anim/anticipate_interpolator 開(kāi)始的時(shí)候從后向前甩
AnticipateOvershootInterpolator @android:anim/anticipate_overshoot_interpolator 類似上面AnticipateInterpolator
BounceInterpolator @android:anim/bounce_interpolator 動(dòng)畫(huà)結(jié)束時(shí)彈起
CycleInterpolator @android:anim/cycle_interpolator 循環(huán)播放速率改變?yōu)檎仪€
DecelerateInterpolator @android:anim/decelerate_interpolator 動(dòng)畫(huà)開(kāi)始快然后慢
LinearInterpolator @android:anim/linear_interpolator 動(dòng)畫(huà)勻速改變
OvershootInterpolator @android:anim/overshoot_interpolator 向前彈出一定值之后回到原來(lái)位置
PathInterpolator 新增,定義路徑坐標(biāo)后按照路徑坐標(biāo)來(lái)跑。

如上就是系統(tǒng)提供的一些插值器,下面我們來(lái)看看怎么使用他們。
2-5-2 插值器使用方法
插值器的使用比較簡(jiǎn)答,如下:

<set android:interpolator="@android:anim/accelerate_interpolator">
    ...
</set>

2-5-3 插值器的自定義
有時(shí)候你會(huì)發(fā)現(xiàn)系統(tǒng)提供的插值器不夠用,可能就像View一樣需要自定義。所以接下來(lái)我們來(lái)看看插值器的自定義,關(guān)于插值器的自定義分為兩種實(shí)現(xiàn)方式,xml自定義實(shí)現(xiàn)(其實(shí)就是對(duì)現(xiàn)有的插值器的一些屬性修改)或者java代碼實(shí)現(xiàn)方式。如下我們來(lái)說(shuō)說(shuō)。
先看看XML自定義插值器的步驟:
在res/anim/目錄下創(chuàng)建filename.xml文件。
修改你準(zhǔn)備自定義的插值器如下:

<?xml version="1.0" encoding="utf-8"?>
<InterpolatorName xmlns:android="http://schemas.android.com/apk/res/android"
    android:attribute_name="value"
    />

在你的補(bǔ)間動(dòng)畫(huà)文件中引用該文件即可。

可以看見(jiàn)上面第二步修改的是現(xiàn)有插值器的一些屬性,但是有些插值器卻不具備修改屬性,具體如下:

<accelerateDecelerateInterpolator>
無(wú)可自定義的attribute。

<accelerateInterpolator>
android:factor 浮點(diǎn)值,加速速率(默認(rèn)值為1)。

<anticipateInterploator>
android:tension 浮點(diǎn)值,起始點(diǎn)后拉的張力數(shù)(默認(rèn)值為2)。

<anticipateOvershootInterpolator>
android:tension 浮點(diǎn)值,起始點(diǎn)后拉的張力數(shù)(默認(rèn)值為2)。 android:extraTension 浮點(diǎn)值,拉力的倍數(shù)(默認(rèn)值為1.5)。

<bounceInterpolator>
無(wú)可自定義的attribute。

<cycleInterplolator>
android:cycles 整形,循環(huán)的個(gè)數(shù)(默認(rèn)為1)。

<decelerateInterpolator>
android:factor 浮點(diǎn)值,減速的速率(默認(rèn)為1)。

<linearInterpolator>
無(wú)可自定義的attribute。

<overshootInterpolator>
android:tension 浮點(diǎn)值,超出終點(diǎn)后的張力(默認(rèn)為2)。

再來(lái)看看Java自定義插值器的(Java自定義插值器其實(shí)是xml自定義的升級(jí),也就是說(shuō)如果我們修改xml的屬性還不能滿足需求,那就可以選擇通過(guò)Java來(lái)實(shí)現(xiàn))方式。
可以看見(jiàn)上面所有的Interpolator都實(shí)現(xiàn)了Interpolator接口,而Interpolator接口又繼承自TimeInterpolator,TimeInterpolator接口定義了一個(gè)float getInterpolation(float input);方法,這個(gè)方法是由系統(tǒng)調(diào)用的,其中的參數(shù)input代表動(dòng)畫(huà)的時(shí)間,在0和1之間,也就是開(kāi)始和結(jié)束之間。
如下就是一個(gè)動(dòng)畫(huà)始末速率較慢、中間加速的AccelerateDecelerateInterpolator插值器:

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    ......
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
    ......
}

到此整個(gè)補(bǔ)間動(dòng)畫(huà)與補(bǔ)間動(dòng)畫(huà)的插值器都分析完畢了,接下來(lái)看下別的動(dòng)畫(huà)。
【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載請(qǐng)注明出處。點(diǎn)我開(kāi)始Android技術(shù)交流
3 Drawable Animation(Drawable動(dòng)畫(huà))使用詳解
3-1 Drawable動(dòng)畫(huà)概述
Drawable動(dòng)畫(huà)其實(shí)就是Frame動(dòng)畫(huà)(幀動(dòng)畫(huà)),它允許你實(shí)現(xiàn)像播放幻燈片一樣的效果,這種動(dòng)畫(huà)的實(shí)質(zhì)其實(shí)是Drawable,所以這種動(dòng)畫(huà)的XML定義方式文件一般放在res/drawable/目錄下。具體關(guān)于幀動(dòng)畫(huà)的xml使用方式翻墻點(diǎn)擊我查看,java方式翻墻點(diǎn)擊我查看。
如下圖就是幀動(dòng)畫(huà)的源碼文件:

這里寫圖片描述

可以看見(jiàn)實(shí)際的真實(shí)父類就是Drawable。
3-2 Drawable動(dòng)畫(huà)詳細(xì)說(shuō)明
我們依舊可以使用xml或者java方式實(shí)現(xiàn)幀動(dòng)畫(huà)。但是依舊推薦使用xml,具體如下:
**<animation-list>
** 必須是根節(jié)點(diǎn),包含一個(gè)或者多個(gè)<item>
元素,屬性有:
android:oneshot true代表只執(zhí)行一次,false循環(huán)執(zhí)行。
**<item>
** 類似一幀的動(dòng)畫(huà)資源。

**<item>
** animation-list的子項(xiàng),包含屬性如下:
android:drawable 一個(gè)frame的Drawable資源。
android:duration 一個(gè)frame顯示多長(zhǎng)時(shí)間。

3-3 Drawable動(dòng)畫(huà)實(shí)例演示
關(guān)于幀動(dòng)畫(huà)相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,這里給出一個(gè)常規(guī)使用框架,如下:

<!-- 注意:rocket.xml文件位于res/drawable/目錄下 -->
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot=["true" | "false"] >
    <item
        android:drawable="@[package:]drawable/drawable_resource_name"
        android:duration="integer" />
</animation-list>
ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);
rocketImage.setBackgroundResource(R.drawable.rocket_thrust);

rocketAnimation = (AnimationDrawable) rocketImage.getBackground();
rocketAnimation.start();

特別注意,AnimationDrawable的start()方法不能在Activity的onCreate方法中調(diào)運(yùn),因?yàn)锳nimationDrawable還未完全附著到window上,所以最好的調(diào)運(yùn)時(shí)機(jī)是onWindowFocusChanged()方法中。
至此幀動(dòng)畫(huà)也就說(shuō)明完成了。讓我們接下來(lái)進(jìn)入Android更牛叉的動(dòng)畫(huà)類型。
【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載請(qǐng)注明出處。點(diǎn)我開(kāi)始Android技術(shù)交流
4 Property Animation(屬性動(dòng)畫(huà))使用詳解
在使用屬性動(dòng)畫(huà)之前先來(lái)看幾個(gè)常用的View屬性成員:
translationX,translationY:控制View的位置,值是相對(duì)于View容器左上角坐標(biāo)的偏移。
rotationX,rotationY:控制相對(duì)于軸心旋轉(zhuǎn)。
x,y:控制View在容器中的位置,即左上角坐標(biāo)加上translationX和translationY的值。
alpha:控制View對(duì)象的alpha透明度值。

這幾個(gè)常用的屬性相信大家都很熟悉,接下來(lái)的屬性動(dòng)畫(huà)我們就從這里展開(kāi)。
4-1 屬性動(dòng)畫(huà)概述
Android 3.0以后引入了屬性動(dòng)畫(huà),屬性動(dòng)畫(huà)可以輕而易舉的實(shí)現(xiàn)許多View動(dòng)畫(huà)做不到的事,上面也看見(jiàn)了,View動(dòng)畫(huà)無(wú)非也就做那幾種事情,別的也搞不定,而屬性動(dòng)畫(huà)就可以的,譬如3D旋轉(zhuǎn)一張圖片。其實(shí)說(shuō)白了,你記住一點(diǎn)就行,屬性動(dòng)畫(huà)實(shí)現(xiàn)原理就是修改控件的屬性值實(shí)現(xiàn)的動(dòng)畫(huà)。
具體先看下類關(guān)系:

/**
 * This is the superclass for classes which provide basic support for animations which can be
 * started, ended, and have <code>AnimatorListeners</code> added to them.
 */
public abstract class Animator implements Cloneable {
    ......
}

所有的屬性動(dòng)畫(huà)的抽象基類就是他。我們看下他的實(shí)現(xiàn)子類:
這里寫圖片描述

其實(shí)可以看見(jiàn),屬性動(dòng)畫(huà)的實(shí)現(xiàn)有7個(gè)類(PS,之所以類繼承關(guān)系列表會(huì)出來(lái)那么多是因?yàn)槲蚁螺d了所有版本的SDK,你只用關(guān)注我紅點(diǎn)標(biāo)注的就行,妹的,ubuntu下圖片處理工具怎么都這么難用),進(jìn)去粗略分析可以發(fā)現(xiàn),好幾個(gè)是hide的類,而其他可用的類繼承關(guān)系又如下:


這里寫圖片描述
java類名 xml關(guān)鍵字 描述信息
ValueAnimator <animator> 放置在res/animator/目錄下在一個(gè)特定的時(shí)間里執(zhí)行一個(gè)動(dòng)畫(huà)
TimeAnimator 不支持/點(diǎn)我查看原因 時(shí)序監(jiān)聽(tīng)回調(diào)工具
ObjectAnimator <objectAnimator> 放置在res/animator/目錄下一個(gè)對(duì)象的一個(gè)屬性動(dòng)畫(huà)
AnimatorSet <set> 放置在res/animator/目錄下 動(dòng)畫(huà)集合

所以可以看見(jiàn),我們平時(shí)使用屬性動(dòng)畫(huà)的重點(diǎn)就在于AnimatorSet、ObjectAnimator、TimeAnimator、ValueAnimator。所以接下來(lái)我們就來(lái)依次說(shuō)說(shuō)如何使用。
4-2 屬性動(dòng)畫(huà)詳細(xì)說(shuō)明
4-2-1 屬性動(dòng)畫(huà)計(jì)算原理
參看Android官方文檔,英文原版詳情點(diǎn)我查看!
Android屬性動(dòng)畫(huà)(注意最低兼容版本,不過(guò)可以使用開(kāi)源項(xiàng)目來(lái)替代低版本問(wèn)題)提供了以下屬性:
Duration:動(dòng)畫(huà)的持續(xù)時(shí)間;
TimeInterpolation:定義動(dòng)畫(huà)變化速率的接口,所有插值器都必須實(shí)現(xiàn)此接口,如線性、非線性插值器;
TypeEvaluator:用于定義屬性值計(jì)算方式的接口,有int、float、color類型,根據(jù)屬性的起始、結(jié)束值和插值一起計(jì)算出當(dāng)前時(shí)間的屬性值;
Animation sets:動(dòng)畫(huà)集合,即可以同時(shí)對(duì)一個(gè)對(duì)象應(yīng)用多個(gè)動(dòng)畫(huà),這些動(dòng)畫(huà)可以同時(shí)播放也可以對(duì)不同動(dòng)畫(huà)設(shè)置不同的延遲;
Frame refreash delay:多少時(shí)間刷新一次,即每隔多少時(shí)間計(jì)算一次屬性值,默認(rèn)為10ms,最終刷新時(shí)間還受系統(tǒng)進(jìn)程調(diào)度與硬件的影響;
Repeat Country and behavoir:重復(fù)次數(shù)與方式,如播放3次、5次、無(wú)限循環(huán),可以讓此動(dòng)畫(huà)一直重復(fù),或播放完時(shí)向反向播放;

接下來(lái)先來(lái)看官方為了解釋原理給出的兩幅圖(其實(shí)就是初中物理題,不解釋):


這里寫圖片描述

上面就是一個(gè)線性勻速動(dòng)畫(huà),描述了一個(gè)Object的X屬性運(yùn)動(dòng)動(dòng)畫(huà),該對(duì)象的X坐標(biāo)在40ms內(nèi)從0移動(dòng)到40,每10ms刷新一次,移動(dòng)4次,每次移動(dòng)為40/4=10pixel。
這里寫圖片描述
上面是一個(gè)非勻速動(dòng)畫(huà),描述了一個(gè)Object的X屬性運(yùn)動(dòng)動(dòng)畫(huà),該對(duì)象的X坐標(biāo)在40ms內(nèi)從0移動(dòng)到40,每10ms刷新一次,移動(dòng)4次,但是速率不同,開(kāi)始和結(jié)束的速度要比中間部分慢,即先加速后減速。
接下來(lái)我們來(lái)詳細(xì)的看一下,屬性動(dòng)畫(huà)系統(tǒng)的重要組成部分是如何計(jì)算動(dòng)畫(huà)值的,下圖描述了如上面所示動(dòng)畫(huà)的實(shí)現(xiàn)作用過(guò)程。
這里寫圖片描述

其中的ValueAnimator是動(dòng)畫(huà)的執(zhí)行類,跟蹤了當(dāng)前動(dòng)畫(huà)的執(zhí)行時(shí)間和當(dāng)前時(shí)間下的屬性值;ValueAnimator封裝了動(dòng)畫(huà)的TimeInterpolator時(shí)間插值器和一個(gè)TypeEvaluator類型估值,用于設(shè)置動(dòng)畫(huà)屬性的值,就像上面圖2非線性動(dòng)畫(huà)里,TimeInterpolator使用了AccelerateDecelerateInterpolator、TypeEvaluator使用了IntEvaluator。
為了執(zhí)行一個(gè)動(dòng)畫(huà),你需要?jiǎng)?chuàng)建一個(gè)ValueAnimator,并且指定目標(biāo)對(duì)象屬性的開(kāi)始、結(jié)束值和持續(xù)時(shí)間。在調(diào)用start后,整個(gè)動(dòng)畫(huà)過(guò)程中, ValueAnimator會(huì)根據(jù)已經(jīng)完成的動(dòng)畫(huà)時(shí)間計(jì)算得到一個(gè)0到1之間的分?jǐn)?shù),代表該動(dòng)畫(huà)的已完成動(dòng)畫(huà)百分比。0表示0%,1表示100%,譬如上面圖一線性勻速動(dòng)畫(huà)中總時(shí)間 t = 40 ms,t = 10 ms的時(shí)候是 0.25。
當(dāng)ValueAnimator計(jì)算完已完成動(dòng)畫(huà)分?jǐn)?shù)后,它會(huì)調(diào)用當(dāng)前設(shè)置的TimeInterpolator,去計(jì)算得到一個(gè)interpolated(插值)分?jǐn)?shù),在計(jì)算過(guò)程中,已完成動(dòng)畫(huà)百分比會(huì)被加入到新的插值計(jì)算中。如上圖2非線性動(dòng)畫(huà)中,因?yàn)閯?dòng)畫(huà)的運(yùn)動(dòng)是緩慢加速的,它的插值分?jǐn)?shù)大約是 0.15,小于t = 10ms時(shí)的已完成動(dòng)畫(huà)分?jǐn)?shù)0.25。而在上圖1中,這個(gè)插值分?jǐn)?shù)一直和已完成動(dòng)畫(huà)分?jǐn)?shù)是相同的。
當(dāng)插值分?jǐn)?shù)計(jì)算完成后,ValueAnimator會(huì)根據(jù)插值分?jǐn)?shù)調(diào)用合適的 TypeEvaluator去計(jì)算運(yùn)動(dòng)中的屬性值。
好了,現(xiàn)在我們來(lái)看下代碼就明白這段話了,上面圖2非線性動(dòng)畫(huà)里,TimeInterpolator使用了AccelerateDecelerateInterpolator、TypeEvaluator使用了IntEvaluator。所以這些類都是標(biāo)準(zhǔn)的API,我們來(lái)看下標(biāo)準(zhǔn)API就能類比自己寫了,如下:
首先計(jì)算已完成動(dòng)畫(huà)時(shí)間分?jǐn)?shù)(以10ms為例):t=10ms/40ms=0.25。
接著看如下源碼如何實(shí)現(xiàn)計(jì)算差值分?jǐn)?shù)的:

public class AccelerateDecelerateInterpolator extends BaseInterpolator
        implements NativeInterpolatorFactory {
    public AccelerateDecelerateInterpolator() {
    }
    ......
    //這是我們關(guān)注重點(diǎn),可以發(fā)現(xiàn)如下計(jì)算公式計(jì)算后(input即為時(shí)間因子)插值大約為0.15。
    public float getInterpolation(float input) {
        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
    }
    ......
}

其實(shí)AccelerateDecelerateInterpolator的基類接口就是TimeInterpolator,如下,他只有g(shù)etInterpolation方法,也就是上面我們關(guān)注的方法。

public interface TimeInterpolator {
    float getInterpolation(float input);
}

接著ValueAnimator會(huì)根據(jù)插值分?jǐn)?shù)調(diào)用合適的TypeEvaluator(IntEvaluator)去計(jì)算運(yùn)動(dòng)中的屬性值,如下,因?yàn)閟tartValue = 0,所以屬性值:0+0.15*(40-0)= 6。

public class IntEvaluator implements TypeEvaluator<Integer> {
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

這就是官方給的一個(gè)關(guān)于屬性動(dòng)畫(huà)實(shí)現(xiàn)的過(guò)程及基本原理解釋,相信你看到這里是會(huì)有些迷糊的,沒(méi)關(guān)系,你先有個(gè)大致概念就行,接下來(lái)我們會(huì)慢慢進(jìn)入實(shí)戰(zhàn),因?yàn)锳ndroid的屬性動(dòng)畫(huà)相對(duì)于其他動(dòng)畫(huà)來(lái)說(shuō)涉及的知識(shí)點(diǎn)本來(lái)就比較復(fù)雜,所以我們慢慢來(lái)。
4-2-2 XML方式屬性動(dòng)畫(huà)
在xml中可直接用的屬性動(dòng)畫(huà)節(jié)點(diǎn)有ValueAnimator、ObjectAnimator、AnimatorSet。如下是官方的一個(gè)例子和解釋(詳情點(diǎn)我):

<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>
</set>

<set>屬性解釋:

xml 屬性 解釋
android:ordering 控制子動(dòng)畫(huà)啟動(dòng)方式是先后有序的還是同時(shí)進(jìn)行。 sequentially:動(dòng)畫(huà)按照先后順序;together(默認(rèn)):動(dòng)畫(huà)同時(shí)啟動(dòng);

<objectAnimator>屬性解釋:

xml屬性 解釋
android:propertyName String類型,必須要設(shè)置的節(jié)點(diǎn)屬性,代表要執(zhí)行動(dòng)畫(huà)的屬性(通過(guò)名字引用),辟如你可以指定了一個(gè)View的”alpha” 或者 “backgroundColor” ,這個(gè)objectAnimator元素沒(méi)有對(duì)外說(shuō)明target屬性,所以你不能在XML中設(shè)置執(zhí)行這個(gè)動(dòng)畫(huà),必須通過(guò)調(diào)用loadAnimator()方法加載你的XML動(dòng)畫(huà)資源,然后調(diào)用setTarget()應(yīng)用到具備這個(gè)屬性的目標(biāo)對(duì)象上(譬如TextView)。
android:valueTo float、int或者color類型,必須要設(shè)置的節(jié)點(diǎn)屬性,表明動(dòng)畫(huà)結(jié)束的點(diǎn);如果是顏色的話,由6位十六進(jìn)制的數(shù)字表示。
android:valueFrom 相對(duì)應(yīng)valueTo,動(dòng)畫(huà)的起始點(diǎn),如果沒(méi)有指定,系統(tǒng)會(huì)通過(guò)屬性的get方法獲取,顏色也是6位十六進(jìn)制的數(shù)字表示。
android:duration 動(dòng)畫(huà)的時(shí)長(zhǎng),int類型,以毫秒為單位,默認(rèn)為300毫秒。
android:startOffset 動(dòng)畫(huà)延遲的時(shí)間,從調(diào)用start方法后開(kāi)始計(jì)算,int型,毫秒為單位。
android:repeatCount 一個(gè)動(dòng)畫(huà)的重復(fù)次數(shù),int型,”-1“表示無(wú)限循環(huán),”1“表示動(dòng)畫(huà)在第一次執(zhí)行完成后重復(fù)執(zhí)行一次,也就是兩次,默認(rèn)為0,不重復(fù)執(zhí)行。
android:repeatMode 重復(fù)模式:int型,當(dāng)一個(gè)動(dòng)畫(huà)執(zhí)行完的時(shí)候應(yīng)該如何處理。該值必須是正數(shù)或者是-1,“reverse”會(huì)使得按照動(dòng)畫(huà)向相反的方向執(zhí)行,可實(shí)現(xiàn)類似鐘擺效果。“repeat”會(huì)使得動(dòng)畫(huà)每次都從頭開(kāi)始循環(huán)。
android:valueType 關(guān)鍵參數(shù),如果該value是一個(gè)顏色,那么就不需要指定,因?yàn)閯?dòng)畫(huà)框架會(huì)自動(dòng)的處理顏色值。有intType和floatType(默認(rèn))兩種:分別說(shuō)明動(dòng)畫(huà)值為int和float型。

<objectAnimator>屬性解釋:
同上<objectAnimator>屬性,不多介紹。
XML屬性動(dòng)畫(huà)使用方法:

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animtor.property_animator);
set.setTarget(myObject);
set.start();

4-2-3 Java方式屬性動(dòng)畫(huà)
1、ObjectAnimator:繼承自ValueAnimator,允許你指定要進(jìn)行動(dòng)畫(huà)的對(duì)象以及該對(duì)象的一個(gè)屬性。該類會(huì)根據(jù)計(jì)算得到的新值自動(dòng)更新屬性。大多數(shù)的情況使用ObjectAnimator就足夠了,因?yàn)樗沟媚繕?biāo)對(duì)象動(dòng)畫(huà)值的處理過(guò)程變得足夠簡(jiǎn)單,不用像ValueAnimator那樣自己寫動(dòng)畫(huà)更新的邏輯,但是ObjectAnimator有一定的限制,比如它需要目標(biāo)對(duì)象的屬性提供指定的處理方法(譬如提供getXXX,setXXX方法),這時(shí)候你就需要根據(jù)自己的需求在ObjectAnimator和ValueAnimator中看哪種實(shí)現(xiàn)更方便了。
ObjectAnimator類提供了ofInt、ofFloat、ofObject這個(gè)三個(gè)常用的方法,這些方法都是設(shè)置動(dòng)畫(huà)作用的元素、屬性、開(kāi)始、結(jié)束等任意屬性值。當(dāng)屬性值(上面方法的參數(shù))只設(shè)置一個(gè)時(shí)就把通過(guò)getXXX反射獲取的值作為起點(diǎn),設(shè)置的值作為終點(diǎn);如果設(shè)置兩個(gè)(參數(shù)),那么一個(gè)是開(kāi)始、另一個(gè)是結(jié)束。特別注意:ObjectAnimator的動(dòng)畫(huà)原理是不停的調(diào)用setXXX方法更新屬性值,所有使用ObjectAnimator更新屬性時(shí)的前提是Object必須聲明有g(shù)etXXX和setXXX方法。

我們通常使用ObjectAnimator設(shè)置View已知的屬性來(lái)生成動(dòng)畫(huà),而一般View已知屬性變化時(shí)都會(huì)主動(dòng)觸發(fā)重繪圖操作,所以動(dòng)畫(huà)會(huì)自動(dòng)實(shí)現(xiàn);但是也有特殊情況,譬如作用Object不是View,或者作用的屬性沒(méi)有觸發(fā)重繪,或者我們?cè)谥乩L時(shí)需要做自己的操作,那都可以通過(guò)如下方法手動(dòng)設(shè)置:

ObjectAnimator mObjectAnimator= ObjectAnimator.ofInt(view, "customerDefineAnyThingName", 0,  1).setDuration(2000);
mObjectAnimator.addUpdateListener(new AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                //int value = animation.getAnimatedValue();  可以獲取當(dāng)前屬性值
                //view.postInvalidate();  可以主動(dòng)刷新
                //view.setXXX(value);
                //view.setXXX(value);
                //......可以批量修改屬性
            }
        });

如下是一個(gè)我在項(xiàng)目中的Y軸3D旋轉(zhuǎn)動(dòng)畫(huà)實(shí)現(xiàn)實(shí)例:

ObjectAnimator.ofFloat(view, "rotationY", 0.0f, 360.0f).setDuration(1000).start();

2、PropertyValuesHolder:多屬性動(dòng)畫(huà)同時(shí)工作管理類。有時(shí)候我們需要同時(shí)修改多個(gè)屬性,那就可以用到此類,具體如下:

PropertyValuesHolder a1 = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);  
PropertyValuesHolder a2 = PropertyValuesHolder.ofFloat("translationY", 0, viewWidth);  
......
ObjectAnimator.ofPropertyValuesHolder(view, a1, a2, ......).setDuration(1000).start();

如上代碼就可以實(shí)現(xiàn)同時(shí)修改多個(gè)屬性的動(dòng)畫(huà)啦。
3、ValueAnimator:屬性動(dòng)畫(huà)中的時(shí)間驅(qū)動(dòng),管理著動(dòng)畫(huà)時(shí)間的開(kāi)始、結(jié)束屬性值,相應(yīng)時(shí)間屬性值計(jì)算方法等。包含所有計(jì)算動(dòng)畫(huà)值的核心函數(shù)以及每一個(gè)動(dòng)畫(huà)時(shí)間節(jié)點(diǎn)上的信息、一個(gè)動(dòng)畫(huà)是否重復(fù)、是否監(jiān)聽(tīng)更新事件等,并且還可以設(shè)置自定義的計(jì)算類型。

特別注意:ValueAnimator只是動(dòng)畫(huà)計(jì)算管理驅(qū)動(dòng),設(shè)置了作用目標(biāo),但沒(méi)有設(shè)置屬性,需要通過(guò)updateListener里設(shè)置屬性才會(huì)生效。

ValueAnimator animator = ValueAnimator.ofFloat(0, mContentHeight);  //定義動(dòng)畫(huà)
animator.setTarget(view);   //設(shè)置作用目標(biāo)
animator.setDuration(5000).start();
animator.addUpdateListener(new AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation){
        float value = (float) animation.getAnimatedValue();
        view.setXXX(value);  //必須通過(guò)這里設(shè)置屬性值才有效
        view.mXXX = value;  //不需要setXXX屬性方法
    }
});

大眼看上去可以發(fā)現(xiàn)和ObjectAnimator沒(méi)啥區(qū)別,實(shí)際上正是由于ValueAnimator不直接操作屬性值,所以要操作對(duì)象的屬性可以不需要setXXX與getXXX方法,你完全可以通過(guò)當(dāng)前動(dòng)畫(huà)的計(jì)算去修改任何屬性。
4、AnimationSet:動(dòng)畫(huà)集合,提供把多個(gè)動(dòng)畫(huà)組合成一個(gè)組合的機(jī)制,并可設(shè)置動(dòng)畫(huà)的時(shí)序關(guān)系,如同時(shí)播放、順序播放或延遲播放。具體使用方法比較簡(jiǎn)單,如下:

ObjectAnimator a1 = ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0f);  
ObjectAnimator a2 = ObjectAnimator.ofFloat(view, "translationY", 0f, viewWidth);  
......
AnimatorSet animSet = new AnimatorSet();  
animSet.setDuration(5000);  
animSet.setInterpolator(new LinearInterpolator());   
//animSet.playTogether(a1, a2, ...); //兩個(gè)動(dòng)畫(huà)同時(shí)執(zhí)行  
animSet.play(a1).after(a2); //先后執(zhí)行
......//其他組合方式
animSet.start();  

5、Evaluators相關(guān)類解釋: Evaluators就是屬性動(dòng)畫(huà)系統(tǒng)如何去計(jì)算一個(gè)屬性值。它們通過(guò)Animator提供的動(dòng)畫(huà)的起始和結(jié)束值去計(jì)算一個(gè)動(dòng)畫(huà)的屬性值。

  • IntEvaluator:整數(shù)屬性值。

  • FloatEvaluator:浮點(diǎn)數(shù)屬性值。

  • ArgbEvaluator:十六進(jìn)制color屬性值。

  • TypeEvaluator:用戶自定義屬性值接口,譬如對(duì)象屬性值類型不是int、float、color類型,你必須實(shí)現(xiàn)這個(gè)接口去定義自己的數(shù)據(jù)類型。

既然說(shuō)到這了,那就來(lái)個(gè)例子吧,譬如我們需要實(shí)現(xiàn)一個(gè)自定義屬性類型和計(jì)算規(guī)則的屬性動(dòng)畫(huà),如下類型float[]:

ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(5000);
valueAnimator.setObjectValues(new float[2]); //設(shè)置屬性值類型
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<float[]>()
{
    @Override
    public float[] evaluate(float fraction, float[] startValue,
                            float[] endValue)
    {
        //實(shí)現(xiàn)自定義規(guī)則計(jì)算的float[]類型的屬性值
        float[] temp = new float[2];
        temp[0] = fraction * 2;
        temp[1] = (float)Math.random() * 10 * fraction;
        return temp;
    }
});

valueAnimator.start();
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
    @Override
    public void onAnimationUpdate(ValueAnimator animation)
    {
        float[] xyPos = (float[]) animation.getAnimatedValue();
        view.setHeight(xyPos[0]);   //通過(guò)屬性值設(shè)置View屬性動(dòng)畫(huà)
        view.setWidth(xyPos[1]);    //通過(guò)屬性值設(shè)置View屬性動(dòng)畫(huà)
    }
});

6、Interpolators相關(guān)類解釋:

  • AccelerateDecelerateInterolator:先加速后減速。

  • AccelerateInterpolator:加速。

  • DecelerateInterpolator:減速。

  • AnticipateInterpolator:先向相反方向改變一段再加速播放。

  • AnticipateOvershootInterpolator:先向相反方向改變,再加速播放,會(huì)超出目標(biāo)值然后緩慢移動(dòng)至目標(biāo)值,類似于彈簧回彈。

  • BounceInterpolator:快到目標(biāo)值時(shí)值會(huì)跳躍。

  • CycleIinterpolator:動(dòng)畫(huà)循環(huán)一定次數(shù),值的改變?yōu)橐徽液瘮?shù):Math.sin(2 * mCycles * Math.PI * input)。

  • LinearInterpolator:線性均勻改變。

  • OvershottInterpolator:最后超出目標(biāo)值然后緩慢改變到目標(biāo)值。

  • TimeInterpolator:一個(gè)允許自定義Interpolator的接口,以上都實(shí)現(xiàn)了該接口。

舉個(gè)例子,就像系統(tǒng)提供的標(biāo)準(zhǔn)API一樣,如下就是加速插值器的實(shí)現(xiàn)代碼,我們自定義時(shí)也可以類似實(shí)現(xiàn):

//開(kāi)始很慢然后不斷加速的插值器。
public class AccelerateInterpolator implements Interpolator {
    private final float mFactor;
    private final double mDoubleFactor;

    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }

    ......

    //input  0到1.0。表示動(dòng)畫(huà)當(dāng)前點(diǎn)的值,0表示開(kāi)頭,1表示結(jié)尾。
    //return  插值。值可以大于1超出目標(biāo)值,也可以小于0突破低值。
    @Override
    public float getInterpolation(float input) {
        //實(shí)現(xiàn)核心代碼塊
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }
}

綜上可以發(fā)現(xiàn),我們可以使用現(xiàn)有系統(tǒng)提供標(biāo)準(zhǔn)的東東實(shí)現(xiàn)屬性動(dòng)畫(huà),也可以通過(guò)自定義繼承相關(guān)接口實(shí)現(xiàn)自己的動(dòng)畫(huà),只要實(shí)現(xiàn)上面提到的那些主要方法即可。
4-2-4 Java屬性動(dòng)畫(huà)拓展之ViewPropertyAnimator動(dòng)畫(huà)
在Android API 12時(shí),View中添加了animate方法,具體如下:

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
     ......
     /**
     * This method returns a ViewPropertyAnimator object, which can be used to animate
     * specific properties on this View.
     *
     * @return ViewPropertyAnimator The ViewPropertyAnimator associated with this View.
     */
    public ViewPropertyAnimator animate() {
        if (mAnimator == null) {
            mAnimator = new ViewPropertyAnimator(this);
        }
        return mAnimator;
    }
    ......
}

可以看見(jiàn)通過(guò)View的animate()方法可以得到一個(gè)ViewPropertyAnimator的屬性動(dòng)畫(huà)(有人說(shuō)他沒(méi)有繼承Animator類,是的,他是成員關(guān)系,不是之前那種繼承關(guān)系)。
ViewPropertyAnimator提供了一種非常方便的方法為View的部分屬性設(shè)置動(dòng)畫(huà)(切記,是部分屬性),它可以直接使用一個(gè)Animator對(duì)象設(shè)置多個(gè)屬性的動(dòng)畫(huà);在多屬性設(shè)置動(dòng)畫(huà)時(shí),它比 上面的ObjectAnimator更加牛逼、高效,因?yàn)樗麜?huì)管理多個(gè)屬性的invalidate方法統(tǒng)一調(diào)運(yùn)觸發(fā),而不像上面分別調(diào)用,所以還會(huì)有一些性能優(yōu)化。如下就是一個(gè)例子:

myView.animate().x(0f).y(100f).start(); 

4-2-5 Java屬性動(dòng)畫(huà)拓展之LayoutAnimator容器布局動(dòng)畫(huà)
Property動(dòng)畫(huà)系統(tǒng)還提供了對(duì)ViewGroup中View添加時(shí)的動(dòng)畫(huà)功能,我們可以用LayoutTransition對(duì)ViewGroup中的View進(jìn)行動(dòng)畫(huà)設(shè)置顯示。LayoutTransition的動(dòng)畫(huà)效果都是設(shè)置給ViewGroup,然后當(dāng)被設(shè)置動(dòng)畫(huà)的ViewGroup中添加刪除View時(shí)體現(xiàn)出來(lái)。該類用于當(dāng)前布局容器中有View添加、刪除、隱藏、顯示等時(shí)候定義布局容器自身的動(dòng)畫(huà)和View的動(dòng)畫(huà),也就是說(shuō)當(dāng)在一個(gè)LinerLayout中隱藏一個(gè)View的時(shí)候,我們可以自定義 整個(gè)由于LinerLayout隱藏View而改變的動(dòng)畫(huà),同時(shí)還可以自定義被隱藏的View自己消失時(shí)候的動(dòng)畫(huà)等。
我們可以發(fā)現(xiàn)LayoutTransition類中主要有五種容器轉(zhuǎn)換動(dòng)畫(huà)類型,具體如下:

  • LayoutTransition.APPEARING:當(dāng)View出現(xiàn)或者添加的時(shí)候View出現(xiàn)的動(dòng)畫(huà)。

  • LayoutTransition.CHANGE_APPEARING:當(dāng)添加View導(dǎo)致布局容器改變的時(shí)候整個(gè)布局容器的動(dòng)畫(huà)。

  • LayoutTransition.DISAPPEARING:當(dāng)View消失或者隱藏的時(shí)候View消失的動(dòng)畫(huà)。

  • LayoutTransition.CHANGE_DISAPPEARING:當(dāng)刪除或者隱藏View導(dǎo)致布局容器改變的時(shí)候整個(gè)布局容器的動(dòng)畫(huà)。

  • LayoutTransition.CHANGE:當(dāng)不是由于View出現(xiàn)或消失造成對(duì)其他View位置造成改變的時(shí)候整個(gè)布局容器的動(dòng)畫(huà)。

XML方式使用系統(tǒng)提供的默認(rèn)LayoutTransition動(dòng)畫(huà):
我們可以通過(guò)如下方式使用系統(tǒng)提供的默認(rèn)ViewGroup的LayoutTransition動(dòng)畫(huà):

android:animateLayoutChanges=”true”

在ViewGroup添加如上x(chóng)ml屬性默認(rèn)是沒(méi)有任何動(dòng)畫(huà)效果的,因?yàn)榍懊嬲f(shuō)了,該動(dòng)畫(huà)針對(duì)于ViewGroup內(nèi)部東東發(fā)生改變時(shí)才有效,所以當(dāng)我們?cè)O(shè)置如上屬性然后調(diào)運(yùn)ViewGroup的addView、removeView方法時(shí)就能看見(jiàn)系統(tǒng)默認(rèn)的動(dòng)畫(huà)效果了。
還有一種就是通過(guò)如下方式設(shè)置:

android:layoutAnimation=”@anim/customer_anim”

通過(guò)這種方式就能實(shí)現(xiàn)很多吊炸天的動(dòng)畫(huà)。
Java方式使用系統(tǒng)提供的默認(rèn)LayoutTransition動(dòng)畫(huà):
在使用LayoutTransition時(shí),你可以自定義這幾種事件類型的動(dòng)畫(huà),也可以使用默認(rèn)的動(dòng)畫(huà),總之最終都是通過(guò)setLayoutTransition(LayoutTransition lt)方法把這些動(dòng)畫(huà)以一個(gè)LayoutTransition對(duì)象設(shè)置給一個(gè)ViewGroup。
譬如實(shí)現(xiàn)如上Xml方式的默認(rèn)系統(tǒng)LayoutTransition動(dòng)畫(huà)如下:

mTransitioner = new LayoutTransition();
mViewGroup.setLayoutTransition(mTransitioner);

稍微再高端一點(diǎn)吧,我們來(lái)自定義這幾類事件的動(dòng)畫(huà),分別實(shí)現(xiàn)他們,那么你可以像下面這么處理:

mTransitioner = new LayoutTransition();
......
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "scaleX", 0, 1);
......//設(shè)置更多動(dòng)畫(huà)
mTransition.setAnimator(LayoutTransition.APPEARING, anim);
......//設(shè)置更多類型的動(dòng)畫(huà)                mViewGroup.setLayoutTransition(mTransitioner);

到此通過(guò)LayoutTransition你就能實(shí)現(xiàn)類似小米手機(jī)計(jì)算器切換普通型和科學(xué)型的炫酷動(dòng)畫(huà)了。
【工匠若水 http://blog.csdn.net/yanbober 轉(zhuǎn)載請(qǐng)注明出處。點(diǎn)我開(kāi)始Android技術(shù)交流
5 Android動(dòng)畫(huà)總結(jié)
到此Android動(dòng)畫(huà)基本已經(jīng)描述OK了,也就這么三大類,尤其是屬性動(dòng)畫(huà)更加一籌。但是特別說(shuō)一句,上面基本都沒(méi)有提及到各種動(dòng)畫(huà)的Listener接口,原因是這個(gè)玩意太簡(jiǎn)單,所以不提了,相信你會(huì)監(jiān)聽(tīng)View的onClickListener就一定會(huì)觸類旁通動(dòng)畫(huà)的Listener方法的。有了這些基礎(chǔ)相信無(wú)論是自定義控件時(shí)還是自定義動(dòng)畫(huà)時(shí)都會(huì)起到直接的指導(dǎo)參考作用。其實(shí)對(duì)于Android的動(dòng)畫(huà)實(shí)現(xiàn)遠(yuǎn)遠(yuǎn)不止現(xiàn)在提到的這些,但是這些又是基礎(chǔ),所以后面還會(huì)寫文章說(shuō)說(shuō)Android提供的其他動(dòng)畫(huà)參考工具類的。
現(xiàn)在我們繼續(xù)沿用官方的對(duì)比,翻譯一下這些動(dòng)畫(huà)的區(qū)別,具體如下(點(diǎn)我參看原文How Property Animation Differs from View Animation):
View動(dòng)畫(huà):
View動(dòng)畫(huà)只能夠?yàn)閂iew添加動(dòng)畫(huà),如果想為非View對(duì)象添加動(dòng)畫(huà)須自己實(shí)現(xiàn);且View動(dòng)畫(huà)支持的種類很少;尤其是他改變的是View的繪制效果,View的屬性沒(méi)有改變,其位置與大小都不變; View動(dòng)畫(huà)代碼量少,使用簡(jiǎn)單方便。
Property動(dòng)畫(huà):
彌補(bǔ)了View動(dòng)畫(huà)的缺陷,你可以為一個(gè)對(duì)象的任意屬性添加動(dòng)畫(huà),對(duì)象自己的屬性會(huì)被真的改變;當(dāng)對(duì)象的屬性變化的時(shí)候,屬性動(dòng)畫(huà)會(huì)自動(dòng)刷新屏幕;屬性動(dòng)畫(huà)改變的是對(duì)象的真實(shí)屬性,而且屬性動(dòng)畫(huà)不止用于View,還可以用于任何對(duì)象。

所有內(nèi)容都已呈現(xiàn),希望能夠?qū)Υ蠹覍W(xué)習(xí)起到幫助。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,285評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,548評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,878評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,098評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,361評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,613評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,339評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容