Java - JFreeChart和POI生成Excel图表的图片

需求:生成Excel时支持图表,包括折线图、柱状图、条形图、饼状图、散点图、面积图。

要求:1、开源免费;2、支持跨平台

设计:

1. 使用图片代替Excel中的图表,采用JFreeChart渲染生成图表图片。

2. 采用POI操作Excel。

以下是向Excel2003文件生成折线图的示例(先用JFreeChart生成图片,再用POI将图片加入Excel文件):

效果图:

public static void test() throws Exception {
    // 第一步,使用JFreeChart生成折线图的图片
    // 图表标题、图表x轴名称、图表y轴名称
    String title = "水果时间段销量";
    String categoryAxisLabel = "时间";
    String valueAxisLabel = "销量";
    String[] titles = new String[] { title, categoryAxisLabel, valueAxisLabel };
    String[] rowKeys = { "苹果", "橘子", "香蕉" };
    String[] colKeys = { "10:00", "11:00", "12:00" };
    double[][] data = { { 120, 200, 150 }, { 230, 200, 235 }, { 100, 200, 300 } };
    byte[] bytes = LineChartImg.getBytes(titles, 400, 200, rowKeys, colKeys, data);

    // 第二步,使用POI将图片放入excel文件
    String excel = "F:/Users/zyj/Desktop/2019-09-27 自由报表/生成的报表.xls";
    int[] anchors = new int[] { 0, 0, 0, 0, 2, 1, 12, 15 };
    XlsHelper.addPicture(excel, 0, anchors, bytes);
}

部分maven:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.0</version>
    </dependency>
<dependency>
    <groupId>org.jfree</groupId>
    <artifactId>jfreechart</artifactId>
    <version>1.5.0</version>
</dependency>

使用JFreeChart生成折线图:

import java.awt.Color;
import java.awt.Font;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.jfree.chart.ChartColor;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.title.LegendTitle;
import org.jfree.chart.title.TextTitle;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.general.DatasetUtilities;
import org.jfree.ui.RectangleEdge;

/**
 * JFreeChart折线图,统一设置折线图的样式
 * 
 * @author zyj
 * @date 2019-10-14
 */
public class LineChartImg {
	/**
	 * JFreeChart生成折线图(PNG)
	 * 
	 * @param titles  折线图的三个标题,0是标题,1是x轴标题,2是y轴标题
	 * @param width   折线图的宽度
	 * @param height  折线图的高度
	 * @param rowKeys y轴数据类别
	 * @param colKeys x轴的分类
	 * @param data 数据值
	 * @return 图片的二进制流
	 * @throws IOException
	 */
	public static byte[] getBytes(String[] titles, int width, int height, String[] rowKeys, String[] colKeys, double[][] data) throws IOException {
		CategoryDataset dataset = DatasetUtilities.createCategoryDataset(rowKeys, colKeys, data);
		// 或者使用下面的方法生成CategoryDataset
//		DefaultCategoryDataset dataset = new DefaultCategoryDataset();
//		for (int i = 0; i < rowKeys.length; i++) {
//			for (int j = 0; j < colKeys.length; j++) {
//				dataset.addValue(data[i][j], rowKeys[i], colKeys[j]);
//			}
//		}

		JFreeChart chart = ChartFactory.createLineChart(titles[0], titles[1], titles[2], dataset, PlotOrientation.VERTICAL, true, true, true);
		// 图表样式设置
		setStyle(chart);
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		ChartUtilities.writeChartAsPNG(os, chart, width, height);
		return os.toByteArray();
	}

