Android動畫效果之Property Animation進階(屬性動畫)

總李寫程式碼發表於2016-08-24

前言:

     前面初步認識了Android的Property Animation(屬性動畫)Android動畫效果之初識Property Animation(屬性動畫)(三),並且利用屬性動畫簡單了補間動畫能夠實現的動畫效果,今天重點學習下Property Animation基本原理及高階使用。本章先通過餘額寶的數字動畫小例子來學習屬性動畫基本原理。具體效果如下:

     其他幾種動畫效果:

ValueAnimator(差值動畫)

    上篇文章一直使用的ObjectAnimator來實現屬性動畫,單純從字面上理解的話ObjectAnimator作用於某個實際的物件,而ValueAnimator是ObjectAnimator的父類,它繼承自抽象類Animator,它作用於一個值,將其由一個值變化為另外一個值,然後根據值的變化,按照一定的規則,動態修改View的屬性,比如View的位置、透明度、旋轉角度、大小等,即可完成了動畫的效果。直接看下上面的數字動畫是怎麼實現的?

ValueAnimator valueAnimator =ValueAnimator.ofFloat( 0f, 126512.36f);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
    float money= (float) animation.getAnimatedValue();
    mTextView.setText(String.format("%.2f", money));
    }
});
valueAnimator.start();

這裡通過ofFloat()方法構造一個ValueAnimator例項,除此之外還提供了其他函式ofInt()、ofObject()、ofPropertyValuesHolder()函式,api 21之後又提供了ofArgb(),每個函式都是可以傳入多個改變值。

Interpolator(插值器)

   Interpolator插值器用於控制動畫的變化速率,也可以簡單的理解成用於控制動畫的快慢,插值器目前都只是對動畫執行過程的時間進行修飾,並沒有對軌跡進行修飾。系統提供的插值器有以下幾種:

插值器名字 解說 對應的xml
AccelerateInterpolator  加速,開始時慢中間加速  @android:anim/accelerate_interpolator
DecelerateInterpolator 減速,開始時快然後減速 @android:anim/decelerate_interpolator
AccelerateDecelerateInterolator   先加速後減速,開始結束時慢,中間加速  @android:anim/accelerate_decelerate_interpolator
AnticipateInterpolator 反向 ,先向相反方向改變一段再加速播放  @android:anim/anticipate_interpolator
AnticipateOvershootInterpolator 反向加超越,先向相反方向改變,再加速播放,會超出目的值然後緩慢移動至目的值 @android:anim/anticipate_overshoot_interpolator
BounceInterpolator  跳躍,快到目的值時值會跳躍,如目的值100,後面的值可能依次為85,77,70,80,90,100 @android:anim/bounce_interpolator
CycleIinterpolator  迴圈,動畫迴圈一定次數,值的改變為一正弦函式:Math.sin(2* mCycles* Math.PI* input) @android:anim/cycle_interpolator
LinearInterpolator  線性,線性均勻改變 @android:anim/linear_interpolator
OvershootInterpolator 超越,最後超出目的值然後緩慢改變到目的值 @android:anim/overshoot_interpolator

通過上面的名字大家是不是很眼熟,是的和補間動畫的插值器是一致的。Android的動畫插值器採用策略設計模式,都是實現了Interpolator這個介面,而Interpolator又是繼承自一個叫做TimeInterpolator的介面(從3.0開始,增加了TimeInterpolator這個介面,並把原先的Interpolator介面的抽象方法移到了其中,3.0後的Interpolator介面也就什麼也沒做,只是對父類改了個名字,達到向下相容)。

package android.animation;

public interface TimeInterpolator {

    float getInterpolation(float input);
}

在Interpolator的實現類裡面,都實現了一個float getInterpolator(float input)的方法,傳入引數是正常執行動畫的時間點,返回值是使用者真正想要它執行的時間點。上面的數字動畫使用了勻速插值器LinearInterpolator其程式碼實現如下:

public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {

    public LinearInterpolator() {
    }

    public LinearInterpolator(Context context, AttributeSet attrs) {
    }

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

    /** @hide */
    @Override
    public long createNativeInterpolator() {
        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
    }
}

可以看出float getInterpolator(float input)返回的值就是當前要執行的時間點實現勻速執行動畫。

如何自定義一個插值器?舉例:我們實現一個先減速後加速插值器,程式碼如下

public class DecelerateAccelerateInterpolator implements Interpolator {
    @Override
    public float getInterpolation(float input) {
        float result;
        if (input <= 0.5f) {
            result = (float) (Math.sin(Math.PI * input)) / 2.0f;
        } else {
            result = (float) (2 - Math.sin(Math.PI * input)) / 2.0f;
        }
        return result;
    }
}

