是否可以使用 Apache POI 创建源数据作为 Pivot Table 的 Pivot Chart?
Is it possible to create Pivot Chart with source data as Pivot Table using Apache POI?
我可以单独使用 apache POI 创建数据透视图 Table 和数据透视图。但我正在尝试创建柱形图形式 pivot table 而不是直接从 sheet 数据。我试过在这里寻找指导,但找不到任何指导。如果可能的话,你能指导我正确的方向吗?谢谢。
更新
下面是我之前的示例代码,它在一个 sheet 中填充数据,然后使用第一个 sheet 中填充的数据在另一个 sheet 中创建数据透视表 table 和数据透视图=22=].
但我希望将数据透视表 table 作为数据透视图的来源,而不是来自其他 sheet 的原始数据。这样我就可以动态隐藏某些记录。例如,如果我需要在图表中只显示亚洲国家,我可以在 pivot table 中过滤它们,这将只在图表中显示那些国家。我不确定如何才能做到这一点。
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.DataConsolidateFunction;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class ColumnChart {
public static void main(String[] args) {
String workbookName = "Population.xlsx";
String dataSheetName = "DataSheet";
String pivotSheetName = "PivotSheet";
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream(workbookName)) {
prepareDataSheet(workbook, dataSheetName);
createPivotTable(workbook, pivotSheetName, dataSheetName);
createPivotChart(workbook, pivotSheetName, dataSheetName);
workbook.write(fos);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void prepareDataSheet(XSSFWorkbook workbook, String dataSheetName) {
List<String> continentList = Arrays.asList("Asia", "Asia", "America", "Asia", "Asia", "America", "Africa", "Asia");
List<String> countryList = Arrays.asList("China", "India", "United States", "Indonesia", "Pakistan", "Brazil", "Nigeria", "Bangladesh");
List<Long> populationList = Arrays.asList(1411778724L, 1386020955L, 332943364L, 271350000L, 225200000L, 214130142L, 211401000L, 171933178L);
XSSFSheet dataSheet = workbook.createSheet(dataSheetName);
Row row = dataSheet.createRow(0);
row.createCell(0).setCellValue("Continent");
row.createCell(1).setCellValue("Country");
row.createCell(2).setCellValue("Population");
int rowCounter = 1;
for (rowCounter = 1; rowCounter <= countryList.size(); rowCounter++) {
row = dataSheet.createRow(rowCounter);
row.createCell(0).setCellValue(continentList.get(rowCounter - 1));
row.createCell(1).setCellValue(countryList.get(rowCounter - 1));
row.createCell(2).setCellValue(populationList.get(rowCounter - 1));
}
}
private static void createPivotTable(XSSFWorkbook workbook, String pivotSheetName, String dataSheetName) {
XSSFSheet pivotSheet = workbook.createSheet(pivotSheetName);
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
CellReference leftTop = new CellReference(0, 0);
CellReference rightBottom = new CellReference(8, 2);
CellReference pivotLocation = new CellReference(1, 1);
AreaReference sourceDataAreaRef = new AreaReference(leftTop, rightBottom, SpreadsheetVersion.EXCEL2007);
XSSFPivotTable pivotTable = pivotSheet.createPivotTable(sourceDataAreaRef, pivotLocation, dataSheet);
pivotTable.addRowLabel(0);
pivotTable.addRowLabel(1);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 2);
}
private static void createPivotChart(XSSFWorkbook workbook, String pivotSheetName, String dataSheetName) {
XSSFSheet pivotSheet = workbook.getSheet(pivotSheetName);
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
XSSFDrawing drawing = pivotSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 4, 2, 10, 20);
XSSFChart chart = drawing.createChart(anchor);
chart.setTitleText("Population By Countries");
chart.setTitleOverlay(false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Country");
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Population");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(dataSheet,
new CellRangeAddress(0, 8, 1, 1));
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet,
new CellRangeAddress(0, 8, 2, 2));
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFChartData.Series series1 = data.addSeries(countries, values);
series1.setTitle("Country", null);
data.setVaryColors(false);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
CTSolidColorFillProperties fillProp = CTSolidColorFillProperties.Factory.newInstance();
CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();
rgb.setVal(new byte[]{(byte) 233, (byte) 87, (byte)162});
fillProp.setSrgbClr(rgb);
CTShapeProperties ctShapeProperties = CTShapeProperties.Factory.newInstance();
ctShapeProperties.setSolidFill(fillProp);
chart.getCTChart().getPlotArea().getBarChartList().get(0).getSerList().get(0).setSpPr(ctShapeProperties);
chart.plot(data);
}
}
对于使用 Microsoft Excel 的用法,它就像向图表添加数据透视源一样简单。之后 Excel 从给定的枢轴 table.
呈现图表
在 Office Open XML
(XSSF
) 中,枢轴源由一个 PivotSource
元素给出,该元素将枢轴的限定名称 table 设置为名称。合格的名称是:[workbookName]worksheetName!pivotTableName
.
这是方括号中的工作簿名称,后跟工作表名称,后跟感叹号,后跟枢轴 table 名称。
在代码中应该是这样的:
...
String pivotTableName = pivotTable.getCTPivotTableDefinition().getName();
String qualifiedPivotSourceName = "[" + workbookName + "]" + pivotSheet.getSheetName() + "!" + pivotTableName;
chart.getCTChartSpace().addNewPivotSource().setName(qualifiedPivotSourceName);
...
使用它甚至不需要在图表中设置源数据范围,因为 excel 将它从给定的数据透视表 table 中获取。所以只有虚拟数据必须这样给出:
...
XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromArray(new String[]{"dummy"});
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromArray(new Double[]{1d});
...
但请注意,这种图表只会在 Microsoft Excel 中显示。其他电子表格计算软件将无法正常显示此类图表。所以应该让图表中的源数据作为后备。
并且你需要设置AxisCrossBetween,所以左轴在类别之间穿过类别轴。否则第一个和最后一个类别正好在交叉点上,条形图只有一半可见。
并且您应该将不使用多级标签的默认设置设置为 false。所以各大洲和国家将有多个水平轴标签。
我已经更新了您的完整示例:
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.DataConsolidateFunction;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class ColumnChart {
public static void main(String[] args) {
String pathToSave = "./";
String workbookName = "Population.xlsx";
String dataSheetName = "DataSheet";
String pivotSheetName = "PivotSheet";
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream(pathToSave + workbookName)) {
prepareDataSheet(workbook, dataSheetName);
XSSFPivotTable pivotTable = createPivotTable(workbook, pivotSheetName, dataSheetName);
createPivotChart(workbook, workbookName, pivotTable, dataSheetName);
workbook.getSheetAt(0).setSelected(false);
workbook.setActiveSheet(1);
workbook.write(fos);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void prepareDataSheet(XSSFWorkbook workbook, String dataSheetName) {
List<String> continentList = Arrays.asList("Asia", "Asia", "America", "Asia", "Asia", "America", "Africa", "Asia");
List<String> countryList = Arrays.asList("China", "India", "United States", "Indonesia", "Pakistan", "Brazil", "Nigeria", "Bangladesh");
List<Long> populationList = Arrays.asList(1411778724L, 1386020955L, 332943364L, 271350000L, 225200000L, 214130142L, 211401000L, 171933178L);
XSSFSheet dataSheet = workbook.createSheet(dataSheetName);
Row row = dataSheet.createRow(0);
row.createCell(0).setCellValue("Continent");
row.createCell(1).setCellValue("Country");
row.createCell(2).setCellValue("Population");
int rowCounter = 1;
for (rowCounter = 1; rowCounter <= countryList.size(); rowCounter++) {
row = dataSheet.createRow(rowCounter);
row.createCell(0).setCellValue(continentList.get(rowCounter - 1));
row.createCell(1).setCellValue(countryList.get(rowCounter - 1));
row.createCell(2).setCellValue(populationList.get(rowCounter - 1));
}
}
private static XSSFPivotTable createPivotTable(XSSFWorkbook workbook, String pivotSheetName, String dataSheetName) {
XSSFSheet pivotSheet = workbook.createSheet(pivotSheetName);
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
CellReference leftTop = new CellReference(0, 0);
CellReference rightBottom = new CellReference(8, 2);
CellReference pivotLocation = new CellReference(1, 1);
AreaReference sourceDataAreaRef = new AreaReference(leftTop, rightBottom, SpreadsheetVersion.EXCEL2007);
XSSFPivotTable pivotTable = pivotSheet.createPivotTable(sourceDataAreaRef, pivotLocation, dataSheet);
pivotTable.addRowLabel(0);
pivotTable.addRowLabel(1);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 2);
return pivotTable;
}
private static void createPivotChart(XSSFWorkbook workbook, String workbookName, XSSFPivotTable pivotTable, String dataSheetName) {
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
XSSFSheet pivotSheet = (XSSFSheet)pivotTable.getParentSheet();
XSSFDrawing drawing = pivotSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 4, 2, 10, 20);
XSSFChart chart = drawing.createChart(anchor);
/*Set pivot source of the chart to a qualified name.
Qualified name is [workbookName]worksheetName!pivotTableName. */
String pivotTableName = pivotTable.getCTPivotTableDefinition().getName();
String qualifiedPivotSourceName = "[" + workbookName + "]" + pivotSheet.getSheetName() + "!" + pivotTableName;
chart.getCTChartSpace().addNewPivotSource().setName(qualifiedPivotSourceName);
chart.setTitleText("Population By Countries");
chart.setTitleOverlay(false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Country");
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Population");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
/*You need set AxisCrossBetween, so the left axis crosses the category axis between the categories.
Else first and last category is exactly on cross points and the bars are only half visible.*/
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(dataSheet,
new CellRangeAddress(1, 8, 1, 1));
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet,
new CellRangeAddress(1, 8, 2, 2));
//XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromArray(new String[]{"dummy"});
//XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromArray(new Double[]{1d});
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFChartData.Series series1 = data.addSeries(countries, values);
series1.setTitle("Country", null);
data.setVaryColors(false);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
CTSolidColorFillProperties fillProp = CTSolidColorFillProperties.Factory.newInstance();
CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();
rgb.setVal(new byte[]{(byte) 233, (byte) 87, (byte)162});
fillProp.setSrgbClr(rgb);
CTShapeProperties ctShapeProperties = CTShapeProperties.Factory.newInstance();
ctShapeProperties.setSolidFill(fillProp);
chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(0).setSpPr(ctShapeProperties);
/*Set the default, which is no using multi level labels, to false.
So continents and countries will have multiple level axis labels.*/
chart.getCTChart().getPlotArea().getCatAxArray(0).addNewNoMultiLvlLbl().setVal(false);
chart.plot(data);
}
}
当前 Excel 版本提供了在数据透视图中显示或隐藏字段按钮的设置。默认是不显示按钮。要显示按钮,需要 ext
个具有 c14:pivotOptions
个元素的元素。因此,为了完整起见,如何将这些放入 XML:
...
// show all field buttons in pivot chart
try {
org.openxmlformats.schemas.drawingml.x2006.chart.CTExtensionList ctExtLst = chart.getCTChartSpace().addNewExtLst();
org.openxmlformats.schemas.drawingml.x2006.chart.CTExtension ctExt = ctExtLst.addNewExt();
org.apache.xmlbeans.XmlObject xmlObject = org.apache.xmlbeans.XmlObject.Factory.parse(
"<c14:pivotOptions xmlns:c14=\"http://schemas.microsoft.com/office/drawing/2007/8/2/chart\">"
+"<c14:dropZoneFilter val=\"1\"/>"
+"<c14:dropZoneCategories val=\"1\"/>"
+"<c14:dropZoneData val=\"1\"/>"
+"<c14:dropZoneSeries val=\"1\"/>"
+"<c14:dropZonesVisible val=\"1\"/>"
+"</c14:pivotOptions>"
);
ctExt.set(xmlObject);
ctExt.setUri("{781A3756-C4B2-4CAC-9D66-4F8BD8637D16}");
ctExt = ctExtLst.addNewExt();
xmlObject = org.apache.xmlbeans.XmlObject.Factory.parse(
"<c16:pivotOptions16 xmlns:c16=\"http://schemas.microsoft.com/office/drawing/2014/chart\">"
+"<c16:showExpandCollapseFieldButtons val=\"1\"/>"
+"</c16:pivotOptions16>"
);
ctExt.set(xmlObject);
ctExt.setUri("{E28EC0CA-F0BB-4C9C-879D-F8772B89E7AC}");
} catch (Exception ex) {
ex.printStackTrace();
}
...
我可以单独使用 apache POI 创建数据透视图 Table 和数据透视图。但我正在尝试创建柱形图形式 pivot table 而不是直接从 sheet 数据。我试过在这里寻找指导,但找不到任何指导。如果可能的话,你能指导我正确的方向吗?谢谢。
更新
下面是我之前的示例代码,它在一个 sheet 中填充数据,然后使用第一个 sheet 中填充的数据在另一个 sheet 中创建数据透视表 table 和数据透视图=22=].
但我希望将数据透视表 table 作为数据透视图的来源,而不是来自其他 sheet 的原始数据。这样我就可以动态隐藏某些记录。例如,如果我需要在图表中只显示亚洲国家,我可以在 pivot table 中过滤它们,这将只在图表中显示那些国家。我不确定如何才能做到这一点。
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.DataConsolidateFunction;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class ColumnChart {
public static void main(String[] args) {
String workbookName = "Population.xlsx";
String dataSheetName = "DataSheet";
String pivotSheetName = "PivotSheet";
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream(workbookName)) {
prepareDataSheet(workbook, dataSheetName);
createPivotTable(workbook, pivotSheetName, dataSheetName);
createPivotChart(workbook, pivotSheetName, dataSheetName);
workbook.write(fos);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void prepareDataSheet(XSSFWorkbook workbook, String dataSheetName) {
List<String> continentList = Arrays.asList("Asia", "Asia", "America", "Asia", "Asia", "America", "Africa", "Asia");
List<String> countryList = Arrays.asList("China", "India", "United States", "Indonesia", "Pakistan", "Brazil", "Nigeria", "Bangladesh");
List<Long> populationList = Arrays.asList(1411778724L, 1386020955L, 332943364L, 271350000L, 225200000L, 214130142L, 211401000L, 171933178L);
XSSFSheet dataSheet = workbook.createSheet(dataSheetName);
Row row = dataSheet.createRow(0);
row.createCell(0).setCellValue("Continent");
row.createCell(1).setCellValue("Country");
row.createCell(2).setCellValue("Population");
int rowCounter = 1;
for (rowCounter = 1; rowCounter <= countryList.size(); rowCounter++) {
row = dataSheet.createRow(rowCounter);
row.createCell(0).setCellValue(continentList.get(rowCounter - 1));
row.createCell(1).setCellValue(countryList.get(rowCounter - 1));
row.createCell(2).setCellValue(populationList.get(rowCounter - 1));
}
}
private static void createPivotTable(XSSFWorkbook workbook, String pivotSheetName, String dataSheetName) {
XSSFSheet pivotSheet = workbook.createSheet(pivotSheetName);
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
CellReference leftTop = new CellReference(0, 0);
CellReference rightBottom = new CellReference(8, 2);
CellReference pivotLocation = new CellReference(1, 1);
AreaReference sourceDataAreaRef = new AreaReference(leftTop, rightBottom, SpreadsheetVersion.EXCEL2007);
XSSFPivotTable pivotTable = pivotSheet.createPivotTable(sourceDataAreaRef, pivotLocation, dataSheet);
pivotTable.addRowLabel(0);
pivotTable.addRowLabel(1);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 2);
}
private static void createPivotChart(XSSFWorkbook workbook, String pivotSheetName, String dataSheetName) {
XSSFSheet pivotSheet = workbook.getSheet(pivotSheetName);
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
XSSFDrawing drawing = pivotSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 4, 2, 10, 20);
XSSFChart chart = drawing.createChart(anchor);
chart.setTitleText("Population By Countries");
chart.setTitleOverlay(false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Country");
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Population");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(dataSheet,
new CellRangeAddress(0, 8, 1, 1));
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet,
new CellRangeAddress(0, 8, 2, 2));
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFChartData.Series series1 = data.addSeries(countries, values);
series1.setTitle("Country", null);
data.setVaryColors(false);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
CTSolidColorFillProperties fillProp = CTSolidColorFillProperties.Factory.newInstance();
CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();
rgb.setVal(new byte[]{(byte) 233, (byte) 87, (byte)162});
fillProp.setSrgbClr(rgb);
CTShapeProperties ctShapeProperties = CTShapeProperties.Factory.newInstance();
ctShapeProperties.setSolidFill(fillProp);
chart.getCTChart().getPlotArea().getBarChartList().get(0).getSerList().get(0).setSpPr(ctShapeProperties);
chart.plot(data);
}
}
对于使用 Microsoft Excel 的用法,它就像向图表添加数据透视源一样简单。之后 Excel 从给定的枢轴 table.
呈现图表在 Office Open XML
(XSSF
) 中,枢轴源由一个 PivotSource
元素给出,该元素将枢轴的限定名称 table 设置为名称。合格的名称是:[workbookName]worksheetName!pivotTableName
.
这是方括号中的工作簿名称,后跟工作表名称,后跟感叹号,后跟枢轴 table 名称。
在代码中应该是这样的:
...
String pivotTableName = pivotTable.getCTPivotTableDefinition().getName();
String qualifiedPivotSourceName = "[" + workbookName + "]" + pivotSheet.getSheetName() + "!" + pivotTableName;
chart.getCTChartSpace().addNewPivotSource().setName(qualifiedPivotSourceName);
...
使用它甚至不需要在图表中设置源数据范围,因为 excel 将它从给定的数据透视表 table 中获取。所以只有虚拟数据必须这样给出:
...
XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromArray(new String[]{"dummy"});
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromArray(new Double[]{1d});
...
但请注意,这种图表只会在 Microsoft Excel 中显示。其他电子表格计算软件将无法正常显示此类图表。所以应该让图表中的源数据作为后备。
并且你需要设置AxisCrossBetween,所以左轴在类别之间穿过类别轴。否则第一个和最后一个类别正好在交叉点上,条形图只有一半可见。
并且您应该将不使用多级标签的默认设置设置为 false。所以各大洲和国家将有多个水平轴标签。
我已经更新了您的完整示例:
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.ss.usermodel.DataConsolidateFunction;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.AreaReference;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xddf.usermodel.chart.*;
import org.apache.poi.xssf.usermodel.*;
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;
import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties;
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class ColumnChart {
public static void main(String[] args) {
String pathToSave = "./";
String workbookName = "Population.xlsx";
String dataSheetName = "DataSheet";
String pivotSheetName = "PivotSheet";
try (XSSFWorkbook workbook = new XSSFWorkbook();
FileOutputStream fos = new FileOutputStream(pathToSave + workbookName)) {
prepareDataSheet(workbook, dataSheetName);
XSSFPivotTable pivotTable = createPivotTable(workbook, pivotSheetName, dataSheetName);
createPivotChart(workbook, workbookName, pivotTable, dataSheetName);
workbook.getSheetAt(0).setSelected(false);
workbook.setActiveSheet(1);
workbook.write(fos);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void prepareDataSheet(XSSFWorkbook workbook, String dataSheetName) {
List<String> continentList = Arrays.asList("Asia", "Asia", "America", "Asia", "Asia", "America", "Africa", "Asia");
List<String> countryList = Arrays.asList("China", "India", "United States", "Indonesia", "Pakistan", "Brazil", "Nigeria", "Bangladesh");
List<Long> populationList = Arrays.asList(1411778724L, 1386020955L, 332943364L, 271350000L, 225200000L, 214130142L, 211401000L, 171933178L);
XSSFSheet dataSheet = workbook.createSheet(dataSheetName);
Row row = dataSheet.createRow(0);
row.createCell(0).setCellValue("Continent");
row.createCell(1).setCellValue("Country");
row.createCell(2).setCellValue("Population");
int rowCounter = 1;
for (rowCounter = 1; rowCounter <= countryList.size(); rowCounter++) {
row = dataSheet.createRow(rowCounter);
row.createCell(0).setCellValue(continentList.get(rowCounter - 1));
row.createCell(1).setCellValue(countryList.get(rowCounter - 1));
row.createCell(2).setCellValue(populationList.get(rowCounter - 1));
}
}
private static XSSFPivotTable createPivotTable(XSSFWorkbook workbook, String pivotSheetName, String dataSheetName) {
XSSFSheet pivotSheet = workbook.createSheet(pivotSheetName);
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
CellReference leftTop = new CellReference(0, 0);
CellReference rightBottom = new CellReference(8, 2);
CellReference pivotLocation = new CellReference(1, 1);
AreaReference sourceDataAreaRef = new AreaReference(leftTop, rightBottom, SpreadsheetVersion.EXCEL2007);
XSSFPivotTable pivotTable = pivotSheet.createPivotTable(sourceDataAreaRef, pivotLocation, dataSheet);
pivotTable.addRowLabel(0);
pivotTable.addRowLabel(1);
pivotTable.addColumnLabel(DataConsolidateFunction.SUM, 2);
return pivotTable;
}
private static void createPivotChart(XSSFWorkbook workbook, String workbookName, XSSFPivotTable pivotTable, String dataSheetName) {
XSSFSheet dataSheet = workbook.getSheet(dataSheetName);
XSSFSheet pivotSheet = (XSSFSheet)pivotTable.getParentSheet();
XSSFDrawing drawing = pivotSheet.createDrawingPatriarch();
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 4, 2, 10, 20);
XSSFChart chart = drawing.createChart(anchor);
/*Set pivot source of the chart to a qualified name.
Qualified name is [workbookName]worksheetName!pivotTableName. */
String pivotTableName = pivotTable.getCTPivotTableDefinition().getName();
String qualifiedPivotSourceName = "[" + workbookName + "]" + pivotSheet.getSheetName() + "!" + pivotTableName;
chart.getCTChartSpace().addNewPivotSource().setName(qualifiedPivotSourceName);
chart.setTitleText("Population By Countries");
chart.setTitleOverlay(false);
XDDFChartLegend legend = chart.getOrAddLegend();
legend.setPosition(LegendPosition.BOTTOM);
XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
bottomAxis.setTitle("Country");
XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
leftAxis.setTitle("Population");
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
/*You need set AxisCrossBetween, so the left axis crosses the category axis between the categories.
Else first and last category is exactly on cross points and the bars are only half visible.*/
leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromStringCellRange(dataSheet,
new CellRangeAddress(1, 8, 1, 1));
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(dataSheet,
new CellRangeAddress(1, 8, 2, 2));
//XDDFDataSource<String> countries = XDDFDataSourcesFactory.fromArray(new String[]{"dummy"});
//XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromArray(new Double[]{1d});
XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
XDDFChartData.Series series1 = data.addSeries(countries, values);
series1.setTitle("Country", null);
data.setVaryColors(false);
XDDFBarChartData bar = (XDDFBarChartData) data;
bar.setBarDirection(BarDirection.COL);
CTSolidColorFillProperties fillProp = CTSolidColorFillProperties.Factory.newInstance();
CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();
rgb.setVal(new byte[]{(byte) 233, (byte) 87, (byte)162});
fillProp.setSrgbClr(rgb);
CTShapeProperties ctShapeProperties = CTShapeProperties.Factory.newInstance();
ctShapeProperties.setSolidFill(fillProp);
chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(0).setSpPr(ctShapeProperties);
/*Set the default, which is no using multi level labels, to false.
So continents and countries will have multiple level axis labels.*/
chart.getCTChart().getPlotArea().getCatAxArray(0).addNewNoMultiLvlLbl().setVal(false);
chart.plot(data);
}
}
当前 Excel 版本提供了在数据透视图中显示或隐藏字段按钮的设置。默认是不显示按钮。要显示按钮,需要 ext
个具有 c14:pivotOptions
个元素的元素。因此,为了完整起见,如何将这些放入 XML:
...
// show all field buttons in pivot chart
try {
org.openxmlformats.schemas.drawingml.x2006.chart.CTExtensionList ctExtLst = chart.getCTChartSpace().addNewExtLst();
org.openxmlformats.schemas.drawingml.x2006.chart.CTExtension ctExt = ctExtLst.addNewExt();
org.apache.xmlbeans.XmlObject xmlObject = org.apache.xmlbeans.XmlObject.Factory.parse(
"<c14:pivotOptions xmlns:c14=\"http://schemas.microsoft.com/office/drawing/2007/8/2/chart\">"
+"<c14:dropZoneFilter val=\"1\"/>"
+"<c14:dropZoneCategories val=\"1\"/>"
+"<c14:dropZoneData val=\"1\"/>"
+"<c14:dropZoneSeries val=\"1\"/>"
+"<c14:dropZonesVisible val=\"1\"/>"
+"</c14:pivotOptions>"
);
ctExt.set(xmlObject);
ctExt.setUri("{781A3756-C4B2-4CAC-9D66-4F8BD8637D16}");
ctExt = ctExtLst.addNewExt();
xmlObject = org.apache.xmlbeans.XmlObject.Factory.parse(
"<c16:pivotOptions16 xmlns:c16=\"http://schemas.microsoft.com/office/drawing/2014/chart\">"
+"<c16:showExpandCollapseFieldButtons val=\"1\"/>"
+"</c16:pivotOptions16>"
);
ctExt.set(xmlObject);
ctExt.setUri("{E28EC0CA-F0BB-4C9C-879D-F8772B89E7AC}");
} catch (Exception ex) {
ex.printStackTrace();
}
...