	/**
	 * JFreeChart折线图样式的统一设置
	 * 
	 * @param chart JFreeChart
	 */
	private static void setStyle(JFreeChart chart) {
		// 设置图片中的字体和颜色以及字号
		Font titleFont = new Font("黑体", Font.BOLD, 12);
		Font xfont = new Font("黑体", Font.BOLD, 10);
		Font labelFont = new Font("黑体", Font.BOLD, 10);
		// 设置图例字体
		chart.getLegend().setItemFont(new Font("黑体", Font.BOLD, 10));
		// 设置标题字体
		chart.setTitle(new TextTitle(chart.getTitle().getText(), titleFont));
		// 图形的绘制结构对象
		CategoryPlot plot = chart.getCategoryPlot();
		// 获取显示线条的对象
		LineAndShapeRenderer lasp = (LineAndShapeRenderer) plot.getRenderer();
		// 设置拐点是否可见/是否显示拐点
		lasp.setBaseShapesVisible(true);
		// 设置拐点不同用不同的形状
		lasp.setDrawOutlines(true);
		// 设置线条是否被显示填充颜色
		lasp.setUseFillPaint(false);
		// 设置折点的大小
//		lasp.setSeriesOutlineStroke(0, new BasicStroke(0.025F));
//		lasp.setSeriesOutlineStroke(1, new BasicStroke(0.05F));
		// 设置折线大小以及折线的颜色
//		LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
//		renderer.setSeriesStroke(0, new BasicStroke(1.0F));
//		renderer.setSeriesPaint(0, new Color(210, 105, 30));
//		renderer.setSeriesStroke(1, new BasicStroke(1.0F));
//		renderer.setSeriesPaint(1, new Color(0, 191, 255));

		// 设置网格线
		plot.setDomainGridlinePaint(Color.gray);
		plot.setDomainGridlinesVisible(true);
		plot.setRangeGridlinePaint(Color.gray);
		plot.setRangeGridlinesVisible(true);
		// x轴
		CategoryAxis domainAxis = plot.getDomainAxis();
		// 设置x轴不显示,即让x轴和数据区重合
		domainAxis.setAxisLineVisible(false);
		// x轴标题
		domainAxis.setLabelFont(xfont);
		// x轴数据倾斜
		domainAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(0.95D));
		// X轴坐标上数值字体
		domainAxis.setTickLabelFont(labelFont);
		// 设置Y轴间隔
		NumberAxis numAxis = (NumberAxis) plot.getRangeAxis();
		numAxis.setTickUnit(new NumberTickUnit(50));
		// y轴
		ValueAxis rangeAxis = plot.getRangeAxis();
		rangeAxis.setLabelFont(xfont);
		// 设置y轴不显示,即和数据区重合
		rangeAxis.setAxisLineVisible(false);
		// y轴坐标上数值字体
		rangeAxis.setTickLabelFont(labelFont);
		rangeAxis.setFixedDimension(0);
		CategoryPlot cp = chart.getCategoryPlot();
		// 背景色设置
		cp.setBackgroundPaint(ChartColor.WHITE);
		cp.setRangeGridlinePaint(ChartColor.GRAY);
		// 创建图例,设置图例的位置,这里的设置实际不起作用,怎么设都在下边
		LegendTitle legendTitle = new LegendTitle(chart.getPlot());
		legendTitle.setPosition(RectangleEdge.BOTTOM);
	}
}

使用POI将图片添加到Excel2003文件:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFPatriarch;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;

/**
 * Excel2003的常用方法
 * 
 * @author zyj
 * @date 2019-10-14
 */
public class XlsHelper {
	/**
	 * POI在Excel中添加图片(PNG)
	 * 
	 * @param excel      excel文件的全路径
	 * @param sheetIndex 图片所在Sheet号(从0开始)
	 * @param anchors    图片位置,八个参数,前四个表示图片离起始单元格和结束单元格边缘的位置,后四个表示起始和结束单元格的位置;示例
	 *                   {0,0,0,0,2,1,12,15}表示从第2列到第12列,从第1行到第15行,单元格内部的边距都是0
	 * @param bytes      图片的二进制流
	 * @throws IOException
	 */
	public static void addPicture(String excel, int sheetIndex, int[] anchors, byte[] bytes) throws IOException {
		File file = new File(excel);
		// Excel目录是否存在,不存在则创建
		if (!file.getParentFile().exists()) {
			file.getParentFile().mkdirs();
		}
		// Excel文件是否存在,不存在则创建空Excel对象
		HSSFWorkbook wb = null;
		if (file.exists()) {
			FileInputStream fins = new FileInputStream(file);
			wb = new HSSFWorkbook(fins);
		} else {
			wb = new HSSFWorkbook();
			wb.createSheet();
			sheetIndex = 0;
		}
		// 添加图片
		addPicture(wb, sheetIndex, anchors, bytes);
		// 保存excel文件
		FileOutputStream fileOut = new FileOutputStream(excel);
		wb.write(fileOut);
		wb.close();
		fileOut.close();
	}

	/**
	 * POI在Workbook中添加图片(PNG)
	 * 
	 * @param wb         Excel工作簿
	 * @param sheetIndex 图片所在Sheet号(从0开始)
	 * @param anchors    图片位置,八个参数,前四个表示图片离起始单元格和结束单元格边缘的位置,后四个表示起始和结束单元格的位置;示例
	 *                   {0,0,0,0,2,1,12,15}表示从第2列到第12列,从第1行到第15行,单元格内部的边距都是0
	 * @param bytes      图片的二进制流
	 */
	public static void addPicture(HSSFWorkbook wb, int sheetIndex, int[] anchors, byte[] bytes) {
		HSSFSheet sheet = wb.getSheetAt(sheetIndex);
		// 画图的顶级管理器,一个sheet只能获取一个(一定要注意这点)
		HSSFPatriarch patriarch = sheet.createDrawingPatriarch();
		// 设置图片位置
		// 八个参数, 前四个表示图片离起始单元格和结束单元格边缘的位置,
		// 后四个表示起始和结束单元格的位置, 如下表示从第2列到第12列, 从第1行到第15行,需要注意excel起始位置是0
		HSSFClientAnchor anchor = new HSSFClientAnchor(anchors[0], anchors[1], anchors[2], anchors[3], (short) anchors[4], anchors[5], (short) anchors[6], anchors[7]);
		anchor.setAnchorType(AnchorType.DONT_MOVE_AND_RESIZE);
		// 插入图片
		int pictureIndex = wb.addPicture(bytes, HSSFWorkbook.PICTURE_TYPE_PNG);
		patriarch.createPicture(anchor, pictureIndex);
	}
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值