TypeEvaluator(估值器)

  TypeEvaluator用於根據當前屬性改變的百分比來計算改變後的屬性值,系統提供瞭如下幾種估值器

  • IntEvaluator 針對整型屬性

  • IntArrayEvaluator 針對整型屬性集合

  • FloatEvaluator 針對浮點型屬性

  • FloatArrayEvaluator 針對浮點型屬性集合

  • ArgbEvaluator  針對Color屬性

  • RectEvaluator 針對Rect屬性

  • PointFEvaluator 針對PointF屬性

  TypeEvaluator設計也是採用策略設計模式,都實現TypeEvaluator介面,原始碼如下:

package android.animation;

public interface TypeEvaluator<T> {

    public T evaluate(float fraction, T startValue, T endValue);

}

介面提供了evaluate(float fraction, T startValue, T endValue);動畫在執行過程中Interpolator自動計算出動畫執行的百分比fraction,然後TypeEvaluator根據fraction計算出當前動畫的屬性值。以FloatEvaluator 程式碼為例:

public class FloatEvaluator implements TypeEvaluator<Number> {


    public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

 如何自定義TypeEvaLuator?比如我們要實現一個錢的增加,字型顏色越紅的動畫,我們現在都知道可以使用ofArgb(),但是ofArgb()需要api 21以上才能使用,所以需要我們自定義一個ArgbEvaLuator,這裡為了演示自定義TypeEvaluator直接把api 21中提供的ArgbEvaluator原始碼拿來使用,如下:

public class TextArgbEvaluator implements TypeEvaluator {
    
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        int startA = (startInt >> 24) & 0xff;
        int startR = (startInt >> 16) & 0xff;
        int startG = (startInt >> 8) & 0xff;
        int startB = startInt & 0xff;

        int endInt = (Integer) endValue;
        int endA = (endInt >> 24) & 0xff;
        int endR = (endInt >> 16) & 0xff;
        int endG = (endInt >> 8) & 0xff;
        int endB = endInt & 0xff;

        return (int) ((startA + (int) (fraction * (endA - startA))) << 24) |
                (int) ((startR + (int) (fraction * (endR - startR))) << 16) |
                (int) ((startG + (int) (fraction * (endG - startG))) << 8) |
                (int) ((startB + (int) (fraction * (endB - startB))));
    }
}

呼叫方式:

        AnimatorSet animatorSet = new AnimatorSet();
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 126512.36f);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float money = (float) animation.getAnimatedValue();
                Log.e("Interpolator", "money---->" + money);
                mTextView.setText(String.format("%.2f", money));
            }
        });

        int startColor = Color.parseColor("#FCA3AB");
        int endColor = Color.parseColor("#FB0435");
        ValueAnimator colorAnimator = ValueAnimator.ofObject(new TextArgbEvaluator(),startColor, endColor);
        colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int color = (int) animation.getAnimatedValue();
                Log.e("Interpolator", "color---->" + color);
                mTextView.setTextColor(color);
            }
        });

        animatorSet.playTogether(valueAnimator,colorAnimator);
        animatorSet.setDuration(5000);
        animatorSet.setInterpolator(new LinearInterpolator());
        animatorSet.start();

執行效果:

ObjectAnimator

    上篇我們簡單了學習了ObjectAnimator動畫,並且通過ObjectAnimator實現了幾種簡單的動畫效果,ObjectAnimator繼承自ValueAnimator,所以主體方法還是ValueAnimator裡實現的。先來回顧一下上篇的一個旋轉動畫例子。

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f);
    objectAnimator.setDuration(500);
    objectAnimator.setRepeatCount(1);
    objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
    objectAnimator.start();

這個例子很簡單,針對view的屬性rotation進行持續時間為500ms的0到360的角度變換。屬性名字rotation在View中有對應setRotation(),否則沒有任何效果,而且引數型別必須為float型,否則沒有任何效果。view常見可操作的引數有:x/y;scaleX/scaleY;rotationX/ rotationY;transitionX/ transitionY等等。現在問題來了,我們本篇例子是為TextView 賦值一個float型的值,我們檢視TextView的函式並不找不到setText(float f),這時該怎麼處理呢?如何為不具有get/set方法的屬性提供修改方法呢?莫著急!谷歌為此提供了兩種方法,第一種就是使用ValueAnimator來實現,就是上面所說的方式,另外一種方式通過自己寫一個包裝類,來為該屬性提供get/set方法。

public class MoneyTextView extends TextView {
    public MoneyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setText(float money) {
        setText(String.format("%.2f", money));
    }
}

然後就可以通過ObjectAnimator實現上面的數字動畫效果了。

 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mTextView, "text", 0f, 126512.36f);
    objectAnimator.setDuration(2000);
    objectAnimator.setInterpolator(new LinearInterpolator());
    objectAnimator.start();

其他有關ObjectAnimator的使用方式請參考上篇文章Android動畫效果之初識Property Animation(屬性動畫)(三)

總結:

   本篇主要簡單學習了屬性動畫的基本原理,屬性動畫使用了比較常見的策略設計模式,感興趣的話可以看下這篇文章Java設計模式之策略模式(Strategy),下篇文章將藉助自定義ViewGroup學習一下佈局動畫。

 

相關文章