说明
动态创建带有多个图表的Excel文件效果图:
一、分析
第一种:创建图表模板,在Excel模板里设置好图表,然后通过更新模板数值,最终刷新图表;(也包含刷新图标各项数据)
第二种:动态创建带有数值与图表的Excel
说明:第一种,有一定的局限性,就是Excel里的数值与图表的格式需要固定,只是对数值进行替换。实际开发过程中,很少有固定的格式,所以重点讲解第二种;
二、全动态导出Excel带有多个图表–》实现步骤
第一步:根据传入的数据集,生成数据表的表头与数据行,并返回创建画布XSSFDrawing
第二步:在画布里创建空图表XSSFChart
第三步:设置图表XSSFChart,比如:创建柱状图
第四步:生成导出Excel文件
1.根据传入的数据集,生成数据表的表头与数据行
创建数据源,就是创建列头,数据行以及样式等;
一个sheet对应一个画布,所有返回一个画布;
代码如下:
private XSSFDrawing createXSSFDrawingBuildDataSource(ExcelMoreDataSourceInput input) {
if (Objects.isNull(wb)) wb = new XSSFWorkbook();
XSSFSheet sheet = wb.createSheet(input.getSheetName());
// 创建数据源
drawTable(sheet, input.getTitleArr(), input.getFldNameArr(), input.getDataList());
// 创建一个画布
return sheet.createDrawingPatriarch();
}
2.在画布里创建空图表XSSFChart
在画布里创建空图表XSSFChart,设置第几行几列为图表(多个图表布局好即可)
代码如下:
/**
* 创建图表
*
* @param isPutOnRight 比如:当列数大于5时,把画布放在数组右边
* @param chartNum 创建第几个图表
*/
private XSSFChart createChart(XSSFDrawing drawing, int cols, int rows, boolean isPutOnRight, int chartNum) {
int colNum = 25;
ClientAnchor anchor;
if (isPutOnRight) {
anchor = drawing.createAnchor(0, 0, 0, 0, 1, rows + 2 + colNum * (chartNum - 1), cols + 15, rows + colNum * chartNums);
} else {
anchor = drawing.createAnchor(0, 0, 0, 0, cols + 1, 4 + colNum * (chartNum - 1), cols + 15, colNum * chartNums);
}
// 创建一个chart对象
return drawing.createChart(anchor);
}
3.设置图表XSSFChart
以制作柱状图为例:
1、确认哪些列作为图例项
2、设置横坐标,这里固定第一列为横坐标
3、设置数据区域范围
具体请看一下代码
代码如下:
/**
* 生成柱状图
*
* @param chart 图表
* @param group 柱状图类型(堆积,多组)
* @param fldNameArrIndex 数据列下标(用于Excel标识数据源区域)
* @param rows 数据行数
* @param sheetName sheet的名称
*/
private void drawBarChartMap(XSSFChart chart, STBarGrouping.Enum group, int[] fldNameArrIndex, int rows, String sheetName) {
CTChart ctChart = chart.getCTChart();
CTPlotArea ctPlotArea = ctChart.getPlotArea();
// 创建柱状图模型
CTBarChart ctBarChart = ctPlotArea.addNewBarChart();
CTBoolean ctBoolean = ctBarChart.addNewVaryColors();
ctBarChart.getVaryColors().setVal(true);
// 设置图类型
ctBarChart.addNewGrouping().setVal(group);
ctBoolean.setVal(true);
ctBarChart.addNewBarDir().setVal(STBarDir.COL);
// 是否添加左侧坐标轴
ctChart.addNewDispBlanksAs().setVal(STDispBlanksAs.ZERO);
ctChart.addNewShowDLblsOverMax().setVal(true);
// 设置这两个参数是为了在STACKED模式下生成堆积模式;(standard)标准模式时需要将这两行去掉
if ("stacked".equals(group.toString()) || "percentStacked".equals(group.toString())) {
ctBarChart.addNewGapWidth().setVal(150);
ctBarChart.addNewOverlap().setVal((byte) 100);
}
// 创建序列,并且设置选中区域
for (int i = 0; i < fldNameArrIndex.length; i++) {
int col = fldNameArrIndex[i];
CTBarSer ctBarSer = ctBarChart.addNewSer();
CTSerTx ctSerTx = ctBarSer.addNewTx();
// 图例区
CTStrRef ctStrRef = ctSerTx.addNewStrRef();
// 选定区域第0行,第1,2,3列标题作为图例 //1 2 3
String legendDataRange = new CellRangeAddress(0, 0, col, col).formatAsString(sheetName, true);
ctStrRef.setF(legendDataRange);
ctBarSer.addNewIdx().setVal(i);
// 横坐标区
CTAxDataSource cttAxDataSource = ctBarSer.addNewCat();
ctStrRef = cttAxDataSource.addNewStrRef();
// 选第0列为横坐标区域
String axisDataRange = new CellRangeAddress(1, rows, 0, 0).formatAsString(sheetName, true);
ctStrRef.setF(axisDataRange);
// 数据区域
CTNumDataSource ctNumDataSource = ctBarSer.addNewVal();
CTNumRef ctNumRef = ctNumDataSource.addNewNumRef();
// 选第1-6行,第1-3列作为数据区域 //1 2 3
String numDataRange = new CellRangeAddress(1, rows, col, col).formatAsString(sheetName,
true);
ctNumRef.setF(numDataRange);
// 添加柱状边框线
ctBarSer.addNewSpPr().addNewLn().addNewSolidFill().addNewSrgbClr().setVal(new byte[]{0, 0, 0});
// 设置负轴颜色不是白色
ctBarSer.addNewInvertIfNegative().setVal(false);
// 设置标签格式
ctBoolean.setVal(false);
CTDLbls newDLbls = ctBarSer.addNewDLbls();
newDLbls.setShowLegendKey(ctBoolean);
ctBoolean.setVal(true);
newDLbls.setShowVal(ctBoolean);
ctBoolean.setVal(false);
newDLbls.setShowCatName(ctBoolean);
newDLbls.setShowSerName(ctBoolean);
newDLbls.setShowPercent(ctBoolean);
newDLbls.setShowBubbleSize(ctBoolean);
newDLbls.setShowLeaderLines(ctBoolean);
}
// 告诉BarChart它有坐标轴,并给它们id
ctBarChart.addNewAxId().setVal(123456);
ctBarChart.addNewAxId().setVal(123457);
// 横坐标
CTCatAx ctCatAx = ctPlotArea.addNewCatAx();
ctCatAx.addNewAxId().setVal(123456); // id of the cat axis
CTScaling ctScaling = ctCatAx.addNewScaling();
ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX);
ctCatAx.addNewAxPos().setVal(STAxPos.B);
ctCatAx.addNewCrossAx().setVal(123457); // id of the val axis
ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);
// 纵坐标
CTValAx ctValAx = ctPlotArea.addNewValAx();
ctValAx.addNewAxId().setVal(123457); // id of the val axis
ctScaling = ctValAx.addNewScaling();
ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX);
// 设置位置
ctValAx.addNewAxPos().setVal(STAxPos.L);
ctValAx.addNewCrossAx().setVal(123456); // id of the cat axis
ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);
// 是否删除主左边轴
ctValAx.addNewDelete().setVal(false);
// 是否删除横坐标
ctCatAx.addNewDelete().setVal(false);
// legend图注
// if(true){
CTLegend ctLegend = ctChart.addNewLegend();
ctLegend.addNewLegendPos().setVal(STLegendPos.B);
ctLegend.addNewOverlay().setVal(false);
// }
}
4.生成导出Excel文件
代码如下:
public void createTpl(String filePath) {
try {
File f = new File(filePath); //写文件
//不存在则新增
if (!f.getParentFile().exists()) {
f.getParentFile().mkdirs();
}
if (!f.exists()) {
f.createNewFile();
}
FileOutputStream out = new FileOutputStream(f);
out.flush();
wb.write(out);
out.close();
} catch (IOException e) {
e.printStackTrace();
}
三、测试用例
/**
* 根据数据源,生成多个图表
*/
public class ExcelChartUtilTest {
@Test
public void excelChartTest() {
// 字段名
List<String> fldNameArr = new ArrayList<>();
fldNameArr.add("value1");
fldNameArr.add("value2");
fldNameArr.add("value3");
fldNameArr.add("value4");
// 列头
List<String> titleArr = new ArrayList<>();
titleArr.add("类型");
titleArr.add("总额");
titleArr.add("数量");
titleArr.add("人均");
// 模拟数据
List<Map<String, Object>> dataList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Map<String, Object> dataMap_ = new HashMap<>();
dataMap_.put("value1", "类型" + i);
dataMap_.put("value2", Math.floor(Math.random() * 100) + "");
dataMap_.put("value3", i);
dataMap_.put("value4", Math.floor(Math.random() * 10));
dataList.add(dataMap_);
}
//开始赋值
ExcelMoreDataSourceInput input = new ExcelMoreDataSourceInput();
input.setTitleArr(titleArr);
input.setFldNameArr(fldNameArr);
input.setDataList(dataList);
// 固定第一列为横坐标区域
List<ChartDataSource> chartDataSources = Lists.newArrayList();
ChartDataSource dataSource = new ChartDataSource();
dataSource.setChartType(ChartType.LINE);
dataSource.setLegendItems(Lists.newArrayList("value2"));
ChartDataSource dataSource1 = new ChartDataSource();
dataSource1.setChartType(ChartType.BAR);
dataSource1.setLegendItems(Lists.newArrayList("value2", "value3", "value4"));
chartDataSources.add(dataSource);
chartDataSources.add(dataSource1);
input.setChartDataSources(chartDataSources);
ExcelChartMoreUtil moreUtil = new ExcelChartMoreUtil(input);
String pathName = "C:/file/chart/chartMore.xlsx";
moreUtil.createTpl(pathName);
}
}
根据模板生成图标(更新数值,从而刷新图标)
模板内容:
执行测试方法后,效果图下:
代码如下:
@Test
public void chart_fixed_row_column_test() throws Exception {
final String fileInput = "C:\\file\\chart\\test\\line-chart-template-test.xlsx";
try (FileInputStream argIS = new FileInputStream(fileInput)) {
try (XSSFWorkbook xssfWorkbook = new XSSFWorkbook(argIS)) {
//获取创建工作簿的第一页
XSSFSheet sheet = xssfWorkbook.getSheetAt(0);
//自动计算
sheet.setForceFormulaRecalculation(true);
//给指定的sheet命名
xssfWorkbook.setSheetName(0, "sheet0");
//初始化当前的索引,设为当前sheet的最后一行行数
int allRows = sheet.getLastRowNum();
//填充数据
for (int i = 1; i <= allRows; i++) {
XSSFRow row = sheet.getRow(i);
if (row == null) {
continue;
}
//遍历列
for (int j = 0; j < 4; j++) {
XSSFCell cell = row.getCell(j) != null ? row.getCell(j) : row.createCell(j);
String cellValue = cell.getStringCellValue();
if (cellValue.startsWith("#")) {
if (j == 0) cell.setCellValue("岗位" + i);
else cell.setCellValue(i * 10 + j);
}
}
// 保存返回
try (OutputStream out = new FileOutputStream("C:\\file\\chart\\test\\line_chart-demo-output.xlsx")) {
xssfWorkbook.write(out);
}
}
}
}
}