You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apache POI生成多XSSF环形图的两个问题求助

Apache POI 环形图(DoughnutChart)问题解决

问题说明

使用Apache POI在Excel中生成多个环形图,已通过XSSFClientAnchor实现并排摆放,但存在两个问题:

  • 环形图切片未显示数值标签;
  • 每个图表对应独立边框,需将并排图表合并为单一边框,且无法在单个Anchor中创建多图表。

原始实现代码

public String picChart7() throws IOException {

    try (XSSFWorkbook wb = new XSSFWorkbook()) {
        XSSFSheet sheet = wb.createSheet("doughnutChart");
        sheet.setDisplayGridlines(false);
        final int NUM_OF_ROWS = 2;
        final int NUM_OF_COLUMNS = 10;

        XSSFDrawing drawing = sheet.createDrawingPatriarch();
        XSSFClientAnchor anchor1 = drawing.createAnchor(0, 0, 0, 0, 1, 5, 4, 28);
        anchor1.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
        anchor1.getTo().setCol(4);
        XSSFClientAnchor anchor2 = drawing.createAnchor(0, 0, 0, 0, 4, 5, 7, 28);
        anchor2.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);

        XSSFChart chart1 = drawing.createChart(anchor1);
        chart1.setTitleText("chart1");
        chart1.setTitleOverlay(false);

        XSSFChart chart2 = drawing.createChart(anchor2);
        chart2.setTitleText("chart2");
        chart2.setTitleOverlay(false);
        
        XDDFChartLegend legend1 = chart1.getOrAddLegend();
        legend1.setPosition(LegendPosition.BOTTOM);

        XDDFChartLegend legend2 = chart2.getOrAddLegend();
        legend2.setPosition(LegendPosition.BOTTOM);
        
        String[] stringArray1 = new String[]{"one","two","three","four","five","six","seven","eight","nine","ten"};
        XDDFDataSource<String> stringValue1 = XDDFDataSourcesFactory.fromArray(stringArray1);
        Long[] longArray1 = new Long[]{ 11L,2L,3L,4L,5L,6L,7L,8L,9L,10L };
        XDDFNumericalDataSource<Long> longValue1 = XDDFDataSourcesFactory.fromArray(longArray1);

        String[] stringArray2 = new String[]{"one","two"};
        XDDFDataSource<String> stringValue2 = XDDFDataSourcesFactory.fromArray(stringArray2);
        Long[] longArray2 = new Long[]{ 60L,40L };
        XDDFNumericalDataSource<Long> longValue2 = XDDFDataSourcesFactory.fromArray(longArray2);
        
        XDDFDoughnutChartData data1 = new XDDFDoughnutChartData(chart1, chart1.getCTChart().getPlotArea().addNewDoughnutChart());
        data1.setVaryColors(true);
        data1.setHoleSize((short) 50);
        data1.setFirstSliceAngle(10);

        XDDFDoughnutChartData data2 = new XDDFDoughnutChartData(chart2, chart2.getCTChart().getPlotArea().addNewDoughnutChart());
        data2.setVaryColors(true);
        data2.setHoleSize((short) 50);
        data2.setFirstSliceAngle(10);
        
        XDDFChartData.Series series1 = data1.addSeries(stringValue1, longValue1);
        XDDFChartData.Series series2 = data2.addSeries(stringValue2, longValue2);

        // 设置数据点颜色
        int pointCount1 = series1.getCategoryData().getPointCount();
        for (int p = 0; p < pointCount1; p++) {
            chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).addNewDPt().addNewIdx().setVal(p);
            chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDPtArray(p)
                    .addNewSpPr().addNewSolidFill().addNewSrgbClr().setVal(getColor(p));
        }
        int pointCount2 = series2.getCategoryData().getPointCount();
        for (int p = 0; p < pointCount2; p++) {
            chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).addNewDPt().addNewIdx().setVal(p);
            chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDPtArray(p)
                    .addNewSpPr().addNewSolidFill().addNewSrgbClr().setVal(getColor(p));
        }
        
        chart1.plot(data1);
        chart2.plot(data2);

        // 写入文件
        try (FileOutputStream fileOut = new FileOutputStream("picChart7.xlsx")) {
            wb.write(fileOut);
        }
    }
    return null;
}

问题解决

1. 显示环形图数值标签

通过操作CTSer的DLbls属性,给每个数据系列开启数值显示:

在chart1.plot(data1);和chart2.plot(data2);之前,添加以下代码:

// 给chart1添加数值标签
CTSer ctSer1 = chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0);
CTDLbls ctdLbls1 = ctSer1.addNewDLbls();
ctdLbls1.setShowVal(true); // 显示数值
ctdLbls1.setShowSerName(false); // 隐藏系列名
ctdLbls1.setShowCatName(false); // 隐藏分类名

// 给chart2添加数值标签
CTSer ctSer2 = chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0);
CTDLbls ctdLbls2 = ctSer2.addNewDLbls();
ctdLbls2.setShowVal(true);
ctdLbls2.setShowSerName(false);
ctdLbls2.setShowCatName(false);

2. 合并图表边框为单一框

由于每个图表对应独立的绘图对象,无法直接合并边框,提供两种可行方案:

方案一:隐藏图表边框,添加外层统一边框

隐藏两个图表的原有边框,然后创建一个覆盖整个区域的矩形形状作为统一边框:

// 隐藏chart1和chart2的边框
chart1.getCTChartSpace().addNewSpPr().addNewLn().addNewNoFill();
chart2.getCTChartSpace().addNewSpPr().addNewLn().addNewNoFill();

// 创建覆盖两个图表的矩形边框
XSSFClientAnchor borderAnchor = drawing.createAnchor(0, 0, 0, 0, 1, 5, 7, 28);
XSSFSimpleShape borderShape = drawing.createSimpleShape(borderAnchor);
borderShape.setShapeType(ShapeTypes.RECTANGLE);
// 设置边框样式:黑色实线,宽度1pt
borderShape.setLineStyle(LineStyle.SOLID);
borderShape.setLineWidth(1.0);
borderShape.setLineColor(new XSSFColor(new byte[]{0,0,0}, null));
// 取消填充,避免遮挡图表
borderShape.setNoFill(true);

方案二:将多个环形图放在同一个图表中

在单个XSSFChart中添加多个环形图,通过手动布局调整位置实现并排,天然共享一个图表边框:

// 创建单个图表和覆盖整个区域的Anchor
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 1, 5, 7, 28);
XSSFChart chart = drawing.createChart(anchor);
chart.setTitleText("Combined Charts");
chart.setTitleOverlay(false);

// 添加第一个环形图,布局在左侧
CTDoughnutChart ctDoughnut1 = chart.getCTChart().getPlotArea().addNewDoughnutChart();
XDDFDoughnutChartData data1 = new XDDFDoughnutChartData(chart, ctDoughnut1);
data1.setVaryColors(true);
data1.setHoleSize((short) 50);
data1.setFirstSliceAngle(10);
// 设置左侧环形图的布局位置和大小
CTLayout ctLayout1 = ctDoughnut1.addNewPlotArea().addNewLayout().addNewManualLayout();
ctLayout1.addNewX().setVal(0.0);
ctLayout1.addNewY().setVal(0.0);
ctLayout1.addNewW().setVal(0.45); // 宽度占45%
ctLayout1.addNewH().setVal(1.0);

// 添加第二个环形图,布局在右侧
CTDoughnutChart ctDoughnut2 = chart.getCTChart().getPlotArea().addNewDoughnutChart();
XDDFDoughnutChartData data2 = new XDDFDoughnutChartData(chart, ctDoughnut2);
data2.setVaryColors(true);
data2.setHoleSize((short) 50);
data2.setFirstSliceAngle(10);
// 设置右侧环形图的布局位置和大小
CTLayout ctLayout2 = ctDoughnut2.addNewPlotArea().addNewLayout().addNewManualLayout();
ctLayout2.addNewX().setVal(0.55); // 从55%位置开始
ctLayout2.addNewY().setVal(0.0);
ctLayout2.addNewW().setVal(0.45);
ctLayout2.addNewH().setVal(1.0);

// 添加系列、设置颜色、数据标签等操作和之前逻辑一致
XDDFChartData.Series series1 = data1.addSeries(stringValue1, longValue1);
XDDFChartData.Series series2 = data2.addSeries(stringValue2, longValue2);

// 设置数据点颜色...(省略原有颜色设置代码)

// 添加数值标签...(省略原有标签设置代码)

chart.plot(data1);
chart.plot(data2);

内容的提问来源于stack exchange,提问作者raymax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 17:37:05