自定义圆环

这个博客展示了如何在Android中实现自定义环形统计图,通过Path绘制圆环,并提供了详细代码。博客涵盖圆环颜色设定、圆环宽度、扇形角度、进度文字等关键点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.text.myapplication;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

import java.text.NumberFormat;

/**
 * 自定义环形统计图 - Path绘制
 * <p>
 * <p>
 * Created by lijinlei on 2017/6/27.
 */
public class CircularStatisticsView extends View {
    /**
     * 默认起点扇形角度
     */
    private float mStartAngle  = -180;
    /**
     * 第一圈圆环的颜色
     */
    private int   mRingColor   = Color.parseColor("#F05A4A");
    /**
     * 第二圈的颜色
     */
    private int   mSectorColor = Color.parseColor("#29AB91");
    /**
     * 第3圈的颜色
     */
    private int   mThreeColor  = Color.parseColor("#ff0000");
    /**
     * 第3圈的颜色
     */
    private int   mFourColor   = Color.parseColor("#f54200");
    /**
     * 第3圈的颜色
     */
    private int   mFiveColor   = Color.parseColor("#f33300");
    int[] mColor = {Color.parseColor("#f33300"), Color.parseColor("#f54200"), Color.parseColor("#ff0000"), Color
            .parseColor("#29AB91"), Color.parseColor("#F05A4A")};
    /**
     * 终点扇形角度
     */
    private float  mEndAngle    = mStartAngle;
    /**
     * 扇形扫角
     */
    private float  mSweepAngle  = 0;
    /**
     * 圈环的宽度
     */
    private float  mCircleWidth = 0;
    /**
     * 圆点的半径
     */
    private float  mDotRadius   = 6f;
    /**
     * 字体的大小
     */
    private float  mTextSize    = 23.5f;
    /**
     * 剩余文字
     */
    private String reminderText = "剩余";
    /**
     * 进度文字
     */
    private String progressText = "已使用";
    private Canvas  canvas;
    /**
     * 中心x坐标
     */
    private float   centerX;
    /**
     * 中心y坐标
     */
    private float   centerY;
    /**
     * 外圆的半径
     */
    private float   mOuterRadius;
    /**
     * 内圆的半径
     */
    private float   mInnerRadius;
    /**
     * 绘制圆环的画笔
     */
    private Paint   mPaint;
    /**
     * 绘制文本的画笔
     */
    private Paint   mTextPaint;
    /**
     * 用于绘制圆环的路径
     */
    private Path    mPath;
    private Context context;
    float[] ring;

    public CircularStatisticsView(Context context) {
        super(context);
        init(context);
    }

    public CircularStatisticsView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(Context context) {
        this.context=context;
        // 圆环画笔
        mPaint = new Paint();
        // 消除锯齿
        mPaint.setAntiAlias(true);
        // 设置填充
        mPaint.setStyle(Paint.Style.FILL);
        // 绘制文本的画笔
        mTextPaint = new Paint();
        // 设置线和字体的宽度
        mTextPaint.setStrokeWidth(2);
        // 消除锯齿
        mTextPaint.setAntiAlias(true);
        // 设置字体大小
        mTextPaint.setTextSize(mTextSize);
        //
        mPath = new Path();
    }

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

