如何在Swing自定义绘制的饼图各区块中心显示对应数据项
实现方案
完全可以实现,核心是计算每个扇形的中心坐标后在对应位置绘制文本即可,具体实现逻辑和修改后代码如下:
核心计算逻辑
- 先确定饼图基础参数:你当前代码中饼图绘制区域左上角为(50,20)、宽高均为200px,因此圆心坐标为(150,120),半径为100px,文本放在半径一半的位置即可保证在扇形内部。
- 每个扇形的中心角度 = 扇形起始角度 + 扇形扫过角度/2,需转换为弧度后调用三角函数计算坐标。
- Swing的Y轴向下为正,和数学坐标系相反,坐标转换公式为:
- X坐标 = 圆心X + 文本距离圆心长度 * cos(中心角度弧度)
- Y坐标 = 圆心Y - 文本距离圆心长度 * sin(中心角度弧度)
- 绘制时用FontMetrics计算文本宽高,对坐标做微调即可让文本完全居中。
修改后完整代码
public class DrawingPiePanel extends JPanel { private static final long serialVersionUID = 1L; Admin ad = Login.ad; String month = ad.year1 + "-01"; kiosk_dao dao = new kiosk_dao(); int Kor = dao.SelectSaleMonthRestaurant(month, "한식"); int Ch = dao.SelectSaleMonthRestaurant(month, "중식"); int Jp = dao.SelectSaleMonthRestaurant(month, "일식"); int We = dao.SelectSaleMonthRestaurant(month, "양식"); // 优化:Swing自定义绘制建议重写paintComponent而非paint方法 @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.clearRect(0, 0, getWidth(), getHeight()); int Total = Kor + Ch + Jp + We; if (Total != 0) { int arc1 = (int) (360.0 * Kor / Total); int arc2 = (int) (360.0 * Ch / Total); int arc3 = (int) (360.0 * Jp / Total); int arc4 = 360 - (arc1 + arc2 + arc3); double KorPer = (double) Kor / (double) Total * 100; double ChPer = (double) Ch / (double) Total * 100; double JpPer = (double) Jp / (double) Total * 100; double WePer = (double) We / (double) Total * 100; // 绘制扇形 g.setColor(Color.YELLOW); g.fillArc(50, 20, 200, 200, 0, arc1); g.setColor(Color.RED); g.fillArc(50, 20, 200, 200, arc1, arc2); g.setColor(Color.BLUE); g.fillArc(50, 20, 200, 200, arc1 + arc2, arc3); g.setColor(Color.GREEN); g.fillArc(50, 20, 200, 200, arc1 + arc2 + arc3, arc4); // 新增:绘制扇形内部文本 g.setColor(Color.BLACK); g.setFont(new Font("굴림체", Font.PLAIN, 12)); int centerX = 50 + 200 / 2; int centerY = 20 + 200 / 2; int labelRadius = 50; // 文本距离圆心的长度,可自行调整 FontMetrics fm = g.getFontMetrics(); // 韩餐文本 double midAngle = Math.toRadians(0 + arc1 / 2.0); int textX = (int) (centerX + labelRadius * Math.cos(midAngle)); int textY = (int) (centerY - labelRadius * Math.sin(midAngle)); String korText = String.format("%.1f%%", KorPer); textX -= fm.stringWidth(korText) / 2; textY += fm.getAscent() / 2; g.drawString(korText, textX, textY); // 中餐文本 midAngle = Math.toRadians(arc1 + arc2 / 2.0); textX = (int) (centerX + labelRadius * Math.cos(midAngle)); textY = (int) (centerY - labelRadius * Math.sin(midAngle)); String chText = String.format("%.1f%%", ChPer); textX -= fm.stringWidth(chText) / 2; textY += fm.getAscent() / 2; g.drawString(chText, textX, textY); // 日料文本 midAngle = Math.toRadians(arc1 + arc2 + arc3 / 2.0); textX = (int) (centerX + labelRadius * Math.cos(midAngle)); textY = (int) (centerY - labelRadius * Math.sin(midAngle)); String jpText = String.format("%.1f%%", JpPer); textX -= fm.stringWidth(jpText) / 2; textY += fm.getAscent() / 2; g.drawString(jpText, textX, textY); // 西餐文本 midAngle = Math.toRadians(arc1 + arc2 + arc3 + arc4 / 2.0); textX = (int) (centerX + labelRadius * Math.cos(midAngle)); textY = (int) (centerY - labelRadius * Math.sin(midAngle)); String weText = String.format("%.1f%%", WePer); textX -= fm.stringWidth(weText) / 2; textY += fm.getAscent() / 2; g.drawString(weText, textX, textY); // 原右侧图例 g.drawString(" 한식: 노랑" + String.format("%.2f", KorPer) + "%", 300, 150); g.drawString(" 중식: 빨강" + String.format("%.2f", ChPer) + "%", 300, 170); g.drawString(" 일식: 파랑" + String.format("%.2f", JpPer) + "%", 300, 190); g.drawString(" 양식: 초록" + String.format("%.2f", WePer) + "%", 300, 210); g.drawString(" 총매출액: " + Total + " 원", 300, 230); } } }
可调参数说明
labelRadius:控制文本距离圆心的距离,数值越大文本越靠近扇形外侧,可根据实际显示效果调整。- 绘制的文本内容可以自行修改,如需显示分类名或者销售额直接替换字符串即可。
- 如果扇形占比过小导致文本重叠,可以判断占比低于阈值时不绘制内部文本,只保留右侧图例即可。
内容的提问来源于stack exchange,提问作者이윤혁
相关产品推荐
相关产品推荐

