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
相关产品推荐
相关产品推荐

