众所周知,java生成报表的工具jfree,能生成各种图表图形,可是今天说的是要生成一个表格形式的报表。
其实这也简单,表格首推excel文档,只要模板确定,数据填充不是问题,excel操作不是问题,要咋样就来咋样啊!
但是产品说了,要能直观查看,能直接转发的表格报表,报表生成完之后你要微信模板消息通知我,你特么让我在微信里还要下载个excel文档再打开再转发吗?用户体验有木有!反人类操作有木有!
。。。。。。
那我把报表做成一张图片,把这一批次的图片生成到一个html页面上,您模板消息点开就是这个网页,报表一字排开,双击读图浏览,长按转发朋友朋友圈,怎么样啊?
做出来我看看先,报表大概长这么个样子:
特别说明:下级1、下级2处并不止有2个下级,有的项目有5个甚至更多个下级,有的项目1个下级都没有!合计下面还有一行小字,对报表中的各项表头做基本的解释,报表表头隔几个月可能要增减,大体模板基本固定为这个样子。。。
这个需求吧,我就开始琢磨:以前有同事做过这样子类似报表的,只是做完这个就离职了,相互之间还不太熟悉呢,翻出原来的代码来看,哇塞,1200多行代码,其中生成报表图片的方法就有600多行,看了代码,直呼了然了然,怪不得离职!
原来的做法,是逐项逐项的计算距离,定好文字的xy坐标,用Graphics2D进行依次绘制,一个位置与一个位置之间的坐标点只有逻辑关联,比如现在要在表头6之前加入一个表头,让原来只有11列的表格变成12列,那原来的代码从表头6开始之后的所有文字、边框的坐标点全部都要拿计算器重新计算一遍,这显然是很恐怖的!
根据这个思路,,我开始用面向对象的方法来考虑,一个报表由若干个表格组成,一个表格有上下左右四条边,有背景颜色、中间的内容有字体颜色、有字体大小、字体位置坐标,于是有如下实体属性(不要纠结访问修饰符):

    public String text="",text1=text,text2="";//表格内容较宽时要换成两行显示public Font font = new Font("宋体", Font.BOLD, 58);public Color fontColor=Color.BLACK;public Color backgroundColor=Color.WHITE;public Color borderColor=Color.black;public int fontX,fontY,fontX2,fontY2;//表格内容换行时两行文字的各自XY轴坐标public int borderX=0,borderY=0,borderWidth,borderHeight;public int gridX,gridY,gridWidth,gridHeight;public int orginWidth,orginHeight;//当前表格原点距离坐标原点的宽度和高度

看起来棒棒哒,于是开始做表格标题:

