屬性動(dòng)畫(Property Animation)

內(nèi)容如下:

  • 屬性動(dòng)畫與補(bǔ)間動(dòng)畫的不同
  • 屬性動(dòng)畫工作原理
  • API 概述
  • 計(jì)算器 Evaluators
  • 插值器 Interpolators
  • 使用ValueAnimator
  • 使用ObjectAnimator
  • AnimatorSet
  • 動(dòng)畫監(jiān)聽
  • 視圖容器中布局的動(dòng)畫改變
  • 使用TypeEvaluator
  • 使用插值器 (Interpolators)
  • 關(guān)鍵幀
  • ViewPropertyAnimator
  • 聲明XML

屬性動(dòng)畫與補(bǔ)間動(dòng)畫的不同

補(bǔ)間動(dòng)畫只提供了運(yùn)動(dòng)可視對(duì)象的能力陨囊,如果你想操作非可視對(duì)象弦疮,就不得不自己用代碼實(shí)現(xiàn)。實(shí)際上蜘醋,補(bǔ)間動(dòng)畫具有局限性胁塞,它只能夠使一個(gè)視圖對(duì)象的幾個(gè)方面做運(yùn)動(dòng),比如:視圖的縮放压语、旋轉(zhuǎn)啸罢,而不能操作背景顏色等等。

補(bǔ)間動(dòng)畫另一個(gè)缺點(diǎn)是:它只能夠改變視圖繪制的位置胎食,并不能改變它的真實(shí)位置扰才。例如:你在屏幕中運(yùn)動(dòng)一個(gè)按鈕,按鈕將被正確的繪制斥季,但是當(dāng)你點(diǎn)擊當(dāng)前位置的按鈕時(shí)训桶,按鈕并不會(huì)有任何改變。所以你不得不通過實(shí)現(xiàn)自己的邏輯來操作它酣倾。

對(duì)于屬性動(dòng)畫而言舵揭,這些限制被全部移除。你能夠使任何對(duì)象(可視和非可視)的任何屬性運(yùn)動(dòng)躁锡,并且對(duì)象自身被修改午绳。屬性動(dòng)畫就是通過不停修改屬性值實(shí)現(xiàn)動(dòng)畫效果的。屬性動(dòng)畫在實(shí)現(xiàn)動(dòng)畫方面更加強(qiáng)大映之。你可以根據(jù)需求將動(dòng)畫分派給對(duì)象的屬性拦焚,比如:顏色蜡坊、位置、大惺臧堋秕衙;你也可以定義動(dòng)畫的差值器以及多動(dòng)畫的同步。

不管怎樣僵刮,補(bǔ)間動(dòng)畫的創(chuàng)建可以用更少的時(shí)間和更少的代碼据忘。如果補(bǔ)間動(dòng)畫能夠完成你需要的每件事,或者代碼中已存在的補(bǔ)間動(dòng)畫代碼能夠?qū)崿F(xiàn)你的需求搞糕。你就沒必要使用屬性動(dòng)畫勇吊。通過不同的使用場(chǎng)景來確定使用兩種動(dòng)畫系統(tǒng)的哪一種。


屬性動(dòng)畫工作原理

圖1描述了一個(gè)假想的對(duì)象窍仰,沿x軸運(yùn)動(dòng)汉规,表示它在屏幕上的水平位置。在40ms內(nèi)運(yùn)動(dòng)40px驹吮,每10毫秒(默認(rèn)幀刷新率)针史,對(duì)象水平移動(dòng)10像素。到40ms時(shí)钥屈,動(dòng)畫結(jié)束悟民。對(duì)象停在40px處。這是一個(gè)線性插值動(dòng)畫的例子篷就,表示對(duì)象勻速運(yùn)動(dòng)。

image.png

還可以指定具有非線性插值的動(dòng)畫近忙。圖2假設(shè)一個(gè)對(duì)象竭业,在動(dòng)畫開始時(shí)加速,然后減速動(dòng)畫結(jié)束時(shí)及舍。該對(duì)象仍然在40ms移動(dòng)40px未辆,但非線性。

