1. 程式人生 > >Android 自定義圓形進度條(圓環刻度)View

Android 自定義圓形進度條(圓環刻度)View

這個也剛好是公司軟體最近的需求需要到的,當初最早的版本是使用美工切好的圖,在上面的基礎上覆蓋一層背景,但多多少少的有些瑕疵。於是就自己自定義寫了一個View實現

效果圖

一、繪製圓環

圓環故名思意,第一個首先繪製是圓環

1:圓環繪製函式

圓環API

public void drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

引數說明

oval:圓弧所在的橢圓物件。

startAngle:圓弧的起始角度。

sweepAngle:圓弧的角度。

useCenter:是否顯示半徑連線,true
表示顯示圓弧與圓心的半徑連線,false表示不顯示。 paint:繪製時所使用的畫筆。
 //circleCenter 整個圓半徑 radius圓環的半徑
RectF oval = new RectF(circleCenter - radius, circleCenter - radius, circleCenter + radius, circleCenter + radius);
//因為-90°才是從12點鐘方向開始 所以從-90開始 progress 為進度
canvas.drawArc(oval, -90, (float) (progress * 3.6), false, paint);

2:對圓環上色

因為要的是漸變效果API也有提供

函式名是:SweepGradient 
建構函式
public SweepGradient (float cx, float cy, int[] colors, float[] positions)
cx  渲染中心點x 座標
cy  渲染中心y 點座標
colors  圍繞中心渲染的顏色陣列,至少要有兩種顏色值
positions   相對位置的 顏色 陣列 ,可為null,  若為null,可為null, 顏色 沿漸變線 均勻分佈

public SweepGradient (float cx, float cy, int color0, int color1)
cx  渲染中心點x 座標
cy  渲染中心點y 座標
color0  起始渲染顏色
color1  結束渲染顏色

實現樣式

//漸變顏色 你可以新增很多種但是至少要保持在2種顏色以上
int[] colors = {0xffff4639, 0xffCDD513, 0xff3CDF5F};
 //circleWidth 圓的直徑 取中心點 
 SweepGradient sweepGradient = new SweepGradient(this.circleWidth / 2, this.circleWidth / 2, colors, null);

但是最後實現出來的效果是漸變開始角度是從0°開始的 但是我們想要的要求是從-90°開始 因此需要對繪製的圓環進行旋轉

  //旋轉 不然是從0度開始漸變
  Matrix matrix = new Matrix();
  matrix.setRotate(-90, this.circleWidth / 2, this.circleWidth / 2);
  sweepGradient.setLocalMatrix(matrix);

最後將漸變新增到圓環

  paint.setShader(sweepGradient);

因為是需要保持第一個圓環的採用漸變,所以在繪製時候在利用完之後 將設定
paint.setShader(null);

3:繪製剩餘的進度

一樣的是繪製圓環開始角度

//同樣的因為是反向繪製的 也可以根據當前的有顏色的角度結束角度開始繪製到-90°
 canvas.drawArc(oval, -90, (float) (-(100 - progress) * 3.6), false, paint);

最終實現效果如圖1所示

二、刻度

1:圓環刻度

是對整個圓環根據刻度大小進行平分,計算出每個所佔的角度 然後根據當前的進度計算該顯示幾個圓環之後再繪製上去,刻度使用是也是圓環,只是角度很小而已
如下

            float start = -90f;
            float p = ((float) maxColorNumber / (float) 100);
            p = (int) (progress * p);
            for (int i = 0; i < p; i++) {
                paint.setColor(roundBackgroundColor);
                // 繪製間隔快
                canvas.drawArc(oval, start + singlPoint - lineWidth, lineWidth, false,                 paint); 
                start = (start + singlPoint);
            }

2:文字刻度

也就是繪製文字是對文字繪製之後進行相應的旋轉

     //繪製文字刻度
        for (int i = 1; i <= 10; i++) {
            canvas.save();// 儲存當前畫布
            canvas.rotate(360 / 10 * i, circleCenter, circleCenter);

            canvas.drawText(i * 10 + "", circleCenter, circleCenter - radius +    roundWidth / 2 + getDpValue(4) + textSize, mPaintText);
            canvas.restore();//
        }

