在 Android 中自定义饼图,核心是继承 View 并重写 onDraw() 方法,利用 Canvas.drawArc() 来绘制各个扇形区域。下面从原理到代码,带你完整实现一个自定义饼图。
核心原理
饼图的本质是将一个圆按数据比例分割成多个扇形。关键公式:
info
每个扇形的扫过角度 = (该数据值 / 数据总和) × 360°绘制工具是
完整实现代码
Canvas.drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)。完整实现代码
1. 创建自定义 View 类
public class CustomPieChart extends View {
private Paint mPaint; // 绘制扇形的画笔
private Paint mTextPaint; // 绘制文字的画笔
private List<PieEntry> mData; // 数据集合
private float mTotal; // 数据总和
private RectF mRectF; // 饼图边界矩形
private float mRadius; // 饼图半径
public CustomPieChart(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mData = new ArrayList<>();
// 扇形画笔
mPaint = new Paint();
mPaint.setAntiAlias(true); // 抗锯齿
mPaint.setStyle(Paint.Style.FILL);
// 文字画笔
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(36);
mTextPaint.setColor(Color.WHITE);
mTextPaint.setTextAlign(Paint.Align.CENTER);
}
// 添加数据
public void addData(String label, float value, int color) {
mData.add(new PieEntry(label, value, color));
mTotal += value;
invalidate(); // 触发重绘
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 以 View 中心为圆心,取宽高较小值的一半作为半径
mRadius = Math.min(w, h) / 2f * 0.8f; // 留 20% 边距
float left = w / 2f - mRadius;
float top = h / 2f - mRadius;
float right = w / 2f + mRadius;
float bottom = h / 2f + mRadius;
mRectF = new RectF(left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mData.isEmpty()) return;
float startAngle = -90; // 从顶部(12点钟方向)开始绘制
for (PieEntry entry : mData) {
// 计算当前扇形的扫过角度
float sweepAngle = (entry.value / mTotal) * 360f;
// 设置颜色并绘制扇形
mPaint.setColor(entry.color);
canvas.drawArc(mRectF, startAngle, sweepAngle, true, mPaint);
// 绘制扇形内的百分比文字
drawPercentageText(canvas, entry, startAngle, sweepAngle);
// 更新起始角度
startAngle += sweepAngle;
}
}
// 在扇形中间绘制百分比
private void drawPercentageText(Canvas canvas, PieEntry entry, float startAngle, float sweepAngle) {
// 计算扇形角平分线的角度
float midAngle = startAngle + sweepAngle / 2f;
double radian = Math.toRadians(midAngle);
// 计算文字位置(半径的一半处)
float textRadius = mRadius * 0.5f;
float x = getWidth() / 2f + (float) (textRadius * Math.cos(radian));
float y = getHeight() / 2f + (float) (textRadius * Math.sin(radian));
// 绘制百分比
String text = String.format("%.1f%%", entry.value / mTotal * 100);
canvas.drawText(text, x, y, mTextPaint);
}
// 数据实体类
private static class PieEntry {
String label;
float value;
int color;
PieEntry(String label, float value, int color) {
this.label = label;
this.value = value;
this.color = color;
}
}
}2. 在布局文件中使用
<com.example.yourpackage.CustomPieChart
android:id="@+id/pieChart"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_gravity="center" />
评论