屬性動畫(Property Animation)

內(nèi)容如下:

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

屬性動畫與補間動畫的不同

補間動畫只提供了運動可視對象的能力,如果你想操作非可視對象,就不得不自己用代碼實現(xiàn)。實際上,補間動畫具有局限性,它只能夠使一個視圖對象的幾個方面做運動,比如:視圖的縮放、旋轉(zhuǎn),而不能操作背景顏色等等。

補間動畫另一個缺點是:它只能夠改變視圖繪制的位置,并不能改變它的真實位置。例如:你在屏幕中運動一個按鈕,按鈕將被正確的繪制,但是當你點擊當前位置的按鈕時,按鈕并不會有任何改變。所以你不得不通過實現(xiàn)自己的邏輯來操作它。

對于屬性動畫而言,這些限制被全部移除。你能夠使任何對象(可視和非可視)的任何屬性運動,并且對象自身被修改。屬性動畫就是通過不停修改屬性值實現(xiàn)動畫效果的。屬性動畫在實現(xiàn)動畫方面更加強大。你可以根據(jù)需求將動畫分派給對象的屬性,比如:顏色、位置、大??;你也可以定義動畫的差值器以及多動畫的同步。

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


屬性動畫工作原理

圖1描述了一個假想的對象,沿x軸運動,表示它在屏幕上的水平位置。在40ms內(nèi)運動40px,每10毫秒(默認幀刷新率),對象水平移動10像素。到40ms時,動畫結(jié)束。對象停在40px處。這是一個線性插值動畫的例子,表示對象勻速運動。

image.png

還可以指定具有非線性插值的動畫。圖2假設(shè)一個對象,在動畫開始時加速,然后減速動畫結(jié)束時。該對象仍然在40ms移動40px,但非線性。

image.png

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


image.png

ValueAnimator 對象保持跟蹤動畫的時間,比如動畫的已運動時間和動畫的當前值。
TimeInterpolator 定義動畫的插值器。
TypeEvaluator 定義如何計算動畫屬性的值。

開始一個動畫:

  • 創(chuàng)建一個ValueAnimator并給它的開始屬性值、結(jié)束屬性值以及動畫的持續(xù)時間。
  • 調(diào)用start()開始動畫。
  • ValueAnimator計算一個在0和1之間的逝去分數(shù),基于動畫的持續(xù)時間和經(jīng)過的多少時間。該分數(shù)表示動畫完成的時間百分比,0表示0%,1表示100%。
  • 當ValueAnimator完成計算一個逝去分數(shù),它調(diào)用當前設(shè)置的TimeInterpolator計算插值分數(shù)。
  • 當插值分數(shù)計算完成,ValueAnimator調(diào)用合適的TypeEvaluator,計算該屬性的值,基于插值分數(shù)、起始值和動畫結(jié)束值。

API 概述

  • ValueAnimator 屬性動畫的計時引擎,通過屬性值進行動畫處理。包括計算動畫值的核心功能,動畫隨時間變化的細節(jié),動畫重復(fù)監(jiān)聽以及更新時間,自定義evaluate。屬性動畫包括兩部分:1.計算動畫值;2.對正在進行動畫的對象設(shè)置這些值。ValueAnimator不能直接完成第二部分,所以必須在UpdateListener中更新屬性值。

  • ObjectAnimator ValueAnimator的子類,可以直接設(shè)置目標對象和屬性進行動畫。ObjectAnimator使用起來更方便,也有限制,對于進行的動畫的屬性必須要有setter,getter的訪問方法。

  • AnimatorSet 提供一種將多動畫分組并使之關(guān)聯(lián)運行的機制。可以使動畫同時執(zhí)行,順序執(zhí)行,延時執(zhí)行。


估算器 Evaluators

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

插值器 Interpolators

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

使用ValueAnimator

示例:使用ValueAnimator做曲線運動

通過調(diào)用ofInt(),ofFloat(),ofObject()方法獲得一個ValueAnimator,如下:

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

還可以通過以下操作指定一個自定義類型來進行動畫:

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

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

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ī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控制多個Animator的播放順序,同時播放,順序播放,誰在前誰在后等。可以嵌套使用。例:

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)建一個Builder對象;

例1:有四個動畫anim1, amin2, anim3,anim4。anim1和anim2同時播放;當anim2播放結(jié)束時播放anim3;當anim3播放結(jié)束時播放anim4。

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

動畫監(jiān)聽

Animator.AnimatorListener

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

ValueAnimator.AnimatorUpdateListener

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

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());
    }
)};

視圖容器中布局的動畫改變

使用TypeEvaluator

如果你想以系統(tǒng)未知的方式運動,你可以通過實現(xiàn)TypeEvaluator接口來創(chuàng)建。實現(xiàn)TypeEvaluator接口只需要實現(xiàn)一個方法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沿正弦曲線運動

創(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 );
    }
}

開始動畫:

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

使用插值器

一個隨時間計算動畫值的函數(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提供一種更簡單的多屬性運動的方式。如下對比:

使用多個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();

使用一個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/ 目錄下

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

下面的示例依次播放兩組對象動畫,第一個動畫集合同時播放兩個對象動畫:

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

運行上述動畫:

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必須實現(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)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容