最後上整個View程式碼

package com.example.shall.myapplication;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;

public class CircularRingPercentageView extends View {
    private Paint paint;
    private int circleWidth;
    private int roundBackgroundColor;
    private int textColor;
    private float textSize;
    private float roundWidth;
    private float progress = 0;
    private int[] colors = {0xffff4639, 0xffCDD513, 0xff3CDF5F};
    private int radius;
    private RectF oval;
    private Paint mPaintText;
    private int maxColorNumber = 100;
    private float singlPoint = 9;
    private float lineWidth = 0.3f;
    private int circleCenter;
    private SweepGradient sweepGradient;
    private boolean isLine;

    /**
     * 分割的數量
     *
     * @param maxColorNumber 數量
     */
    public void setMaxColorNumber(int maxColorNumber) {
        this.maxColorNumber = maxColorNumber;
        singlPoint = (float) 360 / (float) maxColorNumber;
        invalidate();
    }

    /**
     * 是否是線條
     *
     * @param line true 是 false否
     */
    public void setLine(boolean line) {
        isLine = line;
        invalidate();
    }

    public int getCircleWidth() {
        return circleWidth;
    }

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

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


    public CircularRingPercentageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularRing);
        maxColorNumber = mTypedArray.getInt(R.styleable.CircularRing_circleNumber, 40);
        circleWidth = mTypedArray.getDimensionPixelOffset(R.styleable.CircularRing_circleWidth, getDpValue(180));
        roundBackgroundColor = mTypedArray.getColor(R.styleable.CircularRing_roundColor, 0xffdddddd);
        textColor = mTypedArray.getColor(R.styleable.CircularRing_circleTextColor, 0xff999999);
        roundWidth = mTypedArray.getDimension(R.styleable.CircularRing_circleRoundWidth, 40);
        textSize = mTypedArray.getDimension(R.styleable.CircularRing_circleTextSize, getDpValue(8));
        colors[0] = mTypedArray.getColor(R.styleable.CircularRing_circleColor1, 0xffff4639);
        colors[1] = mTypedArray.getColor(R.styleable.CircularRing_circleColor2, 0xffcdd513);
        colors[2] = mTypedArray.getColor(R.styleable.CircularRing_circleColor3, 0xff3cdf5f);
        initView();
        mTypedArray.recycle();
    }


    /**
     * 空白出顏色背景
     *
     * @param roundBackgroundColor
     */
    public void setRoundBackgroundColor(int roundBackgroundColor) {
        this.roundBackgroundColor = roundBackgroundColor;
        paint.setColor(roundBackgroundColor);
        invalidate();
    }

    /**
     * 刻度字型顏色
     *
     * @param textColor
     */
    public void setTextColor(int textColor) {
        this.textColor = textColor;
        mPaintText.setColor(textColor);
        invalidate();
    }

    /**
     * 刻度字型大小
     *
     * @param textSize
     */
    public void setTextSize(float textSize) {
        this.textSize = textSize;
        mPaintText.setTextSize(textSize);
        invalidate();
    }

    /**
     * 漸變顏色
     *
     * @param colors
     */
    public void setColors(int[] colors) {
        if (colors.length < 2) {
            throw new IllegalArgumentException("colors length < 2");
        }
        this.colors = colors;
        sweepGradientInit();
        invalidate();
    }


    /**
     * 間隔角度大小
     *
     * @param lineWidth
     */
    public void setLineWidth(float lineWidth) {
        this.lineWidth = lineWidth;
        invalidate();
    }


    private int getDpValue(int w) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, w, getContext().getResources().getDisplayMetrics());
    }

    /**
     * 圓環寬度
     *
     * @param roundWidth 寬度
     */
    public void setRoundWidth(float roundWidth) {
        this.roundWidth = roundWidth;
        if (roundWidth > circleCenter) {
            this.roundWidth = circleCenter;
        }
        radius = (int) (circleCenter - this.roundWidth / 2); // 圓環的半徑
        oval.left = circleCenter - radius;
        oval.right = circleCenter + radius;
        oval.bottom = circleCenter + radius;
        oval.top = circleCenter - radius;
        paint.setStrokeWidth(this.roundWidth);
        invalidate();
    }

    /**
     * 圓環的直徑
     *
     * @param circleWidth 直徑
     */
    public void setCircleWidth(int circleWidth) {
        this.circleWidth = circleWidth;
        circleCenter = circleWidth / 2;

        if (roundWidth > circleCenter) {
            roundWidth = circleCenter;
        }
        setRoundWidth(roundWidth);
        sweepGradient = new SweepGradient(this.circleWidth / 2, this.circleWidth / 2, colors, null);
        //旋轉 不然是從0度開始漸變
        Matrix matrix = new Matrix();
        matrix.setRotate(-90, this.circleWidth / 2, this.circleWidth / 2);
        sweepGradient.setLocalMatrix(matrix);
    }

    /**
     * 漸變初始化
     */
    public void sweepGradientInit() {
        //漸變顏色
        sweepGradient = new SweepGradient(this.circleWidth / 2, this.circleWidth / 2, colors, null);
        //旋轉 不然是從0度開始漸變
        Matrix matrix = new Matrix();
        matrix.setRotate(-90, this.circleWidth / 2, this.circleWidth / 2);
        sweepGradient.setLocalMatrix(matrix);
    }

    public void initView() {

        circleCenter = circleWidth / 2;//半徑
        singlPoint = (float) 360 / (float) maxColorNumber;
        radius = (int) (circleCenter - roundWidth / 2); // 圓環的半徑
        sweepGradientInit();
        mPaintText = new Paint();
        mPaintText.setColor(textColor);
        mPaintText.setTextAlign(Paint.Align.CENTER);
        mPaintText.setTextSize(textSize);
        mPaintText.setAntiAlias(true);

        paint = new Paint();
        paint.setColor(roundBackgroundColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(roundWidth);
        paint.setAntiAlias(true);

        // 用於定義的圓弧的形狀和大小的界限
        oval = new RectF(circleCenter - radius, circleCenter - radius, circleCenter + radius, circleCenter + radius);

    }


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

        //背景漸變顏色
        paint.setShader(sweepGradient);
        canvas.drawArc(oval, -90, (float) (progress * 3.6), false, paint);
        paint.setShader(null);

        //是否是線條模式
        if (!isLine) {
            float start = -90f;
            float p = ((float) maxColorNumber / (float) 100);
            p = (int) (progress * p);
            for (int i = 0; i < p; i++) {
                paint.setColor(roundBackgroundColor);
                canvas.drawArc(oval, start + singlPoint - lineWidth, lineWidth, false, paint); // 繪製間隔快
                start = (start + singlPoint);
            }
        }
        //繪製剩下的空白區域
        paint.setColor(roundBackgroundColor);
        canvas.drawArc(oval, -90, (float) (-(100 - progress) * 3.6), false, paint);

        //繪製文字刻度
        for (int i = 1; i <= 10; i++) {
            canvas.save();// 儲存當前畫布
            canvas.rotate(360 / 10 * i, circleCenter, circleCenter);
            canvas.drawText(i * 10 + "", circleCenter, circleCenter - radius + roundWidth / 2 + getDpValue(4) + textSize, mPaintText);
            canvas.restore();//
        }
    }


    OnProgressScore onProgressScore;

    public interface OnProgressScore {
        void setProgressScore(float score);

    }

    public synchronized void setProgress(final float p) {
        progress = p;
        postInvalidate();
    }

    /**
     * @param p
     */
    public synchronized void setProgress(final float p, OnProgressScore onProgressScore) {
        this.onProgressScore = onProgressScore;
        progress = p;
        postInvalidate();
    }

}