    @Override
    protected void onDraw(Canvas canvas) {
        this.canvas = canvas;
        // 获取圆心的x坐标
        centerX = getWidth() / 2f;
        // 获取圆心的y坐
        centerY = getHeight() / 2f;
        // 计算外圆和内圆的半径
        if (centerX < centerY) {
            mOuterRadius = centerX / 2f;// 外圆半径
        } else {
            mOuterRadius = centerY / 2f;
        }
        if (mCircleWidth <= 0 || mCircleWidth > mOuterRadius) {
            mInnerRadius = mOuterRadius / 2f;// 内圆半径
        } else {
            mInnerRadius = mOuterRadius - mCircleWidth;
        }
        for (int i = 0; i < ring.length; i++) {
            mSweepAngle = (ring[i] / 100) * 360f;
            mEndAngle = mStartAngle + mSweepAngle;
            getSectorClip(canvas, mStartAngle, mEndAngle, mColor[i]);

            // 获取进度圆弧的中心点
            if (i == 0) {
                float textX1 = (float) (centerX + mOuterRadius * Math.cos((mSweepAngle / 2 - 180) * Math.PI / 180));
                float textY1 = (float) (centerY + mOuterRadius * Math.sin((mSweepAngle / 2 - 180) * Math.PI / 180));
                // 设置画笔颜色
                mTextPaint.setColor(mColor[i]);
                drawText(canvas, getPer(mSweepAngle, 360), textX1, textY1, mTextPaint);
            } else {
                // 获取进度圆弧的中心点
                if (mStartAngle + mSweepAngle / 2 < 0) {
                    float textX2 = (float) (centerX + mOuterRadius * Math.cos(((-mSweepAngle / 2 + mEndAngle)) * Math.PI / 180));
                    float textY2 = (float) (centerY + mOuterRadius * Math.sin(((-mSweepAngle / 2 + mEndAngle)) * Math.PI / 180));
                    // 设置画笔颜色
                    mTextPaint.setColor(mColor[i]);
                    drawText(canvas, getPer(mSweepAngle, 360), textX2, textY2, mTextPaint);
                } else {
                    float textX2 = (float) (centerX + mOuterRadius * Math.cos(((mSweepAngle / 2 + mStartAngle)) * Math.PI / 180));
                    float textY2 = (float) (centerY + mOuterRadius * Math.sin(((mSweepAngle / 2 + mStartAngle)) * Math.PI / 180));
                    // 设置画笔颜色
                    mTextPaint.setColor(mColor[i]);
                    drawText(canvas, getPer(mSweepAngle, 360), textX2, textY2, mTextPaint);
                }
            }
            mStartAngle = mEndAngle;
        }
        canvas.drawPoint(centerX, centerY, mTextPaint);
        // 设置内圆的颜色
        mPaint.setColor(Color.WHITE);
        mPath.addCircle(centerX, centerY, mInnerRadius, Path.Direction.CCW);
        mPath.close();
        // 绘制内圆
        canvas.drawPath(mPath, mPaint);
        mPath.reset();
        mPaint.setColor(Color.BLACK);
        mPaint.setTextSize(30);
        canvas.drawText("20只",centerX-25,centerY+15,mPaint);
    }

    /**
     * 绘制一个扇形的裁剪区
     *
     * @param canvas     画布
     * @param startAngle 扇形的起始角度
     */
    private void getSectorClip(Canvas canvas, float startAngle, float endAngle, int sectorColor) {
        // 进度的颜色
        mPaint.setColor(sectorColor);
        Path path = new Path();
        // 以下获得一个三角形的裁剪区
        // 圆心
        path.moveTo(centerX, centerY);
        // 起始点角度在圆上对应的横坐标
        float mStartAngleX = (float) (centerX + mOuterRadius * Math.cos(startAngle * Math.PI / 180));
        // 起始点角度在圆上对应的纵坐标
        float mStartAngleY = (float) (centerY + mOuterRadius * Math.sin(startAngle * Math.PI / 180));
        path.lineTo(mStartAngleX, mStartAngleY);
        // 终点角度在圆上对应的横坐标
        float mEndAngleX = (float) (centerX + mOuterRadius * Math.cos(mEndAngle * Math.PI / 180));
        // 终点点角度在圆上对应的纵坐标
        float mEndAngleY = (float) (centerY + mOuterRadius * Math.sin(mEndAngle * Math.PI / 180));
        path.lineTo(mEndAngleX, mEndAngleY);
        // 回到初始点形成封闭的曲线
        path.close();
        // 设置一个正方形,内切圆
        RectF rectF = new RectF(centerX - mOuterRadius, centerY - mOuterRadius, centerX + mOuterRadius, centerY +
                mOuterRadius);
        // 获得弧形剪裁区的方法
        path.addArc(rectF, startAngle, endAngle - startAngle);
        canvas.drawPath(path, mPaint);
    }

    /**
     * 计算百分比
     *
     * @return 返回比例
     */
    private String getPer(float now, float total) {
        // 创建一个数值格式化对象
        NumberFormat numberFormat = NumberFormat.getInstance();
        // 设置精确到小数点后2位
        numberFormat.setMaximumFractionDigits(2);
        String result = numberFormat.format(now / total * 100);
        return result + "%";
    }