image.png

屬性動(dòng)畫系統(tǒng)的重要組件如何計(jì)算上面提到的動(dòng)畫:


image.png

ValueAnimator 對(duì)象保持跟蹤動(dòng)畫的時(shí)間锯玛,比如動(dòng)畫的已運(yùn)動(dòng)時(shí)間和動(dòng)畫的當(dāng)前值咐柜。
TimeInterpolator 定義動(dòng)畫的插值器。
TypeEvaluator 定義如何計(jì)算動(dòng)畫屬性的值攘残。

開始一個(gè)動(dòng)畫:

  • 創(chuàng)建一個(gè)ValueAnimator并給它的開始屬性值拙友、結(jié)束屬性值以及動(dòng)畫的持續(xù)時(shí)間。
  • 調(diào)用start()開始動(dòng)畫歼郭。
  • ValueAnimator計(jì)算一個(gè)在0和1之間的逝去分?jǐn)?shù)遗契,基于動(dòng)畫的持續(xù)時(shí)間和經(jīng)過的多少時(shí)間。該分?jǐn)?shù)表示動(dòng)畫完成的時(shí)間百分比病曾,0表示0%牍蜂,1表示100%漾根。
  • 當(dāng)ValueAnimator完成計(jì)算一個(gè)逝去分?jǐn)?shù),它調(diào)用當(dāng)前設(shè)置的TimeInterpolator計(jì)算插值分?jǐn)?shù)鲫竞。
  • 當(dāng)插值分?jǐn)?shù)計(jì)算完成辐怕,ValueAnimator調(diào)用合適的TypeEvaluator,計(jì)算該屬性的值从绘,基于插值分?jǐn)?shù)秘蛇、起始值和動(dòng)畫結(jié)束值。

API 概述

  • ValueAnimator 屬性動(dòng)畫的計(jì)時(shí)引擎顶考,通過屬性值進(jìn)行動(dòng)畫處理赁还。包括計(jì)算動(dòng)畫值的核心功能,動(dòng)畫隨時(shí)間變化的細(xì)節(jié)驹沿,動(dòng)畫重復(fù)監(jiān)聽以及更新時(shí)間艘策,自定義evaluate。屬性動(dòng)畫包括兩部分:1.計(jì)算動(dòng)畫值渊季;2.對(duì)正在進(jìn)行動(dòng)畫的對(duì)象設(shè)置這些值朋蔫。ValueAnimator不能直接完成第二部分,所以必須在UpdateListener中更新屬性值却汉。

  • ObjectAnimator ValueAnimator的子類驯妄,可以直接設(shè)置目標(biāo)對(duì)象和屬性進(jìn)行動(dòng)畫。ObjectAnimator使用起來更方便合砂,也有限制青扔,對(duì)于進(jìn)行的動(dòng)畫的屬性必須要有setter,getter的訪問方法翩伪。

  • AnimatorSet 提供一種將多動(dòng)畫分組并使之關(guān)聯(lián)運(yùn)行的機(jī)制微猖。可以使動(dòng)畫同時(shí)執(zhí)行缘屹,順序執(zhí)行凛剥,延時(shí)執(zhí)行。


估算器 Evaluators

  • IntEvaluator 計(jì)算int型屬性
  • FloatEvaluator 計(jì)算float型屬性
  • ArgbEvaluator 計(jì)算16進(jìn)制的顏色值
  • TypeEvaluator 自定義計(jì)算器(更多見下文)

插值器 Interpolators

  • AccelerateDecelerateInterpolator 先加速后減速
  • AccelerateInterpolator 加速
  • AnticipateInterpolator 向相反運(yùn)動(dòng)一小段后再開始運(yùn)行
  • AnticipateOvershootInterpolator 前后都超出一小段
  • BounceInterpolator 結(jié)束時(shí)振動(dòng)結(jié)束
  • CycleInterpolator 往返運(yùn)行動(dòng)畫
  • DecelerateInterpolator 減速運(yùn)行
  • LinearInterpolator 勻速運(yùn)行
  • OvershootInterpolator 結(jié)束時(shí)超出一小段
  • TimeInterpolator 自定義

