仿網易郵箱大師進度框

仿網易郵箱大師進度框

最近用上了網易郵箱大師,發現整個APP的風格非常簡潔美觀。特別喜歡它的進度框,就嘗試用屬性動畫做了一個,算是對屬性動畫學習的實踐。

先上效果圖:

這裏寫圖片描述

圖像構成

這個圖像由三段弧線構成。用到的方法是canvas.drawArc()需要注意的是paint的設置。

    // 抗鋸齒
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 默認顏色
    paint.setColor(Color.parseColor("#B33425"));
    // 設置爲只畫線不填充
    paint.setStyle(Paint.Style.STROKE);
    // 設置默認的線寬
    paint.setStrokeWidth(strokWidth);
    // 將線的兩頭設置爲半圓
    paint.setStrokeCap(Paint.Cap.ROUND);

繪製這三段弧線

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (animatorSet == null) {
        // 如果動畫不存在,就啓動動畫
        startAnimation();
    }
    // 設置線寬
    paint.setStrokeWidth(strokWidth);

    // 畫紅色弧線
    paint.setColor(Color.parseColor("#B33425"));
    canvas.drawArc(getRectF(), 0f + angle, 80f, false, paint);

    // 隔開120度,再畫灰線
    paint.setColor(Color.parseColor("#5D5C5A"));
    canvas.drawArc(getRectF(), 0f + angle + 120, 80f, false, paint);

    // 隔開240度, 再畫藍線
    paint.setColor(Color.parseColor("#2972A6"));
    canvas.drawArc(getRectF(), 0f + angle + 240, 80f, false, paint);
}

動畫構成

仔細觀察,可以看出整個動畫由三個小動畫組成:
1. 旋轉,通過控制弧線起始角度
2. 進度條的粗細變化,通過控制paint的strokeWidth
3. 繪畫空間的縮放,通過控制drawArc的RectF參數

定義三個域來保存數據,並通過動畫操縱它們:

// 繪製角度
private float angle = 0;
// 線寬
private float strokWidth = 5;
// 距離邊框的空白大小
private float padding = 0;

開始動畫:

public void startAnimation() {

    // 簡單的ValueAnimator,從0度到360度變化。
    ValueAnimator circleAnimator = ValueAnimator.ofFloat(0, 360);
    circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            angle = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 一直正向循環
    circleAnimator.setRepeatMode(ValueAnimator.RESTART);
    circleAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制線寬,從5像素到20像素
    ValueAnimator thickAnimator = ValueAnimator.ofFloat(5, 20);
    thickAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            strokWidth = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 反向循環
    thickAnimator.setRepeatMode(ValueAnimator.REVERSE);
    thickAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制繪製空間的空白變化,會讓弧線變曲
    ValueAnimator paddingAnimator = ValueAnimator.ofFloat(10, 100);
    paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            padding = (float) animation.getAnimatedValue();
            invalidate();
        }
    });

    paddingAnimator.setRepeatMode(ValueAnimator.REVERSE);
    paddingAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 同時播放三個動畫
    animatorSet = new AnimatorSet();
    animatorSet.play(circleAnimator).with(thickAnimator).with(paddingAnimator);
    animatorSet.setDuration(1500);
    animatorSet.start();
}

繪製空間的變化:

// 獲取繪製空間,通過padding控制座標
private RectF getRectF() {

    return new RectF(0f + strokWidth / 2 + padding, // 左
            0f + strokWidth / 2 + padding, // 上
            getWidth() - strokWidth / 2 - padding, // 右
            getWidth() - strokWidth / 2 - padding); // 下
}

附上全文件

/**
 * Created by JayChen on 2016/10/13.
 */

public class TripProgress extends View {

private Paint paint;

// 繪製角度
private float angle = 0;
// 線寬
private float strokWidth = 5;
// 距離邊框的空白大小
private float padding = 0;

private AnimatorSet animatorSet;

public TripProgress(Context context, AttributeSet attrs) {
    super(context, attrs);

    // 抗鋸齒
    paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    // 默認顏色
    paint.setColor(Color.parseColor("#B33425"));
    // 設置爲只畫線不填充
    paint.setStyle(Paint.Style.STROKE);
    // 設置默認的線寬
    paint.setStrokeWidth(strokWidth);
    // 將線的兩頭設置爲半圓
    paint.setStrokeCap(Paint.Cap.ROUND);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (animatorSet == null) {
        // 如果動畫不存在,就啓動動畫
        startAnimation();
    }
    // 設置線寬
    paint.setStrokeWidth(strokWidth);

    // 畫紅色弧線
    paint.setColor(Color.parseColor("#B33425"));
    canvas.drawArc(getRectF(), 0f + angle, 80f, false, paint);

    // 隔開120度,再畫灰線
    paint.setColor(Color.parseColor("#5D5C5A"));
    canvas.drawArc(getRectF(), 0f + angle + 120, 80f, false, paint);

    // 隔開240度, 再畫藍線
    paint.setColor(Color.parseColor("#2972A6"));
    canvas.drawArc(getRectF(), 0f + angle + 240, 80f, false, paint);
}

public void startAnimation() {

    // 簡單的ValueAnimator,從0度到360度變化。
    ValueAnimator circleAnimator = ValueAnimator.ofFloat(0, 360);
    circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            angle = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 一直正向循環
    circleAnimator.setRepeatMode(ValueAnimator.RESTART);
    circleAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制線寬,從5像素到20像素
    ValueAnimator thickAnimator = ValueAnimator.ofFloat(5, 20);
    thickAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            strokWidth = (float) animation.getAnimatedValue();
            invalidate();
        }
    });
    // 反向循環
    thickAnimator.setRepeatMode(ValueAnimator.REVERSE);
    thickAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 控制繪製空間的空白變化,會讓弧線變曲
    ValueAnimator paddingAnimator = ValueAnimator.ofFloat(10, 100);
    paddingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            padding = (float) animation.getAnimatedValue();
            invalidate();
        }
    });

    paddingAnimator.setRepeatMode(ValueAnimator.REVERSE);
    paddingAnimator.setRepeatCount(ValueAnimator.INFINITE);

    // 同時播放三個動畫
    animatorSet = new AnimatorSet();
    animatorSet.play(circleAnimator).with(thickAnimator).with(paddingAnimator);
    animatorSet.setDuration(1500);
    animatorSet.start();
}

// 獲取繪製空間,通過padding控制座標
private RectF getRectF() {

    return new RectF(0f + strokWidth / 2 + padding, // 左
            0f + strokWidth / 2 + padding, // 上
            getWidth() - strokWidth / 2 - padding, // 右
            getWidth() - strokWidth / 2 - padding); // 下
}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章