    /**
     * 设置圆环宽度
     *
     * @param width 圆环宽度
     */
    public void setCircleWidth(int width) {
        mCircleWidth = width;
        invalidate();
    }

    /**
     * 设置当前进度
     */
    public void setPercentage(float[] ring, float sector) {
        this.ring = ring;
        postInvalidate();
    }

    private float mOffset1X    = 20;
    private float mOffset1Y    = 20;
    private float mOffset2X    = 150;
    private float mOffset2Y    = 20;
    private float mPointOffset = 10;

    /**
     * 绘制文字
     *
     * @param canvas 画布
     * @param string 绘制的内容
     * @param firstX 起始点X坐标
     * @param firstY 起始点Y坐标
     * @param paint  文字的画笔
     */
    private void drawText(Canvas canvas, String string, float firstX, float firstY, Paint paint) {
        Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
        float endX1 = 0;
        float endY1 = 0;
        float endX2 = 0;
        float endY2 = 0;
        float textX = 0;
        float textY = 0;
        float bitmapX=0;
        float bitmapY=0;
        //初始点位于第四象限
        if (firstX <= centerX && firstY <= centerY) {
            firstX = firstX - mPointOffset;
            firstY = firstY - mPointOffset;
            endX1 = firstX - mOffset1X;
            endY1 = firstY - mOffset1Y;
            endX2 = firstX - mOffset2X;
            endY2 = firstY - mOffset2Y;
            textX = endX2;
            textY = endY2 - 10;
            float x=bmp.getWidth();
            bitmapX=endX2-bmp.getWidth()-5;
            bitmapY=endY2-bmp.getHeight()/2;
            canvas.drawBitmap(bmp,bitmapX, bitmapY,mPaint);

        }
        //初始点位于第三象限
        if (firstX < centerX && firstY > centerY) {
            firstX = firstX - mPointOffset;
            firstY = firstY + mPointOffset;
            endX1 = firstX - mOffset1X;
            endY1 = firstY + mOffset1Y;
            endX2 = firstX - mOffset2X;
            endY2 = firstY + mOffset2Y;
            textX = endX2;
            textY = endY2 + 30;
            bitmapX=endX2-bmp.getWidth()-5;
            bitmapY=endY2-bmp.getHeight()/2;
            canvas.drawBitmap(bmp,bitmapX, bitmapY,mPaint);
        }
        //初始点位于第一象限
        if (firstX > centerX && firstY < centerY) {
            firstX = firstX + mPointOffset;
            firstY = firstY - mPointOffset;
            endX1 = firstX + mOffset1X;
            endY1 = firstY - mOffset1Y;
            endX2 = firstX + mOffset2X;
            endY2 = firstY - mOffset2Y;
            textX = endX1;
            textY = endY2 - 10;
            bitmapX=endX2+5;
            bitmapY=endY2-bmp.getHeight()/2;
            canvas.drawBitmap(bmp,bitmapX, bitmapY,mPaint);
        }
        //初始点位于第二象限
        if (firstX >= centerX && firstY >= centerY) {
            firstX = firstX + mPointOffset;
            firstY = firstY + mPointOffset;
            endX1 = firstX + mOffset1X;
            endY1 = firstY + mOffset1Y;
            endX2 = firstX + mOffset2X;
            endY2 = firstY + mOffset2Y;
            textX = endX1;
            textY = endY2 + 30;
            bitmapX=endX2+5;
            bitmapY=endY2-bmp.getHeight()/2;
            canvas.drawBitmap(bmp,bitmapX, bitmapY,mPaint);
        }
        canvas.drawCircle(firstX, firstY, mDotRadius, paint);
        canvas.drawLine(firstX, firstY, endX1, endY1, paint);
        canvas.drawLine(endX1, endY1, endX2, endY2, paint);
        canvas.drawText(string, textX, textY, paint);
        canvas.drawText("20只",centerX,centerY,paint);
    }
}

package com.text.myapplication;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {
    CircularStatisticsView mPieChart;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loction);
        float[] ring = {10, 20, 30, 20, 20};
        mPieChart = (CircularStatisticsView) findViewById(R.id.mPieChart);
        mPieChart.setPercentage(ring, 100);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值