/*** 得到表格标题grid对象* @param tableName* @param gridWith* @param gridHeight* @return*/public static XXXXTableGridBean getInstanceForTableName(String tableName,int gridWith,int gridHeight){XXXXTableGridBean bean = new XXXXTableGridBean();bean.text = tableName;bean.font = new Font("宋体", Font.BOLD, 70);bean.fontColor = Color.WHITE;FontMetrics fm = new JLabel().getFontMetrics(bean.font);bean.fontX = (gridWith - fm.stringWidth(tableName))/2;//文字居中bean.fontY = 70;bean.backgroundColor = new Color(238, 154, 0);bean.gridX=bean.gridY=0;bean.gridWidth=gridWith;bean.gridHeight=gridHeight;bean.borderWidth=bean.gridWidth;bean.borderHeight=bean.gridHeight;bean.orginWidth = bean.gridWidth;bean.orginHeight = bean.gridHeight;return bean;}

做完标题又做表头:

/*** 得到表头grid对象* @param title* @param tableNameGridBean* @param preTitleGridBean* @param gridX* @param gridY* @param gridWidth* @param gridHeight* @return*/public static XXXXTableGridBean getInstanceForTableTitle(String title,XXXXTableGridBean tableNameGridBean,XXXXTableGridBean preTitleGridBean,int gridX, int gridY, int gridWidth, int gridHeight) {XXXXTableGridBean bean = new XXXXTableGridBean();bean.text = title;bean.backgroundColor = new Color(159, 121, 238);bean.gridX=gridX;bean.gridY=gridY;bean.gridWidth=gridWidth;bean.gridHeight=gridHeight;bean.borderX=gridX;bean.borderY=gridY;bean.borderWidth=bean.gridWidth;bean.borderHeight=bean.gridHeight;bean.orginWidth = preTitleGridBean.orginWidth+preTitleGridBean.gridWidth;bean.orginHeight = tableNameGridBean.gridHeight;bean.fontColor = Color.WHITE;FontMetrics fm = new JLabel().getFontMetrics(bean.font);int fontWidth = fm.stringWidth(title);if(fontWidth>gridWidth){//文字宽度超过表格宽度,则换行文字bean.text1 = bean.text.substring(0,4);bean.text2 = bean.text.replace(bean.text1,"").trim();fontWidth = fm.stringWidth(bean.text1);bean.fontX = bean.orginWidth+((bean.gridWidth - fontWidth)/2);//文字居中bean.fontY = tableNameGridBean.gridHeight+80;bean.fontX2 = bean.fontX;bean.fontY2 =  bean.fontY + 60;}else{bean.fontX = bean.orginWidth+((bean.gridWidth - fontWidth)/2);//文字居中bean.fontY = tableNameGridBean.gridHeight+80;}return bean;}

也还行,表头也顺利生成,于是做表格内容,注意,这里开始有第一个跨行的表格出现,而具体跨多少行,是由参数里面的数据条数动态决定的:

public static XXXXTableGridBean getInstanceForTableContentGrid_FirstRowSpan(String val,List<XXXXDTO> allCityInfos,XXXXDTO nowCityInfo){XXXXTableGridBean bean = new XXXXTableGridBean();bean.text = val;int index = allCityInfos.indexOf(nowCityInfo);int projectSize=0;for(int i=0;i<index;i++){projectSize += allCityInfos.get(i).getProjectInfoDTOList().size();}int topHeight = nameGridHeight+titleGridHeight+projectSize*contentGridHeight;bean.borderX=0;bean.borderY=topHeight;bean.borderWidth=GridBeanConfigs[0].getWidth();bean.borderHeight=nowCityInfo.getProjectInfoDTOList().size()*contentGridHeight;bean.gridX=bean.borderX;bean.gridY=bean.borderY;bean.gridWidth=bean.borderWidth;bean.gridHeight=bean.borderHeight;bean.orginWidth=bean.gridWidth;bean.orginHeight=topHeight;FontMetrics fm = new JLabel().getFontMetrics(bean.font);int fontWidth = fm.stringWidth(bean.text);bean.fontX = (bean.orginWidth-fontWidth)/2;bean.fontY = bean.orginHeight+(bean.borderHeight-bean.font.getSize())/2+bean.font.getSize();return bean;}

然后第二个跨行格出现,这里的跨行数也是由参数中的数据条数动态决定的:

public static XXXXTableGridBean getInstanceForTableContentGrid_GridContent(String val,XXXXTableGridBean preGridBean,int gridWidth,int gridHeight,int currentLineNumber,int allSubLineNumber,int nowSubLineNumber){XXXXTableGridBean bean = new XXXXTableGridBean();bean.text = val;bean.font =  new Font("宋体", Font.BOLD, 58/allSubLineNumber*nowSubLineNumber);bean.gridWidth=gridWidth;bean.gridHeight=gridHeight/allSubLineNumber*nowSubLineNumber;bean.borderWidth=bean.gridWidth;bean.borderHeight=bean.gridHeight/allSubLineNumber*nowSubLineNumber;if(preGridBean==null){bean.orginWidth=GridBeanConfigs[0].getWidth();bean.orginHeight=nameGridHeight+titleGridHeight+((currentLineNumber-1)*contentGridHeight)/allSubLineNumber*nowSubLineNumber;}else{bean.orginWidth = preGridBean.orginWidth+preGridBean.gridWidth;bean.orginHeight = preGridBean.orginHeight;}bean.gridX=bean.orginWidth;bean.gridY=bean.orginHeight;bean.borderX=bean.gridX;bean.borderY=bean.gridY;FontMetrics fm = new JLabel().getFontMetrics(bean.font);bean.fontX = bean.orginWidth+((bean.gridWidth - fm.stringWidth(val))/2);//文字居中bean.fontY = bean.orginHeight+bean.font.getSize()+30/allSubLineNumber*nowSubLineNumber;return bean;}

做到这,我已经呼吸急促头昏眼花了,赶紧用听诊器听了一下心跳!

我感觉这累死累活的不靠谱,这图画到后期,脑汁完全不够用,以后维护也是一大难题,扩展性或许是比原来高了一点点,但是优势不明显,半斤八两,代码量一上去就很头大了,就不能有其他解决方案吗?
我于是带着这个问题在睡梦中沉思,主要是想:既然报表要方便扩展易于变化,那最简单的就是做成excel,excel能不能动态设置跨行呢?可以,excel能不能在服务端打开以后自动截个图呢?不知道,但是excel倒是可以转存为图片,操作excel需要借助poi或者jxl,转存图片需要借助aspose-cells包,这篇文章有介绍:https://www.cnblogs.com/abc8023/p/9336513.html
还是觉得麻烦,表格这东西最熟悉的莫过于html的table标签,有没有可能把做好的html转存为图片呢?嘿,还真有:
基于Java内置浏览器DJNativeSwing的实现(https://blog.csdn.net/ltllml44/article/details/72910295)
基于qt的qtjambi包的shi(https://blog.csdn.net/redlevin/article/details/80145963)
基于Html2Image包的实现(https://www.cnblogs.com/zxf330301/p/5666592.html)
简直像突然感觉打开了新世界的大门!
综合对比下来,决定采用Html2Image尝试一下:
首先构造好数据,根据数据生成好html模板页面(这里采用jsp):

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><style type="text/css">html,body{margin:0px;padding:0px;}table{width:3680px;font-size:58px;border-top:1px solid black;border-left:1px solid black;text-align: center;}table td, table th{height:120px;border-right:1px solid black; border-bottom:1px solid black;line-height:58px;text-align: center;}table td{font-size: 58px;text-align: center;}</style>
</head>
<body>
<table cellpadding="0" cellspacing="0"><tr><th colspan="14" style="height:100px;line-height: 100px;background-color:#EF9A01;color:white;font-size:70px;">XXX报表(${dto.provinceName})${dto.dayText}</th></tr><tr style="height: 170px;background-color: #9F7AEE;color:white;"><th width="220">表头</th><th width="520">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th><th width="300">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th><th width="240">表头</th></tr><c:forEach items="${dto.dxxxDTOs}" var="d" varStatus="dStatus"><c:set var="preCityGridIndex" value="-1" /><c:set var="preProjectGridIndex" value="-1" /><c:forEach items="${d.projectInfoDTOList}" var="project" varStatus="pStatus"><c:forEach items="${project.childXXXDTOS}" var="c"><tr><c:if test="${dStatus.index!=preCityGridIndex}"><td rowspan="${d.cityRowSpanCount}">${d.downtownName}</td></c:if><c:if test="${pStatus.index!=preProjectGridIndex}"><td rowspan="${fn:length(project.childXXXDTOS)}">${project.item0}</td><td rowspan="${fn:length(project.childXXXDTOS)}">${project.item1}</td><td rowspan="${fn:length(project.childXXXDTOS)}">${project.item2}</td><td rowspan="${fn:length(project.childXXXDTOS)}">${project.item3}</td><td rowspan="${fn:length(project.childXXXDTOS)}">${project.item4}</td><td rowspan="${fn:length(project.childXXXDTOS)}">${project.item5}</td></c:if><td >${c.childName}</td><td >${c.childxx}</td><td >${c.childxx}</td><td >${c.childxx}</td><td >${c.childxx}</td><td >${c.childxx}</td><td >${c.childxx}</td></tr><c:set var="preCityGridIndex" value="${dStatus.index}" /><c:set var="preProjectGridIndex" value="${pStatus.index}" /></c:forEach></c:forEach></c:forEach><tr style="height: 120px;background-color:#7D26CD;color:white;line-height: 58px;"><td colspan="2">合计</td><c:forEach items="${dto.totalCountList}" var="n" varStatus="nStatus"><c:if test="${nStatus.index == 5}"><td>&nbsp;</td></c:if><c:if test="${nStatus.index != 5}"><td>${n}</td></c:if></c:forEach></tr>
</table>
</body>
</html>

然后用Html2Image生成图片:
加入jar包:

<dependency><groupId>com.github.xuwei-k</groupId><artifactId>html2image</artifactId><version>0.1.0</version>
</dependency>

生成图片:

private void html2Image(String configId,String imageID, String province, String templateWebURL) {HtmlImageGenerator generator = new HtmlImageGenerator();generator.loadUrl(templateWebURL);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}generator.getBufferedImage();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}String entryKey = chineseCharToPinYin(province);if (StringUtil.isEmpty(entryKey)) {entryKey = String.valueOf(Math.random());}entryKey += imageID;imageFilePath = Constant.XXXFilePath.replace("{day}", DateUtil.formatNoLineDate(new Date())).replace("{daily_configId}", configId)+ "img/";File imgFile = new File(imageFilePath);if (!imgFile.exists()) {imgFile.mkdirs();}generator.saveAsImage(imageFilePath + entryKey + ".png"); //经试验,在win7本地生成jpg图片整体背景色会发红,生成png则正常}

这里要注意:在win7本地生成jpg图片整体背景色会发红,生成png则正常,具体何故不知。
ok,到这里图片文件轻松搞定,再加上生成html文件代码、发送微信模板消息通知代码,前前后后总共约500行代码,关键是这种方式以后扩展很方便,轻松修改html模板页面,图片生成则和图片模板完全解耦,维护方便有木有!

此记,以查!

java生成报表(excel表格)的另类方法相关推荐

  1. python处理excel案例_使用Python处理Excel表格的简单方法

    使用Python处理Excel表格的简单方法 Excel 中的每一个单元,都会有这些属性:颜色(colors).number formatting.字体(fonts).边界(borders).alig ...

  2. java poi导出Excel表格超大数据量解决方案

    Java实现导出excel表格功能,大部分都会使用apache poi,apache poi API 地址 POI之前的版本不支持大数据量处理,如果数据过多则经常报OOM错误,有时候调整JVM大小效果 ...

  3. java 导入导出excel表格

    java 导入导出excel表格 业务上有需求上传excel表格并读取内容,本文记录一下该方法 表格导入 引入相应的工具包 <dependency><groupId>cn.af ...

  4. java table数据转excel,excel将数据转化成表格-如何将java数据转换成Excel表格

    excel 怎么把三列数据转换成一张表? 1.打开 OCR识别软然后单击"打开图像/PDF-"按钮 2弹出的"打开图窗口中,选择需要的扫描文件, 3.然后单击" ...

  5. java实现这个无表头空表格,《java程序导出excel表格是空白的没有数据?》 java怎么导入数据库...

    java程序导出excel表格是空白的没有数据? 看一下是否可以链接上数据库,或者查看一下链接的数据库是否有数据 java从数据库中导出excel poi 建议你使用pageoffice. java ...

  6. 计算机表格要学些什么,几个Excel表格的制作方法

    人靠衣装马靠鞍,那么Excel表格靠什么呢?靠表头啊!今天小编就来分享几个Excel表格的制作方法,既有实用的,也有"艺术"的-- 斜线表头制作 Excel中制作表格非常简单,选取 ...

  7. java实现读取excel表格中的数据,兼容xls和xlsx

    前言 利用 java 实现读取 excel 表格中的数据,兼容 xls 与 xlsx 格式,不用额外做区分,写不同的实现方法. 引入的依赖包 <dependency><groupId ...

  8. java poi对Excel表格进行单元行删除

    java poi对Excel表格进行单元行删除 前几天工作时,突然需要去使用POI 来对Excel 表格进行操作,其他的操作都都没什么问题,但是在需要删除某些单元行的时候碰到了一下小坑,记录下来,防止 ...

  9. 轻松胜任在Java中处理Excel表格

    轻松胜任在Java中处理Excel表格 概要 许多机构都使用微软Excel表格作为信息交换的方式,众多的非专业编程人员.商业分析师和项目经理都能熟练的运用该技术.而使用Java从Excel文档中分析. ...

最新文章

  1. 搭建Windows Server 2008故障转移群集
  2. Android WebView 支持H5的定位Js
  3. 区块链:新经济蓝图及导读
  4. 怎么向后删除?(删除光标后的字符)(按delete键)
  5. 24-hadoop-hiveserver2jdbc-正则数据导入
  6. Java并发编程 Volatile关键字解析
  7. plsql(轻量版)_记录类型1
  8. socket 通信关于bind那点事
  9. SP10707 COT2 - Count on a tree II
  10. Ubuntu11.04 3945ABG无线网卡驱动安装
  11. 解决 springboot 启动报错 -- Cannot determine embedded database driver class for database type NONE
  12. 笔试面试收获(持续更新中)
  13. 读取和修改caffemodel文件里的参数
  14. TreeView节点的演练 c# 1614840318
  15. macOS Big Sur中“通知中心”的小组件
  16. php是哪种化学药剂,常用化学试剂 英文缩写一览表
  17. 社交网络影响力最大化
  18. 用java编写猜数字游戏
  19. 如何准备全国计算机二级Python,二级Python考试技巧
  20. 高性能服务器设计——常用网络服务器设计模型

热门文章

  1. asp php微信支付,Asp微信支付接口代码 微信中生成订单后可以直接调出微信钱包直接付款_随便下源码网...
  2. 愿你是那天上星,永远闪耀照亮夜空——50行Python代码绘制满天星
  3. c语言中input作用是什么,C语言中的input是什么意思
  4. Axiso解决跨域访问
  5. STM32-uasrt发送0X01给上位机,上位机接收到的字符串为0XFC
  6. qq打开网页时提示用浏览器打开html,微信QQ打开网页时提示用浏览器打开
  7. 【特别推荐】10款唯美浪漫的婚礼 结婚纪念网站模板
  8. 《万字长文》-吃透Docker-基础篇
  9. 还记得小时候玩的坦克大战么,用Python就可以轻松实现
  10. 华为交换机配置链路聚合(手工模式链路聚合和lacp模式聚合)