使用ValueAnimator

示例:使用ValueAnimator做曲線運(yùn)動(dòng)

通過調(diào)用ofInt()轻姿,ofFloat()犁珠,ofObject()方法獲得一個(gè)ValueAnimator,如下:

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

還可以通過以下操作指定一個(gè)自定義類型來進(jìn)行動(dòng)畫:

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, 
 endPropertyValue);
animation.setDuration(1000);
animation.start();

上述代碼,當(dāng)調(diào)用start()時(shí)互亮,ValueAnimator 將在1s內(nèi)計(jì)算0-1之間的動(dòng)畫值犁享。它只是在計(jì)算一個(gè)值并沒有影響到一個(gè)具體的對(duì)象。你需要實(shí)現(xiàn)ValueAnimator.AnimatorUpdateListener這個(gè)接口胳挎,通過調(diào)用getAnimatedValue()方法可以獲得特定幀的計(jì)算值饼疙,通過這個(gè)值可以對(duì)目標(biāo)對(duì)象進(jìn)行需要的變換。

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
    // You can use the animated value in a property that uses the
    // same type as the animation. In this case, you can use the
    // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

使用ObjectAnimator

示例:衛(wèi)星菜單

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

擁有setter,getter方法的屬性都可以進(jìn)行動(dòng)畫操作窑眯。
經(jīng)常操作的屬性:

"alpha" 透明度屏积;

"translationY" 沿Y軸平移;

"translationX" 沿X軸平移磅甩;

"scaleX" 橫向縮放炊林;

"scaleY" 縱向縮放;

"rotation" 平面旋轉(zhuǎn)卷要;

"rotationX" 關(guān)于X軸旋轉(zhuǎn)渣聚;

"rotationY" 關(guān)于Y軸旋轉(zhuǎn);


AnimatorSet

你可以使用AnimatorSet控制多個(gè)Animator的播放順序僧叉,同時(shí)播放奕枝,順序播放,誰在前誰在后等瓶堕“溃可以嵌套使用。例:

AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();

常用方法:
playSequentially (Animator...) 順序播放郎笆;
playTogether (Animator...) 一起播放谭梗;

Builder方式:
play(Animator a);
創(chuàng)建一個(gè)Builder對(duì)象;

例1:有四個(gè)動(dòng)畫anim1, amin2, anim3,anim4宛蚓。anim1和anim2同時(shí)播放;當(dāng)anim2播放結(jié)束時(shí)播放anim3;當(dāng)anim3播放結(jié)束時(shí)播放anim4激捏。

 AnimatorSet s = new AnimatorSet();
 s.play(anim1).with(anim2);
 s.play(anim2).before(anim3);
 s.play(anim4).after(anim3);

動(dòng)畫監(jiān)聽

Animator.AnimatorListener

  • onAnimationStart() 動(dòng)畫開始時(shí)
  • onAnimationEnd() 動(dòng)畫結(jié)束時(shí)
  • onAnimationRepeat() 動(dòng)畫重復(fù)時(shí)
  • onAnimationCancel() 動(dòng)畫取消時(shí),同時(shí)調(diào)用onAnimationEnd()

ValueAnimator.AnimatorUpdateListener

  • onAnimationUpdate() 每幀動(dòng)畫都將被調(diào)用
    通過getAnimatedValue()方法獲取動(dòng)畫值凄吏。

AnimatorListenerAdapter

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
    public void onAnimationEnd(Animator animation) {
        balls.remove(((ObjectAnimator)animation).getTarget());
    }
)};

視圖容器中布局的動(dòng)畫改變

使用TypeEvaluator

如果你想以系統(tǒng)未知的方式運(yùn)動(dòng)远舅,你可以通過實(shí)現(xiàn)TypeEvaluator接口來創(chuàng)建。實(shí)現(xiàn)TypeEvaluator接口只需要實(shí)現(xiàn)一個(gè)方法evaluate()竞思。如下:

public class FloatEvaluator implements TypeEvaluator {
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

例:使TextView沿正弦曲線運(yùn)動(dòng)

創(chuàng)建包裝類:

