maven依赖引入
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
代码示例
/**
* @author alin
* @date 2024-06-03
*/
public class ExportChart {
public static void main(String[] args) throws Exception {
testCreateChart();
}
/**
* 创建折线图
*
* @throws Exception
*/
public static void testCreateChart() throws Exception {
try (XSSFWorkbook wb = new XSSFWorkbook()) {
String sheetName = "温度折线图";
XSSFSheet sheet = wb.createSheet(sheetName);
XSSFDrawing drawing = sheet.createDrawingPatriarch();
// TODO 样式可以根据数据长度自行调整
XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 2, 4, 30, 50);
XSSFChart chart = drawing.createChart(anchor);
// 折线图标题
chart.setTitleText("单据: ****运输过程温度数据 车牌:豫P12345");
// 标题是否覆盖图表
chart.setTitleOverlay(false);
// 设置图表标题字体大小
setChartTitleFontSize(chart, 16);
XDDFChartLegend legend = chart.getOrAddLegend();
// 图例位置:上下左右
legend.setPosition(LegendPosition.TOP_RIGHT);
// 创建x轴
XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
// X轴标题
xAxis.setTitle("采集时间");
// 设置X轴标题字体大小
setAxisTitleFontSize(xAxis, 16);
// 左侧标题, Y轴
XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.TOP);
yAxis.setTitle("温度");
// 刻度
yAxis.setMajorUnit(5);
// 设置Y轴标题字体大小
setAxisTitleFontSize(yAxis, 16);
// 获取mock数据
List<TemperatureModel> list = getResult();
// x轴标题数据
List<String> xTitleData = new ArrayList<>(list.size());
// y轴数据
List<Double> yData1 = new ArrayList<>(list.size());
List<Double> yData2 = new ArrayList<>(list.size());
List<Double> yData3 = new ArrayList<>(list.size());
for (TemperatureModel layer : list) {
xTitleData.add(layer.getGatherTime());
yData1.add(layer.getTemperature() == null ? 0D : layer.getTemperature());
yData2.add(layer.getTemperature2() == null ? 0D : layer.getTemperature2());
yData3.add(layer.getTemperature3() == null ? 0D : layer.getTemperature3());
}
// 数据源
XDDFDataSource<String> date = XDDFDataSourcesFactory.fromArray(xTitleData.toArray(new String[0]));
XDDFNumericalDataSource<Double> one = XDDFDataSourcesFactory.fromArray(yData1.toArray(new Double[0]));
XDDFNumericalDataSource<Double> two = XDDFDataSourcesFactory.fromArray(yData2.toArray(new Double[0]));
XDDFNumericalDataSource<Double> three = XDDFDataSourcesFactory.fromArray(yData3.toArray(new Double[0]));
// 创建折线图
XDDFLineChartData data = (XDDFLineChartData) chart.createData(ChartTypes.LINE, xAxis, yAxis);
// 折线1
XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) data.addSeries(date, one);
// 折线标题
series1.setTitle("温度1(℃)", null);
// 折线是否平滑
series1.setSmooth(true);
// 折线节点样式
series1.setMarkerStyle(MarkerStyle.CIRCLE);
// 折线2
XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) data.addSeries(date, two);
series2.setTitle("温度2(℃)", null);
series2.setSmooth(true);
series2.setMarkerStyle(MarkerStyle.CIRCLE);
chart.plot(data);
// 折线3
XDDFLineChartData.Series series3 = (XDDFLineChartData.Series) data.addSeries(date, three);
series3.setTitle("温度3(℃)", null);
series3.setSmooth(true);
series3.setMarkerStyle(MarkerStyle.CIRCLE);
chart.plot(data);
// 输出文件到指定路径
try (FileOutputStream fileOut = new FileOutputStream("d:/豫P12345运输温度折线图.xlsx")) {
wb.write(fileOut);
wb.close();
}
}
}
/**
* 模拟数据
*
* @return
*/
public static List<TemperatureModel> getResult() {
List<TemperatureModel> list = new ArrayList<>();
for (int i = 0; i < 30; i++) {
ExportChart.TemperatureModel temperatureModel = new ExportChart.TemperatureModel();
temperatureModel.setGatherTime("2024-06-03 10:" + String.format("%02d", i * 2) + ":00");
temperatureModel.setTemperature(i % 2 == 0 ? -RandomUtils.nextDouble(0, 30) : RandomUtils.nextDouble(0, 30));
temperatureModel.setTemperature2(RandomUtils.nextDouble(0, 30));
temperatureModel.setTemperature3(i % 2 == 0 ? -RandomUtils.nextDouble(0, 30) : RandomUtils.nextDouble(0, 30));
list.add(temperatureModel);
}
return list;
}
/**
* 设置图表标题字体大小
* 无法直接设置图表标题字体大小, 可以使用底层的XML对象来实现
*
* @param chart
* @param fontSize
*/
private static void setChartTitleFontSize(XSSFChart chart, double fontSize) {
CTChart ctChart = chart.getCTChart();
if (ctChart.isSetTitle()) {
CTTitle title = ctChart.getTitle();
if (title.isSetTx()) {
CTTextBody rich = title.getTx().getRich();
CTTextParagraph para = rich.getPArray(0);
CTTextCharacterProperties rPr = para.getRArray(0).getRPr();
// 字号大小 fontSize * 100 = fontSize pt
rPr.setSz((int) (fontSize * 100));
//rPr.setB(true); // 加粗
//rPr.setI(true); // 斜体
}
}
}
/**
* 设置X轴标题字体大小
* 无法直接设置X轴标题字体大小, 可以通过反射获取到CTCatAx对象, 然后设置字体大小
*
* @param axis
* @param fontSize
*/
private static void setAxisTitleFontSize(XDDFCategoryAxis axis, double fontSize) {
try {
Field ctCatAx1 = XDDFCategoryAxis.class.getDeclaredField("ctCatAx");
ctCatAx1.setAccessible(true);
CTCatAx ctCatAx = (CTCatAx) ctCatAx1.get(axis);
if (ctCatAx.isSetTitle()) {
CTTitle title = ctCatAx.getTitle();
if (title.isSetTx()) {
CTTextBody rich = title.getTx().getRich();
if (rich != null && rich.sizeOfPArray() > 0) {
CTTextParagraph para = rich.getPArray(0);
if (para.sizeOfRArray() > 0) {
CTTextCharacterProperties rPr = para.getRArray(0).getRPr();
if (rPr == null) {
rPr = para.getRArray(0).addNewRPr();
}
rPr.setSz((int) (fontSize * 100));
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置Y轴标题字体大小
* 无法直接设置Y轴标题字体大小, 可以通过反射获取到CTValAx对象, 然后设置字体大小
*
* @param axis
* @param fontSize
*/
private static void setAxisTitleFontSize(XDDFValueAxis axis, double fontSize) {
try {
Field ctValAx1 = XDDFValueAxis.class.getDeclaredField("ctValAx");
ctValAx1.setAccessible(true);
CTValAx ctValAx = (CTValAx) ctValAx1.get(axis);
if (ctValAx.isSetTitle()) {
CTTitle title = ctValAx.getTitle();
if (title.isSetTx()) {
CTTextBody rich = title.getTx().getRich();
if (rich != null && rich.sizeOfPArray() > 0) {
CTTextParagraph para = rich.getPArray(0);
if (para.sizeOfRArray() > 0) {
CTTextCharacterProperties rPr = para.getRArray(0).getRPr();
if (rPr == null) {
rPr = para.getRArray(0).addNewRPr();
}
rPr.setSz((int) (fontSize * 100));
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Data
public static class TemperatureModel {
/**
* 采集时间
*/
private String gatherTime;
/**
* 温度1
*/
private Double temperature;
/**
* 温度2
*/
private Double temperature2;
/**
* 温度3
*/
private Double temperature3;
}
}