项目有个需求是生成上图的表格图表,本来excel很容易生成上边的表格图,但是java poi不支持在服务器端把excel表格导出成图片,在没有找到合适的工具库下,用java 2d实现同样图表。
这个表格图分成标题、表头、表中、表尾4个部分,
背景填充用:graphics.fillRect()
画线条用:graphics.drawLine()
画文字用:graphics.drawString()
主要用上边三个java 2d方法实现,剩下的就是各种坐标位置的计算。
实现代码:
package com.penngo.test;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class ExcelChart {
private Font titleFont = new Font("宋体",Font.BOLD,20);
private Font headFont = new Font("宋体",Font.BOLD,16);
private Font rowFont = new Font("宋体",Font.PLAIN,14);
private Font bottomFont = new Font("宋体",Font.PLAIN,14);
private Color titleBackgroup = new Color(237, 125, 49); // 标题背景色
private Color headBackgroup = new Color(252, 228, 214); // 表头背景色
private Color lineColor = new Color(237, 125,49); // 线条颜色
private int titleMargin = 8;
private int headMargin = 8;
private int rowMargin = 5;
private int bottomMargin = 5;
private int width;
private int height;
private int widthMargin = 10; // x横轴边距
private int heightMargin = 10; // y轴边框
/**
* 将图片保存到指定位置
* @param image 缓冲文件类
* @param fileLocation 文件位置
*/
public void createImage(BufferedImage image, File fileLocation) {
try {
ImageIO.write(image, "png", fileLocation);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 字符串总宽度
* @param g
* @param str
* @return
*/
private int getStringWidth(Graphics g,String str) {
char[] strcha=str.toCharArray();
int strWidth = g.getFontMetrics().charsWidth(strcha, 0, str.length());
return strWidth;
}
//字符高度
private int getStringHeight(Graphics g) {
int height = g.getFontMetrics().getHeight();
return height;
}
private int calculateImageHeight(int rowCount){
BufferedImage image = new BufferedImage(100, 100,BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
graphics.setFont(titleFont);
int titleRowHeight = getStringHeight(graphics) + titleMargin * 2;
graphics.setFont(headFont);
int headRowHeight = getStringHeight(graphics) + headMargin * 2;
graphics.setFont(rowFont);
int rowHeight = getStringHeight(graphics) + rowMargin * 2;
graphics.setFont(bottomFont);
int bottomRowHeight = getStringHeight(graphics) + bottomMargin * 2;
int height = widthMargin * 2 + titleRowHeight + headRowHeight + rowHeight * rowCount + bottomRowHeight;
return height;
}
public void createTableChart(File img, String title, String bottomTitle, List<String> headList, List<List<String>> rows, int width){
widthMargin = 10;
heightMargin = 10;
this.width = width;
this.height = calculateImageHeight(rows.size());
BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
Graphics graphics = image.getGraphics();
((Graphics2D)graphics).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
((Graphics2D)graphics).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setColor(Color.WHITE);
graphics.fillRect(0,0, width, height);
int rowWidth = (width - widthMargin * 2) / headList.size();
// 线条颜色
Color lineColor = new Color(237, 125,49);
// 创建标题背景
int xTitle = widthMargin;
int yTitle = heightMargin;
graphics.setFont(getTitleFont());
int titleHeight = getStringHeight(graphics);
writeTitle(xTitle, yTitle, titleHeight, title, graphics);
// 创建表头
int xHead = widthMargin;
int yHead = yTitle + titleHeight + titleMargin * 2;
graphics.setFont(getHeadFont());
int headHeight = getStringHeight(graphics);
writeHead(xHead, yHead,rowWidth, headHeight, headList,graphics);
// 表格内容
int xRow = widthMargin;
int yRow = yHead + headHeight + headMargin * 2;
graphics.setFont(rowFont);
int rowHeight = getStringHeight(graphics);
writeRow(xRow, yRow, rowWidth,rowHeight,headList, rows, graphics);
// 表格底部
int xBottom = widthMargin;
int yBottom = yRow + (rowHeight + rowMargin * 2) * rows.size();
graphics.setFont(bottomFont);
int bottomHeight = getStringHeight(graphics);
writeBottom(xBottom, yBottom, bottomHeight, bottomTitle, graphics);
// 保存图片
createImage(image, img);
}
private void writeTitle(int xTitle, int yTitle, int titleHeight, String title, Graphics graphics){
int titleWidth = getStringWidth(graphics, title);
graphics.setColor(getTitleBackgroup());
graphics.fillRect(xTitle,yTitle, width - widthMargin * 2 + 1, titleHeight + titleMargin * 2);
// 标题文字
graphics.setColor(Color.WHITE);
graphics.drawString(title, widthMargin + (width - widthMargin * 2)/2 - titleWidth / 2, heightMargin + titleHeight + titleMargin - 3);
}
private void drawString(Graphics graphics, String txt, int x, int y){
Graphics2D g2d = (Graphics2D)graphics;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);//设置抗锯齿
g2d.setPaint(new Color(0, 0, 0, 64));//阴影颜色
g2d.drawString(txt, x, y);//先绘制阴影
g2d.setPaint(Color.BLACK);//正文颜色
g2d.drawString(txt, x, y);//用正文颜色覆盖上去
}
/**
* 写入表头文本
*/
private void writeHead(int xHead, int yHead,int rowWidth, int headHeight, List<String> headList,Graphics graphics){
// 背景填充
graphics.setColor(getHeadBackgroup());
graphics.fillRect(xHead, yHead, width - widthMargin * 2, headHeight + headMargin * 2);
// 表格竖线
graphics.setColor(getLineColor());
for(int i = 0;i < headList.size() + 1; i++){
graphics.drawLine(xHead + i * rowWidth, yHead, xHead + i * rowWidth, yHead + headHeight + headMargin * 2);
}
graphics.setColor(Color.BLACK);
for(int i = 0; i < headList.size(); i++){
String str = headList.get(i);
if(str.length() < 5){
graphics.drawString(str, xHead + i * rowWidth + headMargin, yHead + headHeight + headMargin);
}
else{
String t1 = str.substring(0, 5);
int t1Width = getStringWidth(graphics, t1);
graphics.drawString(t1, xHead + i * rowWidth + headMargin, yHead + headHeight - headMargin/2 + 1);
String t2 = str.substring(5, str.length());
int t2Width = getStringWidth(graphics, t2);
int tdx = (t1Width - t2Width) / 2;
graphics.drawString(t2, xHead + i * rowWidth + headMargin + tdx, yHead + headHeight + headMargin + 3);
}
}
}
/**
* 写入表数据
*/
private void writeRow(int xRow, int yRow, int rowWidth,int rowHeight,List<String> headList, List<List<String>> rows,Graphics graphics){
// 画横线
int rowCount = rows.size() + 1;
graphics.setColor(getLineColor());
for(int i = 0; i< rowCount; i++){
int y = yRow + i * (rowHeight + rowMargin * 2);
graphics.drawLine(xRow, y, width - widthMargin, y);
}
// 画竖线
int colCount = headList.size() + 1;
for(int c = 0; c < colCount; c++){
int x = xRow + c * rowWidth;
int maxY = yRow + rows.size() * (rowHeight + rowMargin * 2);
graphics.drawLine(x, yRow, x, maxY);
}
// 写入行数据
graphics.setColor(Color.BLACK);
for(int r = 0; r < rows.size(); r++){
List<String> row = rows.get(r);
for(int c = 0; c < row.size(); c++){
int x = xRow + rowMargin + c * rowWidth;
int y = yRow + rowHeight + rowMargin+ r * (rowHeight + rowMargin * 2) - 2;
// graphics.drawString(row.get(c), x, y);
drawString(graphics, row.get(c), x, y);
}
}
}
private void writeBottom(int xBottom, int yBottom, int bottomHeight, String bottomTitle, Graphics graphics){
// 画横线
graphics.setColor(getLineColor());
graphics.drawLine(xBottom, yBottom + bottomHeight + bottomMargin * 2, width - widthMargin ,yBottom + bottomHeight + bottomMargin * 2);
// 画竖线
graphics.drawLine(xBottom, yBottom, xBottom ,yBottom + bottomHeight + bottomMargin * 2);
graphics.drawLine(width - widthMargin, yBottom, width - widthMargin ,yBottom + bottomHeight + bottomMargin * 2);
graphics.setColor(Color.BLACK);
int bottomTitleWidth = getStringWidth(graphics, bottomTitle);
int x = width / 2 - bottomTitleWidth / 2;
graphics.drawString(bottomTitle, x, yBottom + bottomHeight + bottomMargin);
}
public Font getTitleFont() {
return titleFont;
}
public void setTitleFont(Font titleFont) {
this.titleFont = titleFont;
}
public Font getHeadFont() {
return headFont;
}
public void setHeadFont(Font headFont) {
this.headFont = headFont;
}
public Font getRowFont() {
return rowFont;
}
public void setRowFont(Font rowFont) {
this.rowFont = rowFont;
}
public Font getBottomFont() {
return bottomFont;
}
public void setBottomFont(Font bottomFont) {
this.bottomFont = bottomFont;
}
public int getTitleMargin() {
return titleMargin;
}
public void setTitleMargin(int titleMargin) {
this.titleMargin = titleMargin;
}
public int getHeadMargin() {
return headMargin;
}
public void setHeadMargin(int headMargin) {
this.headMargin = headMargin;
}
public int getRowMargin() {
return rowMargin;
}
public void setRowMargin(int rowMargin) {
this.rowMargin = rowMargin;
}
public int getBottomMargin() {
return bottomMargin;
}
public void setBottomMargin(int bottomMargin) {
this.bottomMargin = bottomMargin;
}
public Color getLineColor() {
return lineColor;
}
public void setLineColor(Color lineColor) {
this.lineColor = lineColor;
}
public Color getTitleBackgroup() {
return titleBackgroup;
}
public void setTitleBackgroup(Color titleBackgroup) {
this.titleBackgroup = titleBackgroup;
}
public Color getHeadBackgroup() {
return headBackgroup;
}
public void setHeadBackgroup(Color headBackgroup) {
this.headBackgroup = headBackgroup;
}
public static void main(String[] args) throws Exception{
ExcelChart tableChart = new ExcelChart();
try {
List<String> titleList = Arrays.asList("名称","增幅","收入","收入净利润","今年收入增幅比例");
List<List<String>> rows = Arrays.asList(
Arrays.asList("项目收入1", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入2", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入3", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入4", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入5", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入6", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入7", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入8", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入9", "6.05%", "9.95亿", "1.83亿", "5.74‰"),
Arrays.asList("项目收入10", "6.05%", "9.95亿", "1.83亿", "5.74‰")
);
tableChart.setTitleBackgroup(new Color(237, 125, 49));
tableChart.setHeadBackgroup(new Color(252, 228, 214));
tableChart.setLineColor(new Color(237, 125, 49));
tableChart.createTableChart(new File("logs/tablechart1.png"),
"我是标题一二三四五六七八九十","日期:9月10日 制图:https://my.oschina.net/penngo",
titleList,rows,600);
} catch (Exception e) {
e.printStackTrace();
}
}
}