   private class TextHolder
        private TextView view;
        public TextHolder(TextView textView){
            view=textView;
        }
        public XYHolder getXY() {
            return new XYHolder(view.getX(),view.getY());
        }

        public void setXY(XYHolder xyHolder) {
            view.setX(xyHolder.getX());
            view.setY(xyHolder.getY());
        }
    }

估值器:

public class SineTypeEvaluator implements TypeEvaluator<XYHolder> {
    @Override
    public XYHolder evaluate(float fraction, XYHolder startValue, XYHolder endValue) {
        float xHolder=startValue.getX()+fraction*(endValue.getX()-startValue.getX());
        float yHolder=(float) (300*Math.sin(xHolder/100)+endValue.getY());
        return new XYHolder(xHolder,yHolder );
    }
}

開始動(dòng)畫:

ObjectAnimator objectAnimator=ObjectAnimator.ofObject(new TextHolder(mText1),"xY",new 
SineTypeEvaluator(),new XYHolder(0f,400f),new XYHolder(628f,400f));
        objectAnimator.setDuration(2000);
        objectAnimator.start();

使用插值器

一個(gè)隨時(shí)間計(jì)算動(dòng)畫值的函數(shù)

AccelerateDecelerateInterpolator

public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

public float getInterpolation(float input) {
    return input;
}

關(guān)鍵幀

例如:

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation)
rotationAnim.setDuration(5000ms);

ViewPropertyAnimator

ViewPropertyAnimator提供一種更簡單的多屬性運(yùn)動(dòng)的方式表谊。如下對(duì)比:

使用多個(gè)ObjectAnimator:

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

使用一個(gè)ObjectAnimator:

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();

使用ViewPropertyAnimator:

myView.animate().x(50f).y(100f);</pre>

聲明XML

將xml文件保存在res/animator/ 目錄下

動(dòng)畫類與xml標(biāo)簽的對(duì)應(yīng)關(guān)系:
ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>
AnimatorSet- <set>

下面的示例依次播放兩組對(duì)象動(dòng)畫,第一個(gè)動(dòng)畫集合同時(shí)播放兩個(gè)對(duì)象動(dòng)畫:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

運(yùn)行上述動(dòng)畫:

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

在XML中聲明ValueAnimator:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

ValueAnimator必須實(shí)現(xiàn)AnimatorUpdateListener盖喷,如下:

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
    R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});
xmlAnimator.start();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市难咕,隨后出現(xiàn)的幾起案子课梳,更是在濱河造成了極大的恐慌,老刑警劉巖余佃,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件暮刃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡爆土,警方通過查閱死者的電腦和手機(jī)椭懊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來步势,“玉大人氧猬,你說我怎么就攤上這事背犯。” “怎么了盅抚?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵漠魏,是天一觀的道長。 經(jīng)常有香客問我妄均,道長柱锹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任丰包,我火速辦了婚禮禁熏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘邑彪。我一直安慰自己瞧毙,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布锌蓄。 她就那樣靜靜地躺著升筏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪瘸爽。 梳的紋絲不亂的頭發(fā)上您访,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音剪决,去河邊找鬼灵汪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛柑潦,可吹牛的內(nèi)容都是我干的享言。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼渗鬼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼览露!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起譬胎,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤差牛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后堰乔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偏化,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年镐侯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了侦讨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖韵卤,靈堂內(nèi)的尸體忽然破棺而出骗污,到底是詐尸還是另有隱情,我是刑警寧澤怜俐,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布身堡,位于F島的核電站,受9級(jí)特大地震影響拍鲤,放射性物質(zhì)發(fā)生泄漏贴谎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一季稳、第九天 我趴在偏房一處隱蔽的房頂上張望擅这。 院中可真熱鬧,春花似錦景鼠、人聲如沸仲翎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽溯香。三九已至,卻和暖如春浓恶,著一層夾襖步出監(jiān)牢的瞬間玫坛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國打工包晰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留湿镀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓伐憾,卻偏偏與公主長得像勉痴,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子树肃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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