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

Apache POI环形图因标签数量差异垂直位置偏移修复咨询

问题

我使用Apache POI编写代码,在Excel文件中创建两个环形图(Doughnut Chart):第一个环形图包含10个标签,第二个仅包含2个标签。我期望两个图表在锚点中保持垂直对齐,但第二个图表因标签数量过少,其在锚点中的垂直位置发生了偏移。请问如何调整代码,使第二个图表与第一个保持垂直对齐?

原代码

public String picChart9() 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);
        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);
        //chart1.createValueAxis(AxisPosition.TOP);

        XSSFChart chart2 = drawing.createChart(anchor2);
        chart2.setTitleText("chart2");
        chart2.setTitleOverlay(false);
        //chart2.createValueAxis(AxisPosition.LEFT);
        /**/
        System.out.println("chart1.getCTChart() --:"+chart1.getCTChart());
        System.out.println("chart1.getAxes() --:"+chart1.getAxes());
        System.out.println("chart2.getAxes() --:"+chart2.getAxes());
        /**/
        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);

        // Remove the Anchore border line
        (chart1).getCTChartSpace().addNewSpPr().addNewLn().addNewNoFill();
        (chart2).getCTChartSpace().addNewSpPr().addNewLn().addNewNoFill();

        // Data point colors; is necessary for showing data points in Calc

        // Add data labels-Chart-1
        if (!chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).isSetDLbls()) {
            chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).addNewDLbls();
        }
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowVal().setVal(true);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewNumFmt();
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setSourceLinked(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setFormatCode("#,##0.00");

        // Add data labels-Chart-2
        if (!chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).isSetDLbls()) {
            chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).addNewDLbls();
        }
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowVal().setVal(true);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewNumFmt();
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setSourceLinked(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setFormatCode("#,##0.00");

        //plot chart
        chart1.plot(data1);
        chart2.plot(data2);

        // Write the output to a file
        try (FileOutputStream fileOut = new FileOutputStream("picChart9.xlsx")) {
            wb.write(fileOut);
        }
    }
    return null;
}
解决方案

偏移的核心原因是Excel默认会根据图表内容自动调整绘图区的垂直布局,标签少的环形图会因为空白分布差异导致整体位置偏移。要解决这个问题,需要强制统一两个图表的布局参数:

  • 启用锚点的固定模式,禁止图表随内容自动移动和调整大小
  • 手动设置绘图区的位置和尺寸,确保两个图表的绘图区参数完全一致
  • 统一标题和图例的布局规则

修改后的完整代码

public String picChart9() 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);
        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);

        // Remove the Anchore border line
        (chart1).getCTChartSpace().addNewSpPr().addNewLn().addNewNoFill();
        (chart2).getCTChartSpace().addNewSpPr().addNewLn().addNewNoFill();

        // Add data labels-Chart-1
        if (!chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).isSetDLbls()) {
            chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).addNewDLbls();
        }
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowVal().setVal(true);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewNumFmt();
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setSourceLinked(false);
        chart1.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setFormatCode("#,##0.00");

        // Add data labels-Chart-2
        if (!chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).isSetDLbls()) {
            chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).addNewDLbls();
        }
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowVal().setVal(true);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().addNewNumFmt();
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setSourceLinked(false);
        chart2.getCTChart().getPlotArea().getDoughnutChartArray(0).getSerArray(0).getDLbls().getNumFmt().setFormatCode("#,##0.00");

        // 关键:手动设置两个图表的绘图区位置和尺寸,参数完全一致
        // 设置绘图区相对于图表容器的偏移和大小(单位:EMU,1EMU=1/914400英寸)
        chart1.getCTChart().getPlotArea().addNewLayout().addNewManualLayout();
        org.openxmlformats.schemas.drawingml.x2006.chart.CTManualLayout layout1 = chart1.getCTChart().getPlotArea().getLayout().getManualLayout();
        layout1.addNewX().setVal(0.1); // 水平偏移比例
        layout1.addNewY().setVal(0.15); // 垂直偏移比例(统一这个值让两个图表垂直对齐)
        layout1.addNewW().setVal(0.8); // 宽度比例
        layout1.addNewH().setVal(0.7); // 高度比例
        layout1.addNewLayoutTarget().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget.INNER);

        org.openxmlformats.schemas.drawingml.x2006.chart.CTManualLayout layout2 = chart2.getCTChart().getPlotArea().addNewLayout().addNewManualLayout();
        layout2.addNewX().setVal(0.1);
        layout2.addNewY().setVal(0.15); // 和chart1保持相同的垂直偏移
        layout2.addNewW().setVal(0.8);
        layout2.addNewH().setVal(0.7);
        layout2.addNewLayoutTarget().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STLayoutTarget.INNER);

        //plot chart
        chart1
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 23:22:34