Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義動畫(仿Win10加載動畫)

自定義動畫(仿Win10加載動畫)

編輯:關於Android編程

一、源代碼

源代碼及demo

二、背景

先看看Win10的加載動畫(找了很久才找到):
這裡寫圖片描述

每次打開電腦都會有這個加載動畫,看上挺cool的,就想著自己能否實現它。

要實現這個動畫?

首先想能否通過自定義SurfaceView控件(界面刷新是通過子線程來完成)來實現。這需要知道某一刻時間,那些小圓點在什麼位置。小圓點都在做圓周運動,可以看出除了左上角,可以通過勢能和動能的相互轉化來計算速度。但速度是變化的,如何計算某一個時刻的位置?網上一查,暈,都是微積分,算了吧。

後來,還是使用動畫吧,最後的效果:
這裡寫圖片描述
動起來:
這裡寫圖片描述

要玩動畫,自然就得理解動畫的核心部分。如果理解了,就坐電梯直達“動畫分析”。

三、動畫的兩個核心

一般復雜動畫需要自定義TimeInterpolator和TypeEvaluator,前者控制運動的速度,後者控制運動的軌跡。

TypeEvaluator不僅控制運動軌跡,只要有權限且能體現效果的setter屬性,都可以控制,如旋轉、縮放、顏色等;TimeInterpolator能干的事情,它也能干,只是為了方便,把它抽出來了。

這是他倆的關系(詳見參考5):
這裡寫圖片描述

3.1 TimeInterpolator

TimeInterpolator,名為時間插值器(或時間校正器),用於校正動畫播放的時間。默認是加速減速插值器AccelerateDecelerateInterpolator。

正常情況下,動畫在執行時間duration內,從起點到終點,中間是勻速運動,每一時刻都對應著固定的位置。為了統一,把時間[0, duration]轉換成時間百分比[0, 1],如duration=100ms時,在50ms,應該對應著時間比0.5,且位置在正中間。如下圖紅色線:
這裡寫圖片描述

說明:

橫軸是實際運行的時間百分比軸(X軸),縱軸是校正後的時間百分比軸(Y軸); 圖中紫色線是經過校正後的時間百分比,在x=0.5時,正常情況是y=0.35,但校正後y不到0.3,也就是說這個時刻所在的位置還不到總路線的1/3。說白了,導數就是速度,紫色線的導數在逐漸變大,表示速度也在逐漸變快,這就是一個加速過程。 X軸是實際運行的時間軸,與Y軸,只有一對一、或一對多的關系,不能出現多對一的情況。一對多的關系就是來回運動的具體體現。

再看TimeInterpolator,是個接口,只有一個方法getInterpolation(float input),方法中的參數對應著X軸,返回值對應著Y軸。

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

3.2 TypeEvaluator

TypeEvaluator就是一個估值器。拿代碼說來,明白一點。

public interface TypeEvaluator {
    public T evaluate(float fraction, T startValue, T endValue);
}

也是一個接口,有一個方法evaluate(float fraction, T startValue, T endValue),參數說明:

fraction:時間插值器的校正值 startValue:開始值 endValue:結束值 T:可以是float類的單值,也可以是坐標、顏色等

一般與AnimatorUpdateListener的onAnimationUpdate()方法結合。

3.3 貝塞爾曲線

以下用到了很多二階貝塞爾曲線,具體計算公式如下(詳見參考2):
這裡寫圖片描述
B(t)=(1?t)2P0+2t(1?t)P1+t2P2,t∈[0,1]

原理:由 P0 至 P1 的連續點 Q0,描述一條線段。
由 P1 至 P2 的連續點 Q1,描述一條線段。
由 Q0 至 Q1 的連續點 B(t),描述一條二次貝塞爾曲線。

經驗:P1-P0為曲線在P0處的切線

另外加兩條經驗:

為了更自然,P1-P2一般情況下也是曲線在P2處的切線,這樣就能算出P1的具體位置 曲線上每個點的坐標x和y,x和y分別套用此公式

3.4 效果比較

無圖無真相。。。圖來了
這裡寫圖片描述
在起點、終點、運行時間都一樣的情況下,三種效果比較:

普通動畫——默認插值器是加速減速插值器AccelerateDecelerateInterpolator 自定義插值器動畫——速度效果是先勻速,再做貝塞爾曲線運動(先反向減速後,再正向加速)。如下圖紅線(其他畫圖軟件都沒不好辦,這時還是PS的鋼筆工具好用)
這裡寫圖片描述 自定義估值器動畫——插值器是默認的(水平方向與普通動畫是一致的),做正弦曲線運動

三種動畫的代碼:

private static final long ANIM_DURATION = 5000;
/**
 * 普通動畫
 * 差值器默認為AccelerateDecelerateInterpolator
 */
private void normalAnim() {
    ObjectAnimator oa = ObjectAnimator.ofFloat(v_normal, "translationX", 0, 300);
    oa.setDuration(ANIM_DURATION);
    oa.setRepeatCount(ValueAnimator.INFINITE);
    oa.start();
}
/**
 * 自定義插值器的動畫
 */
private void interpolatorAnim() {
ObjectAnimator oa = ObjectAnimator.ofFloat(v_interpolator, "translationX", 0, 300);
oa.setInterpolator(new TimeInterpolator() {
    /**
     * 獲取插值器的值
     * @param input 原生時間比值[0, 1]
     * @return 校正後的值
     */
    @Override
    public float getInterpolation(float input) {
        // 前半段時間為直線(勻速運動),後半段貝塞爾曲線(先反向)
        if (input < 0.5) {
            return input;
        }
        // 把貝塞爾曲線范圍[0.5, 1]轉換成[0, 1]范圍
        input = (input - 0.5f) * (1 - 0) / (1 - 0.5f);
        float tmp = 1 - input;
        return tmp * tmp * 0.5f + 2 * input * tmp * 0 + input * input * 1;
    }
});
oa.setDuration(ANIM_DURATION);
oa.setRepeatCount(ValueAnimator.INFINITE);
oa.start();
}
/**
 * 自定義估值器的動畫
 */
private void evaluatorAnim() {
    ValueAnimator va = ValueAnimator.ofObject(
            new TypeEvaluator() {
                /**
                 * 估算結果
                 *
                 * @param fraction 由插值器提供的值,∈[0, 1]
                 * @param startValue 開始值
                 * @param endValue 結束值
                 * @return
                 */
                @Override
                public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
                    PointF p = new PointF();
                    float distance = endValue.x - startValue.x;
                    p.x = fraction * distance;
                    float halfDistance = distance / 2;
                    float sinX = (float) Math.sin(fraction * Math.PI / 0.5);
                    p.y = -halfDistance * sinX;
                    return p;
                }
            },
            new PointF(0, 0),
            new PointF(300, 0)
    );
    va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            PointF pointF = (PointF) animation.getAnimatedValue();
            v_evaluator.setTranslationX(pointF.x);
            v_evaluator.setTranslationY(pointF.y);
        }
    });
    va.setDuration(ANIM_DURATION);
    va.setRepeatCount(ValueAnimator.INFINITE);
    va.start();
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved