統計圖3--環形圖

這次在網上直接找了別人現成的輪子來用



import android.animation.TypeEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.graphics.Typeface;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
import android.support.annotation.Nullable;
import android.text.TextPaint;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;

public class CircleProgressView extends View {

    private Context context;
    /**
     * 畫筆
     */
    private Paint mPaint;

    /**
     * 文本畫筆
     */
    private TextPaint mTextPaint;

    /**
     * 筆畫描邊的寬度
     */
    private float mStrokeWidth;

    /**
     * 開始角度(默認從12點鐘方向開始)
     */
    private int mStartAngle = 270;
    /**
     * 掃描角度(一個圓)
     */
    private int mSweepAngle = 360;

    /**
     * 圓心座標x
     */
    private float mCircleCenterX;
    /**
     * 圓心座標y
     */
    private float mCircleCenterY;

    /**
     * 圓正常顏色
     */
    private int mNormalColor = 0xFFC8C8C8;
    /**
     * 進度顏色
     */
    private int mProgressColor = 0xFF4FEAAC;

    /**
     * 是否使用着色器
     */
    private boolean isShader = true;

    /**
     * 着色器
     */
    private Shader mShader;

    /**
     * 着色器顏色
     */
    private int[] mShaderColors = new int[]{0xFF4FEAAC, 0xFFA8DD51, 0xFFE8D30F, 0xFFA8DD51, 0xFF4FEAAC};

    /**
     * 半徑
     */
    private float mRadius;

    /**
     * 內圓與外圓的間距
     */
    private float mCirclePadding;

    /**
     * 刻度間隔的角度大小
     */
    private float mTickSplitAngle = 5;

    /**
     * 刻度的角度大小
     */
    private float mBlockAngle = 1;

    /**
     * 總刻度數
     */
    private int mTotalTickCount;

    /**
     * 最大進度
     */
    private int mMax = Integer.MAX_VALUE;

    /**
     * 當前進度
     */
    private int mProgress = 0;

    /**
     * 動畫持續的時間
     */
    private int mDuration = 500;

    /**
     * 標籤內容
     */
    private String mLabelText;

    /**
     * 字體大小
     */
    private float mLabelTextSize;

    /**
     * 字體顏色
     */
    private int mLabelTextColor = 0xFF333333;
    /**
     * 進度百分比
     */
    private float mProgressPercent;
    private String mTextProgressPercent;

    /**
     * 是否顯示標籤文字
     */
    private boolean isShowLabel = true;
    /**
     * 是否默認顯示百分比爲標籤文字
     */
    private boolean isShowPercentText = true;
    /**
     * 是否顯示外邊框刻度
     */
    private boolean isShowTick = true;

    /**
     * 是否旋轉
     */
    private boolean isTurn = false;


    private OnChangeListener mOnChangeListener;


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

    public CircleProgressView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    /**
     * 初始化
     *
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        this.context = context;
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressView);

        DisplayMetrics displayMetrics = getDisplayMetrics();
        mStrokeWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, displayMetrics);

        mLabelTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, displayMetrics);

        mCirclePadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, displayMetrics);

        int size = a.getIndexCount();
        for (int i = 0; i < size; i++) {
            int attr = a.getIndex(i);
            if (attr == R.styleable.CircleProgressView_cpvStrokeWidth) {
                mStrokeWidth = a.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6, displayMetrics));
            } else if (attr == R.styleable.CircleProgressView_cpvNormalColor) {
                mNormalColor = a.getColor(attr, 0xFFC8C8C8);
            } else if (attr == R.styleable.CircleProgressView_cpvProgressColor) {
                mProgressColor = a.getColor(attr, 0xFF4FEAAC);
                isShader = false;
            } else if (attr == R.styleable.CircleProgressView_cpvStartAngle) {
                mStartAngle = a.getInt(attr, 270);
            } else if (attr == R.styleable.CircleProgressView_cpvSweepAngle) {
                mSweepAngle = a.getInt(attr, 360);
            } else if (attr == R.styleable.CircleProgressView_cpvMax) {
                mMax = a.getInt(attr, 100);
            } else if (attr == R.styleable.CircleProgressView_cpvProgress) {
                mProgress = a.getInt(attr, 0);
            } else if (attr == R.styleable.CircleProgressView_cpvDuration) {
                mDuration = a.getInt(attr, 500);
            } else if (attr == R.styleable.CircleProgressView_cpvLabelText) {
                mLabelText = a.getString(attr);
            } else if (attr == R.styleable.CircleProgressView_cpvLabelTextSize) {
                mLabelTextSize = a.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 30, displayMetrics));
            } else if (attr == R.styleable.CircleProgressView_cpvLabelTextColor) {
                mLabelTextColor = a.getColor(attr, 0xFF333333);
            } else if (attr == R.styleable.CircleProgressView_cpvShowLabel) {
                isShowLabel = a.getBoolean(attr, true);
            } else if (attr == R.styleable.CircleProgressView_cpvShowTick) {
                isShowTick = a.getBoolean(attr, true);
            } else if (attr == R.styleable.CircleProgressView_cpvCirclePadding) {
                mCirclePadding = a.getDimension(attr, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, displayMetrics));
            } else if (attr == R.styleable.CircleProgressView_cpvTickSplitAngle) {
                mTickSplitAngle = a.getInt(attr, 5);
            } else if (attr == R.styleable.CircleProgressView_cpvBlockAngle) {
                mBlockAngle = a.getInt(attr, 1);
            } else if (attr == R.styleable.CircleProgressView_cpvTurn) {
                isTurn = a.getBoolean(attr, false);
            }
        }

        isShowPercentText = TextUtils.isEmpty(mLabelText);

        a.recycle();
        mProgressPercent = (int) (mProgress * 100.0f / mMax);
        mPaint = new Paint();
        mTextPaint = new TextPaint();
        mTotalTickCount = (int) (mSweepAngle / (mTickSplitAngle + mBlockAngle));

    }


    private DisplayMetrics getDisplayMetrics() {
        return getResources().getDisplayMetrics();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int defaultValue = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getDisplayMetrics());

        int width = measureHandler(widthMeasureSpec, defaultValue);
        int height = measureHandler(heightMeasureSpec, defaultValue);

        //圓心座標
        mCircleCenterX = (width + getPaddingLeft() - getPaddingRight()) / 2.0f;
        mCircleCenterY = (height + getPaddingTop() - getPaddingBottom()) / 2.0f;
        //計算間距
        int padding = Math.max(getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
        //半徑=視圖寬度-橫向或縱向內間距值 - 畫筆寬度
        mRadius = (width - padding - mStrokeWidth) / 2.0f - mCirclePadding;

        //默認着色器
        mShader = new SweepGradient(mCircleCenterX, mCircleCenterX, mShaderColors, null);

        setMeasuredDimension(width, height);


    }

    /**
     * 測量
     *
     * @param measureSpec
     * @param defaultSize
     * @return
     */
    private int measureHandler(int measureSpec, int defaultSize) {

        int result = defaultSize;
        int measureMode = MeasureSpec.getMode(measureSpec);
        int measureSize = MeasureSpec.getSize(measureSpec);
        if (measureMode == MeasureSpec.EXACTLY) {
            result = measureSize;
        } else if (measureMode == MeasureSpec.AT_MOST) {
            result = Math.min(defaultSize, measureSize);
        }
        return result;
    }


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


    /**
     * 繪製弧形(默認爲一個圓)
     *
     * @param canvas
     */
    private void drawArc(Canvas canvas) {
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);

        mPaint.setStrokeWidth(mStrokeWidth);

        if (isShowTick) {//是否顯示外邊框刻度
            float tickDiameter = mRadius * 2;
            float tickStartX = mCircleCenterX - mRadius;
            float tickStartY = mCircleCenterY - mRadius;
            RectF rectF = new RectF(tickStartX, tickStartY, tickStartX + tickDiameter, tickStartY + tickDiameter);

            final int currentBlockIndex = (int) (mProgressPercent / 100f * mTotalTickCount);
            if (isTurn) {
                for (int i = 0; i < mTotalTickCount; i++) {
                    //底層未選中刻度
                    mPaint.setShader(null);
                    mPaint.setColor(Color.parseColor("#FFF8F8F8"));
                    //繪製外邊框刻度
                    canvas.drawArc(rectF, i * (mBlockAngle + mTickSplitAngle) + mStartAngle, mBlockAngle, false, mPaint);
                }

                for (int i = currentBlockIndex; i < currentBlockIndex + currentBlockIndex; i++) {
                    //已選中的刻度
                    if (isShader && mShader != null) {
                        mPaint.setShader(mShader);
                    } else {
                        mPaint.setColor(mProgressColor);
                    }
                    //繪製外邊框刻度
                    canvas.drawArc(rectF, i * (mBlockAngle + mTickSplitAngle) + mStartAngle, mBlockAngle, false, mPaint);
                }
            } else {
                for (int i = 0; i < mTotalTickCount; i++) {
                    if (i < currentBlockIndex) {
                        //已選中的刻度
                        if (isShader && mShader != null) {
                            mPaint.setShader(mShader);
                        } else {
                            mPaint.setColor(mProgressColor);
                        }
                    } else {
                        //未選中的刻度
                        mPaint.setShader(null);
                        mPaint.setColor(Color.parseColor("#FFF8F8F8"));
                    }
                    //繪製外邊框刻度
                    canvas.drawArc(rectF, i * (mBlockAngle + mTickSplitAngle) + mStartAngle, mBlockAngle, false, mPaint);
                }
            }

        }


        mPaint.setShader(null);
        mPaint.setStrokeCap(Paint.Cap.SQUARE);
        mPaint.setColor(Color.parseColor("#FFF8F8F8"));

        //進度圓半徑
        float circleRadius = isShowTick ? mRadius - mCirclePadding - mStrokeWidth : mRadius;
        float diameter = circleRadius * 2;
        float startX = mCircleCenterX - circleRadius;
        float startY = mCircleCenterY - circleRadius;
        RectF rectF1 = new RectF(startX, startY, startX + diameter, startY + diameter);

        //繪製底層弧形
        canvas.drawArc(rectF1, mStartAngle, mSweepAngle, false, mPaint);

        //着色器不爲空則設置着色器,反之用純色
        if (isShader && mShader != null) {
            mPaint.setShader(mShader);
        } else {
            mPaint.setColor(mProgressColor);
        }

        if (isTurn) {
            //繪製當前進度弧形
            canvas.drawArc(rectF1, mStartAngle + mSweepAngle * getRatio(), mSweepAngle * getRatio(), false, mPaint);
        } else {
            //繪製當前進度弧形
            canvas.drawArc(rectF1, mStartAngle, mSweepAngle * getRatio(), false, mPaint);
        }

    }

    /**
     * 繪製中間的文本
     *
     * @param canvas
     */
    private void drawText(Canvas canvas) {
        if (!isShowLabel) {
            return;
        }
        mTextPaint.reset();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mTextPaint.setTypeface(Typeface.createFromAsset(context.getAssets(), "din_alternate.ttf"));
        mTextPaint.setTextSize(mLabelTextSize);
        mTextPaint.setColor(mLabelTextColor);
        mTextPaint.setTextAlign(Paint.Align.CENTER);

        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        // 計算文字高度 
        float fontHeight = fontMetrics.bottom - fontMetrics.top;
        // 計算文字baseline 
        float textBaseY = getHeight() - (getHeight() - fontHeight) / 2 - fontMetrics.bottom;
        if (isShowPercentText) {//是否顯示百分比
            canvas.drawText(mTextProgressPercent, getWidth() / 2, textBaseY, mTextPaint);
        } else if (!TextUtils.isEmpty(mLabelText)) {//顯示自定義文本
            canvas.drawText(mLabelText, getWidth() / 2, textBaseY, mTextPaint);
        }

    }

    /**
     * 顯示進度動畫效果(根據當前已有進度開始)
     * @param progress
     */
    public void showAppendAnimation(int progress){
        showAnimation(mProgress,progress,mDuration);
    }

    /**
     * 顯示進度動畫效果
     * @param progress
     */
    public void showAnimation(int progress){
        showAnimation(progress,mDuration);
    }

    /**
     * 顯示進度動畫效果
     * @param progress
     * @param duration 動畫時長
     */
    public void showAnimation(int progress,int duration){
        showAnimation(0,progress,duration);
    }

    /**
     * 顯示進度動畫效果,從from到to變化
     * @param from
     * @param to
     * @param duration 動畫時長
     */
    public void showAnimation(int from,int to,int duration){
        this.mDuration = duration;
        this.mProgress = from;
        ValueAnimator valueAnimator = ValueAnimator.ofInt(from,to);
        valueAnimator.setDuration(duration);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                setProgress((int)animation.getAnimatedValue());
            }
        });

        valueAnimator.start();
    }

    /**
     * 進度比例
     *
     * @return
     */
    private float getRatio() {
        return mProgress * 1.0f / mMax;
    }

    /**
     * 設置最大進度
     *
     * @param max
     */
    public void setMax(int max) {
        this.mMax = max;
        invalidate();
    }

    /**
     * 設置當前進度
     *
     * @param progress
     */
    public void setProgress(int progress) {
        this.mProgress = progress;
        mProgressPercent = (float) (mProgress * 100.0f / mMax);
        mTextProgressPercent = TransformUtils.getPercent(String.valueOf(mProgress), String.valueOf(mMax), 2);
        invalidate();

        if (mOnChangeListener != null) {
            mOnChangeListener.onProgressChanged(mProgress, mMax);
        }
    }

    /**
     * 設置正常顏色
     *
     * @param color
     */
    public void setNormalColor(@ColorInt int color) {
        this.mNormalColor = color;
        invalidate();
    }


    /**
     * 設置着色器
     *
     * @param shader
     */
    public void setShader(Shader shader) {
        isShader = true;
        this.mShader = shader;
        invalidate();
    }

    /**
     * 設置進度顏色(通過着色器實現漸變色)
     *
     * @param colors
     */
    public void setProgressColor(@ColorInt int... colors) {
        Shader shader = new SweepGradient(mCircleCenterX, mCircleCenterX, colors, null);
        setShader(shader);
    }

    /**
     * 設置進度顏色(純色)
     *
     * @param color
     */
    public void setProgressColor(@ColorInt int color) {
        isShader = false;
        this.mProgressColor = color;
        invalidate();
    }

    /**
     * 設置進度顏色
     *
     * @param resId
     */
    public void setProgressColorResource(@ColorRes int resId) {
        int color = getResources().getColor(resId);
        setProgressColor(color);
    }

    /**
     * 設置是否顯示外環刻度
     *
     * @param isShowTick
     */
    public void setShowTick(boolean isShowTick) {
        this.isShowTick = isShowTick;
        invalidate();
    }

    /**
     * 設置是否旋轉
     *
     * @param isTurn
     */
    public void setTurn(boolean isTurn) {
        this.isTurn = isTurn;
        invalidate();
    }

    public int getStartAngle() {
        return mStartAngle;
    }

    public int getSweepAngle() {
        return mSweepAngle;
    }

    public float getCircleCenterX() {
        return mCircleCenterX;
    }

    public float getCircleCenterY() {
        return mCircleCenterY;
    }

    public float getRadius() {
        return mRadius;
    }

    public long getMax() {
        return mMax;
    }

    public long getProgress() {
        return mProgress;
    }

    public String getLabelText() {
        return mLabelText;
    }

    /**
     * 設置標籤文本
     *
     * @param labelText
     */
    public void setLabelText(String labelText) {
        this.mLabelText = labelText;
        this.isShowPercentText = TextUtils.isEmpty(labelText);
        invalidate();
    }

    /**
     * 進度百分比
     *
     * @return
     */
    public float getProgressPercent() {
        return mProgressPercent;
    }

    /**
     * 如果自定義設置過{@link #setLabelText(String)} 或通過xml設置過{@code app:labelText}則
     * 返回{@link #mLabelText},反之默認返回百分比{@link #mProgressPercent}
     *
     * @return
     */
    public String getText() {
        if (isShowPercentText) {
            return mTextProgressPercent;
        }

        return mLabelText;
    }

    public int getLabelTextColor() {
        return mLabelTextColor;
    }

    public void setLabelTextColor(@ColorInt int labelTextColor) {
        this.mLabelTextColor = labelTextColor;
        invalidate();
    }

    public void setLabelTextColorResource(@ColorRes int resId) {
        int color = getResources().getColor(resId);
        setLabelTextColor(color);
    }

    public void setLabelTextSize(float textSize) {
        setLabelTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
    }

    public void setLabelTextSize(int unit, float textSize) {
        float size = TypedValue.applyDimension(unit, textSize, getDisplayMetrics());
        if (mLabelTextSize != size) {
            this.mLabelTextSize = size;
            invalidate();
        }

    }

    /**
     * 設置進度改變監聽
     *
     * @param onChangeListener
     */
    public void setOnChangeListener(OnChangeListener onChangeListener) {
        this.mOnChangeListener = onChangeListener;
    }

    public interface OnChangeListener {
        void onProgressChanged(float progress, float max);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章