Android屬性動畫(二) ValueAnimator的實際應用 & 自定義TypeEvaluator

在上一篇文章《Android屬性動畫(一) 初識基本用法》中,我們學習了屬性動畫的基本用法,但是在一些場景中,這些用法還遠不能滿足我們的需求,今天就讓我們來學習一下屬性動畫的高級用法吧!

1.ValueAnimator的實際應用

在上篇文章中我們學習到,ValueAnimator.ofInt()方法可以幫我們計算初始值到結束值之間的動畫過渡值,但是這些值如何應用到實際的View中呢,舉個簡單的例子來實踐一下,先上張圖:

監控業務雷達圖

可以看到圖中有一個六邊形的雷達圖,六個角分別代表六種監控業務,每個角有一個白色的圓點,代表此項監控業務正常,現在有這樣一個需求,當其中一項監控業務報警的時候,白色的圓點變成閃爍的黃點,同時背景變成紅色。背景變色這個很容易,直接給佈局設置一下背景顏色就可以了,我們來實現一下閃爍的黃點這個效果。

// 是否正在播放動畫
private boolean isAnimationPlaying = false;

/**
 * 繪製點
 *
 * @param canvas 畫布
 */
private void drawPoints(Canvas canvas) {
    for (int i = 0; i < dataCount; i++) {
        if (deviceStatus[i] == ONLINE) {
            pointPaint.setColor(Color.WHITE);
            pointPaint.setAlpha(255);
            canvas.drawCircle(getPoint(i).x, getPoint(i).y, dp2px(3), pointPaint);

        } else if (deviceStatus[i] == ALARM) {
            pointPaint.setColor(Color.WHITE);
            pointPaint.setAlpha(255);
            canvas.drawCircle(getPoint(i).x, getPoint(i).y, dp2px(3), pointPaint);

            if (!isAnimationPlaying) {
                isAnimationPlaying = true;
                ValueAnimator animator = ValueAnimator.ofInt(0, 100);
                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int currentValue = (int) animation.getAnimatedValue();
                        // 給報警點設置0至100的透明度
                        alarmPointPaint.setAlpha(currentValue);
                        invalidate();
                    }
                });
                // 單次動畫時長1.5s
                animator.setDuration(1500);
                // 無限循環播放動畫
                animator.setRepeatCount(ValueAnimator.INFINITE);
                // 循環時倒序播放
                animator.setRepeatMode(ValueAnimator.REVERSE);
                animator.start();
            } else {
                canvas.drawCircle(getPoint(i).x, getPoint(i).y, dp2px(8), alarmPointPaint);
            }
        }
    }
}

當監控狀態爲ONLINE的時候繪製白色圓點,報警的時候先判斷一下當前是否正在播放動畫,如果沒有播放,初始化ValueAnimator並設置一個監聽器,通過給畫筆設置0到100的透明度的方式來實現閃爍效果,動畫播放模式設置成了無限循環,如果消除報警,不要忘了調用ValueAnimator的cancel方法來結束動畫,完整的代碼已上傳到GitHub上,如果感興趣可以下載下來看看,OK,運行一下程序看下效果:

報警

2.自定義TypeEvaluator

先了解下TypeEvaluator是什麼,TypeEvaluator可以翻譯成估值器,用於計算動畫從開始到結束的過渡值,前面說過ValueAnimator.ofInt()方法可以幫助我們實現初始值到結束值之間的平滑過度,其實它的內部就是通過IntEvaluator來完成計算的,而IntEvaluator就是繼承於TypeEvaluator,看下IntEvaluator類:

public class IntEvaluator implements TypeEvaluator<Integer> {

    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

可以看到IntEvaluator實現了TypeEvaluator接口,重寫了evaluate方法,傳入了三個參數,第一個參數非常重要,代表當前動畫的完成度,第二個和第三個參數分別代表動畫的開始值和結束值,結束值減去開始值然後乘以動畫完成度,再加上開始值就可以得到當前動畫的值了。

我們已經使用過了ValueAnimator的ofFloat和ofInt方法,其中已經內置了FloatEvaluator和IntEvaluator來用於過渡值的計算,ValueAnimator還有一個ofObject方法,可以對任意對象進行動畫操作,這就需要我們自己來定義一個估值器,告訴系統如何進行動畫過渡。

現在有這樣一個需求,監控業務報警之後背景顏色從藍色到紅色的變化有些生硬,我們需要一個2s的顏色過渡動畫,由於是直接操作View,這裏我們使用 ObjectAnimator.ofObject()來實現這樣一效果。

在上篇文章中我們學習到,ObjectAnimator內部的工作原理就是通過在View中查找屬性名對應的get、set方法來設置對應的屬性值的,因此我們需要自定義一個ColorView控件,定義一個backgroundColor屬性,並提供它的get和set方法。

public class ColorView extends RelativeLayout {

    private String backgroundColor;

    public ColorView(Context context) {
        this(context, null);
    }

    public ColorView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ColorView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public String getBackgroundColor() {
        return backgroundColor;
    }

    public void setBackgroundColor(String backgroundColor) {
        this.backgroundColor = backgroundColor;
        this.setBackgroundColor(Color.parseColor(backgroundColor));
    }
}

定義一個ColorEvaluator類來計算顏色的過渡值:

public class ColorEvaluator implements TypeEvaluator {

    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        String startColor = (String) startValue;
        String endColor = (String) endValue;

        int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
        int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
        int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);

        int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
        int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
        int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);

        int currentRed = (int) ((endRed - startRed) * fraction + startRed);
        int currentGreen = (int) ((endGreen - startGreen) * fraction + startGreen);
        int currentBlue = (int) ((endBlue - startBlue) * fraction + startBlue);

        return "#" + getHexString(currentRed) + getHexString(currentGreen) + getHexString(currentBlue);
    }

    private String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }
}

在佈局文件中使用ColorView:

<?xml version="1.0" encoding="utf-8"?>
<com.yl.propertyanimationdemo.widget.ColorView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_device_status"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#2B93EC">

    <com.yl.propertyanimationdemo.widget.RadarView
        android:id="@+id/rv_device_status"
        android:layout_width="250dp"
        android:layout_height="230dp"
        android:layout_centerInParent="true" />

    <Button
        android:id="@+id/btn_alarm"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp"
        android:background="#00000000"
        android:text="報警"
        android:textColor="#FFF"
        android:textSize="20sp" />

</com.yl.propertyanimationdemo.widget.ColorView>

設置顏色過渡動畫:

// 顏色過渡動畫
ObjectAnimator animator = ObjectAnimator.ofObject(rlDeviceStatus, "backgroundColor",
        new ColorEvaluator(), "#2B93EC", "#ED6E74");
animator.setDuration(2000);
animator.start();

看下效果:

顏色過渡

3.寫在最後

源碼已託管到GitHub上,歡迎Fork,覺得還不錯就Start一下吧!

GitHub地址:https://github.com/alidili/PropertyAnimationDemo

歡迎同學們吐槽評論,如果你覺得本篇博客對你有用,那麼就留個言或者頂一下吧(^-^)

《Android屬性動畫(三) TimeInterpolator(插值器)》

發佈了64 篇原創文章 · 獲贊 275 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章