package com.cmeiyuan.widget.chart; import java.text.DecimalFormat; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.Paint.Style; import android.graphics.RectF; import android.graphics.SweepGradient; import android.graphics.Typeface; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import com.cmeiyuan.common.TextUtil; import com.github.mikephil.charting.utils.Utils; import com.cmeiyuan.widget.R; /** * @author 334388454@qq.com */ public class CircleChartView extends View { protected static final String TAG = "CircleChartView"; // 百分比 private float mPercentValue; // 初始角度 private int mInitialAngle = -90; // 外环宽度 private float mOuterRingWidth; // 内环宽度 private float mInnerRingWidth; // 外环起始颜色 private final int mOuterRingStartColor; // 外环结束颜色 private int mOuterRingEndColor; // 外环颜色 private int mOuterRingColor; // 内环颜色 private int mInnerRingColor; // 内环颜色 private int mCenterFillColor; // 背景层画笔 private Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 中间层画笔 private Paint mCenterFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 外环圈画笔 private Paint mPercentFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 主标签画笔 private final Paint mLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 标签后缀画笔 private final Paint mLableSuffixPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 附加标签画笔 private final Paint mExtraLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 圆环区域 private final RectF mRect = new RectF(); // 渐变变换矩阵 private final Matrix mMatrix = new Matrix(); // 扫描渐变 private SweepGradient mSweepGradient; // 小数点截取 private DecimalFormat mFormat = new DecimalFormat("#0.00"); // 附加文字 private String mExtraLabel; // 文字间距 private float mLabelMargin; // 黄金分割点、比例数字显示基线 private final float mGoldenPoint = 0.618f; public CircleChartView(Context context) { this(context, null); } public CircleChartView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public CircleChartView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleChartView, defStyle, 0); int percent = a.getInt(R.styleable.CircleChartView_percent, 0); int outerRingColor = a.getColor(R.styleable.CircleChartView_outerRingColor, Color.RED); int innerRingColor = a.getColor(R.styleable.CircleChartView_innerRingColor, Color.GRAY); int outerRingStartColor = a.getColor(R.styleable.CircleChartView_outerRingStartColor, Color.YELLOW); int outerRingEndColor = a.getColor(R.styleable.CircleChartView_outerRingEndColor, Color.RED); int centerAreaColor = a.getColor(R.styleable.CircleChartView_centerAreaColor, Color.WHITE); float outerRingWidth = a.getDimension(R.styleable.CircleChartView_outerRingWidth, 60); float innerRingWidth = a.getDimension(R.styleable.CircleChartView_innerRingWidth, 45); float textSize = a.getDimension(R.styleable.CircleChartView_textSize, 120); int textColor = a.getColor(R.styleable.CircleChartView_textColor, Color.RED); float extraTextSize = a.getDimension(R.styleable.CircleChartView_extraTextSize, 120); int extraTextColor = a.getColor(R.styleable.CircleChartView_extraTextColor, Color.BLACK); float margin = a.getDimension(R.styleable.CircleChartView_textLabelMagin, 30); String fontName = a.getString(R.styleable.CircleChartView_fontName); this.mPercentValue = percent; this.mOuterRingColor = outerRingColor; this.mInnerRingColor = innerRingColor; this.mOuterRingStartColor = outerRingStartColor; this.mOuterRingEndColor = outerRingEndColor; this.mCenterFillColor = centerAreaColor; this.mOuterRingWidth = outerRingWidth; this.mInnerRingWidth = innerRingWidth; this.mLabelPaint.setTextSize(textSize); this.mLabelPaint.setColor(textColor); this.mLableSuffixPaint.setColor(textColor); this.mLableSuffixPaint.setTextSize(textSize / 2); this.mExtraLabelPaint.setTextSize(extraTextSize); this.mExtraLabelPaint.setColor(extraTextColor); this.mLabelMargin = margin; if (fontName != null && fontName.length() > 0) { try { mLabelPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), fontName)); mLableSuffixPaint.setTypeface(Typeface.createFromAsset(getContext().getAssets(), fontName)); } catch (Exception e) { } } a.recycle(); } @SuppressLint("DrawAllocation") @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 中心点坐标 float cirX = getCenterX(); float cirY = getCenterY(); // 半径 float radius = getRadius(); float sweepAngle = mPercentValue * 360 / 100; float left = cirX - radius; float top = cirY - radius; float right = cirX + radius; float bottom = cirY + radius; // 绘制背景 mCenterFillPaint.setStyle(Style.FILL); mCenterFillPaint.setColor(mCenterFillColor); float bgRadius = radius - Math.abs(mOuterRingWidth - mInnerRingWidth); canvas.drawCircle(cirX, cirY, bgRadius, mCenterFillPaint); // 绘制内环 float innerL = left + (mOuterRingWidth - mInnerRingWidth / 2); float innerT = top + (mOuterRingWidth - mInnerRingWidth / 2); float innerR = right - (mOuterRingWidth - mInnerRingWidth / 2); float innerB = bottom - (mOuterRingWidth - mInnerRingWidth / 2); mRect.set(innerL, innerT, innerR, innerB); mBackgroundPaint.setStyle(Style.STROKE); mBackgroundPaint.setColor(mInnerRingColor); mBackgroundPaint.setStrokeWidth(mInnerRingWidth); canvas.drawArc(mRect, getInitialAngle(), -360, false, mBackgroundPaint); // 绘制外环 if (mSweepGradient != null) { mMatrix.reset(); mMatrix.postRotate(270, cirX, cirX); mSweepGradient.setLocalMatrix(mMatrix); mPercentFillPaint.setShader(mSweepGradient); } float offset = 0; float outerL = left + (mOuterRingWidth / 2 + offset); float outerT = top + (mOuterRingWidth / 2 + offset); float ourerR = right - (mOuterRingWidth / 2 + offset); float outerB = bottom - (mOuterRingWidth / 2 + offset); mRect.set(outerL, outerT, ourerR, outerB); mPercentFillPaint.setStyle(Style.STROKE); mPercentFillPaint.setStrokeWidth(mOuterRingWidth); mPercentFillPaint.setColor(mOuterRingColor); canvas.drawArc(mRect, getInitialAngle() + 0.2f, sweepAngle, false, mPercentFillPaint); String labelText = mFormat.format(mPercentValue); String suffixText = "%"; String extraText = mExtraLabel; Paint lablePaint = mLabelPaint; Paint lableSuffixPaint = mLableSuffixPaint; Paint extraLabelPaint = mExtraLabelPaint; int labelH = TextUtil.getTextHeight(lablePaint, "63.23"); // 绘制数字 if (labelText != null && labelText.length() > 0) { float p = mGoldenPoint; boolean isSingleLine = TextUtils.isEmpty(extraText); if (isSingleLine) { lableSuffixPaint.setTextSize(lablePaint.getTextSize() * 0.7f); } float x1 = cirX - lableSuffixPaint.getTextSize() * 0.3f; float y1 = isSingleLine ? cirY + lablePaint.getTextSize() * 0.3f : (getHeight() * p); lablePaint.setTextAlign(Align.CENTER); canvas.drawText(labelText, x1, y1, lablePaint); lableSuffixPaint.setTextAlign(Align.LEFT); canvas.drawText(suffixText, x1 + lablePaint.measureText(labelText) / 2 + lableSuffixPaint.getTextSize() * 0.12f, y1, lableSuffixPaint); } // 绘制附加文字 if (extraText != null && extraText.length() > 0) { float p = mGoldenPoint; int x1 = (int) cirX; int y1 = (int) (getHeight() * p - labelH - mLabelMargin); extraLabelPaint.setTextAlign(Align.CENTER); canvas.drawText(extraText, x1, y1, extraLabelPaint); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w > 0 && h > 0) { float cirX = getCenterX(); float cirY = getCenterY(); int startColor = mOuterRingStartColor; int endColor = mOuterRingEndColor; mSweepGradient = new SweepGradient(cirX, cirY, startColor, endColor); } } protected float getRadius() { return (getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - Utils.convertDpToPixel(5)) / 2f; } protected float getCenterX() { return getMeasuredWidth() / 2f; } protected float getCenterY() { return getMeasuredHeight() / 2f; } public void setFloatFormat(String format) { mFormat = new DecimalFormat(format); } public float getOuterRingWidth() { return mOuterRingWidth; } public void setOuterRingWidth(float width) { this.mOuterRingWidth = width; } public float getInnerRingWidth() { return mInnerRingWidth; } public void setInnerRingWidth(float width) { this.mInnerRingWidth = width; } public int getOuterRingStartColor() { return mOuterRingStartColor; } public int getOuterRingEndColor() { return mOuterRingEndColor; } public int getOuterRingColor() { return mOuterRingColor; } public void setOuterRingColor(int color) { this.mOuterRingColor = color; } public int getInnerRingColor() { return mInnerRingColor; } public void setInnerRingColor(int color) { this.mInnerRingColor = color; } public int getCenterFillColor() { return mCenterFillColor; } public void setCenterFillColor(int color) { this.mCenterFillColor = color; } public Paint getBackgroundPaint() { return mBackgroundPaint; } public void setBackgroundPaint(Paint paint) { this.mBackgroundPaint = paint; } public Paint getCenterFillPaint() { return mCenterFillPaint; } public void setCenterFillPaint(Paint paint) { this.mCenterFillPaint = paint; } public Paint getPercentFillPaint() { return mPercentFillPaint; } public void setPercentFillPaint(Paint paint) { this.mPercentFillPaint = paint; } public Paint getLabelPaint() { return mLabelPaint; } public Paint getLableSuffixPaint() { return mLableSuffixPaint; } public Paint getExtraLabelPaint() { return mExtraLabelPaint; } public float getTextLabelMargin() { return mLabelMargin; } public void setTextLabelMargin(float margin) { this.mLabelMargin = margin; } public void setPercent(float percent) { this.mPercentValue = percent; invalidate(); } public float getPercent() { return mPercentValue; } public void setExtraLabel(String text) { this.mExtraLabel = text; } public String getExtraLabel() { return mExtraLabel; } public void setTextSize(float textSize) { this.mLabelPaint.setTextSize(textSize); } public void setTextColor(int color) { this.mLabelPaint.setColor(color); } public void setOuterRingStartColor(int color) { this.mOuterRingColor = color; } public void setOuterRingEndColor(int color) { this.mOuterRingEndColor = color; } public int getInitialAngle() { return mInitialAngle; } public void setInitialAngle(int angle) { this.mInitialAngle = angle; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; int height = 0; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (widthMode == MeasureSpec.UNSPECIFIED) { width = widthSize; } else if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else if (widthMode == MeasureSpec.AT_MOST) { width = widthSize; } if (heightMode == MeasureSpec.UNSPECIFIED) { height = heightSize; } else if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else if (heightMode == MeasureSpec.AT_MOST) { height = heightSize; } int wh = Math.min(width, height); setMeasuredDimension(wh, wh); } }