热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java不固定模板excel_Java+POI+模板”一:打造复杂Excel报表

JavaPOI模板”一:打造复杂Excel报表博客分类:1设计思路Java对于Excel的操作一般借助于POI类库,由于有些报表的表头比较

Java+POI+模板”一:打造复杂Excel 报表

博客分类:

1 设计思路

Java 对于Excel 的操作一般借助于POI 类库,由于有些报表的表头比较复杂,直接用POI 控制报表的生成比较困难,这时可以先制作Excel 报表模板,而后再通过Java 调用POI 函数将用户数据写入到Excel 报表模板,最后导出到新的目标文件即可。

2 设计步骤

2.1 初始步骤

2.1.1创建Excel 报表模板

根据需要设计出Excel 报表,并保存为default.xls。如下图所示。

02508a181c6bf6af9170ea56599d44d5.png

2.1.2创建ExcelTemplate类

Java代码  bf632ec55a05f69a9359f73417402b26.png

/**

* 该类实现了基于模板的导出

* 如果要导出序号,需要在excel中定义一个标识为sernums

* 如果要替换信息,需要传入一个Map,这个map中存储着要替换信息的值,在excel中通过#来开头

* 要从哪一行那一列开始替换需要定义一个标识为datas

* 如果要设定相应的样式,可以在该行使用styles完成设定,此时所有此行都使用该样式

* 如果使用defaultStyls作为表示,表示默认样式,如果没有defaultStyles使用datas行作为默认样式

*/

public class ExcelTemplate {

private ExcelTemplate() {

}

public static ExcelTemplate getInstance() {

return et;

}

}

下面是以后要用到的一些变量和常量

Java代码  bf632ec55a05f69a9359f73417402b26.png

/**

* 数据行标识

*/

public final static String DATA_LINE = "datas";

/**

* 默认样式标识

*/

public final static String DEFAULT_STYLE = "defaultStyles";

/**

* 行样式标识

*/

public final static String STYLE = "styles";

/**

* 插入序号样式标识

*/

public final static String SER_NUM = "sernums";

private static ExcelTemplate et = new ExcelTemplate();

private Workbook wb;

private Sheet sheet;

/**

* 数据的初始化列数

*/

private int initColIndex;

/**

* 数据的初始化行数

*/

private int initRowIndex;

/**

* 当前列数

*/

private int curColIndex;

/**

* 当前行数

*/

private int curRowIndex;

/**

* 当前行对象

*/

private Row curRow;

/**

* 最后一行的数据

*/

private int lastRowIndex;

/**

* 默认样式

*/

private CellStyle defaultStyle;

/**

* 默认行高

*/

private float rowHeight;

/**

* 存储某一方所对于的样式

*/

private Map styles;

/**

* 序号的列

*/

private int serColIndex;

2.2 读取excel报表模板的数据

Java代码  bf632ec55a05f69a9359f73417402b26.png

/**

* 1、读取相应的模板文档

*/

public ExcelTemplate readTemplateByClasspath(String path){

try {

wb=WorkbookFactory.create(ExcelTemplate.class.getResourceAsStream(path));

initTemplate();

} catch (InvalidFormatException e) {

e.printStackTrace();

throw new RuntimeException("InvalidFormatException, please check.");

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("The template is not exist, please check.");

}

return this;

}

public ExcelTemplate readTemplateByPath(String path){

try {

wb=WorkbookFactory.create(new File(path));

} catch (InvalidFormatException e) {

e.printStackTrace();

throw new RuntimeException("InvalidFormatException, please check.");

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("The template is not exist, please check.");

}

return this;

}

在读取报表模板的时候会初始化模板,记录模板的样式,插入数据的位置

Java代码  bf632ec55a05f69a9359f73417402b26.png

private void initTemplate(){

sheet=wb.getSheetAt(0);

initConfigData();

lastRowIndex = sheet.getLastRowNum();

curRow=sheet.getRow(curRowIndex);

}

/**

* 循环遍历,找到有datas字符的那个单元,记录initColIndex,initRowIndex,curColIndex,curRowIndex

* 调用initStyles()方法

* 在寻找datas字符的时候会顺便找一下sernums,如果有则记录其列号serColIndex;如果没有则调用initSer()方法,重新循环查找

*/

private void initConfigData() {

boolean findData=false;

boolean findSer = false;

for(Row row : sheet){

if(findData) break;

for(Cell c: row){

if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;

String str=c.getStringCellValue().trim();

if(str.equals(SER_NUM)){

serColIndex=c.getColumnIndex();

findSer=true;

}

if(str.equals(DATA_LINE)){

initColIndex=c.getColumnIndex();

initRowIndex=row.getRowNum();

curColIndex=initColIndex;

curRowIndex=initRowIndex;

findData=true;

break;

}

}

}

if(!findSer){

initSer();

}

initStyles();

}

/**

* 初始化序号位置

*/

private void initSer() {

for(Row row:sheet) {

for(Cell c:row) {

if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;

String str = c.getStringCellValue().trim();

if(str.equals(SER_NUM)) {

serColIndex = c.getColumnIndex();

}

}

}

}

/**

* 初始化样式信息

*/

private void initStyles(){

styles = new HashMap();

for(Row row:sheet) {

for(Cell c:row) {

if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;

String str = c.getStringCellValue().trim();

if(str.equals(DEFAULT_STYLE)) {

defaultStyle = c.getCellStyle();

rowHeight=row.getHeightInPoints();

}

if(str.equals(STYLE)||str.equals(SER_NUM)) {

styles.put(c.getColumnIndex(), c.getCellStyle());

}

}

}

}

2.3 新建excel并向其写数据

Java代码  bf632ec55a05f69a9359f73417402b26.png

public void writeToFile(String filepath){

FileOutputStream fos=null;

try {

fos=new FileOutputStream(filepath);

wb.write(fos);

} catch (FileNotFoundException e) {

e.printStackTrace();

throw new RuntimeException("写入的文件不存在"+e.getMessage());

} catch (IOException e) {

e.printStackTrace();

throw new RuntimeException("写入数据失败"+e.getMessage());

} finally{

if(fos!=null)

try {

fos.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

2.4 实现Excel公共模板的第一步(实现了数据插入)

下面是创建单元格的方法,以及重载方法

Java代码  bf632ec55a05f69a9359f73417402b26.png

public void createCell(String value){

Cell c =curRow.createCell(curColIndex);

setCellStyle(c);

c.setCellValue(value);

curColIndex++;

}

public void createCell(int value) {

Cell c = curRow.createCell(curColIndex);

setCellStyle(c);

c.setCellValue((int)value);

curColIndex++;

}

public void createCell(Date value) {

Cell c = curRow.createCell(curColIndex);

setCellStyle(c);

c.setCellValue(value);

curColIndex++;

}

public void createCell(double value) {

Cell c = curRow.createCell(curColIndex);

setCellStyle(c);

c.setCellValue(value);

curColIndex++;

}

public void createCell(boolean value) {

Cell c = curRow.createCell(curColIndex);

setCellStyle(c);

c.setCellValue(value);

curColIndex++;

}

public void createCell(Calendar value) {

Cell c = curRow.createCell(curColIndex);

setCellStyle(c);

c.setCellValue(value);

curColIndex++;

}

上面的方法会调用setCellStyle方法来设置单元格样式

Java代码  bf632ec55a05f69a9359f73417402b26.png

/**

* 设置某个单元格的样式

* @param c

*/

private void setCellStyle(Cell c) {

if(styles.containsKey(c.getColumnIndex())) {

c.setCellStyle(styles.get(c.getColumnIndex()));

} else {

c.setCellStyle(defaultStyle);

}

}

createNewRow方法用于创建新的行,新的行的位置位于curRowIndex,从curRowIndex到lastRowIndex的所有行会自动向下移动一行。

Java代码  bf632ec55a05f69a9359f73417402b26.png

public void createNewRow(){

if(lastRowIndex>curRowIndex&&curRowIndex!=initRowIndex) {

sheet.shiftRows(curRowIndex, lastRowIndex, 1,true,true);

lastRowIndex++;

}

curRow = sheet.createRow(curRowIndex);

curRow.setHeightInPoints(rowHeight);

curRowIndex++;

curColIndex=initColIndex;

}

2.5 实现增加序号

Java代码  bf632ec55a05f69a9359f73417402b26.png

/**

* 插入序号,会自动找相应的序号标示的位置完成插入

*/

public void insertSer() {

int index = 1;

Row row = null;

Cell c = null;

for(int i=initRowIndex;i

row = sheet.getRow(i);

c = row.createCell(serColIndex);

setCellStyle(c);

c.setCellValue(index++);

}

}

2.6 替换模板中#开头的值

Java代码  bf632ec55a05f69a9359f73417402b26.png

/**

* 根据map替换相应的常量,通过Map中的值来替换#开头的值

* @param datas

*/

public void replaceFinalData(Map datas) {

if(datas==null) return;

for(Row row:sheet) {

for(Cell c:row) {

if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;

String str = c.getStringCellValue().trim();

if(str.startsWith("#")) {

if(datas.containsKey(str.substring(1))) {

c.setCellValue(datas.get(str.substring(1)));

}

}

}

}

}

3实现步骤

Java代码  bf632ec55a05f69a9359f73417402b26.png

@Test

public void test01() {

ExcelTemplate et = ExcelTemplate.getInstance()

.readTemplateByClasspath("/excel/default.xls");

et.createNewRow();

et.createCell("1111111");

et.createCell("aaaaaaaaaaaa");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("222222");

et.createCell("bbbbb");

et.createCell("b");

et.createCell("dbbb");

et.createNewRow();

et.createCell("3333333");

et.createCell("cccccc");

et.createCell("a1");

et.createCell(12333);

et.createNewRow();

et.createCell("4444444");

et.createCell("ddddd");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell(112);

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell("a2a2");

et.createNewRow();

et.createCell("555555");

et.createCell("eeeeee");

et.createCell("a1");

et.createCell("a2a2");

Map datas = new HashMap();

datas.put("title","测试用户信息");

datas.put("date","2012-06-02 12:33");

datas.put("dep","昭通师专财务处");

et.replaceFinalData(datas);

et.insertSer();

et.writeToFile("d:/test/poi/test01.xls");

}

4 最后结果

java 导出自定义样式excel

原创诸葛_小明 最后发布于2018-11-01 14:56:59 阅读数 2294  收藏

展开

由于项目需要 要求导出一个这样的表格

然而 正常导出的表格都是这样婶儿地

这种格式网上demo有很多就不详细说了 ,主要说说上面三行是怎么画的。

第一行大标题,是9行合并成的一行,而且字体大小需要单独设置

HSSFSheet sheet;

HSSFCell cell;

response.setContentType("application/octet-stream");

response.setHeader("Content-Disposition", "attachment;filename="+filename+".xls");

sheet = workbook.createSheet("物料调拨单");

//第一行大标题

HSSFCellStyle tStyle = workbook.createCellStyle();

tStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);

tStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);

HSSFFont tFont = workbook.createFont(); //标题字体

tFont.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);

tFont.setFontHeightInPoints((short)15);

tStyle.setFont(tFont);

short twidth = 15,theight=25*20;

sheet.setDefaultColumnWidth(twidth);

cell = getCell(sheet,0,0);

cell.setCellStyle(tStyle);

setText(cell,"物料调拨单");

sheet.getRow(0).setHeight(theight);

sheet.addMergedRegion(new CellRangeAddress(0,0,0,8));

sheet.addMergedRegion(new CellRangeAddress(0,0,0,8)); 这个就是合并单元格方法,需要传入4个int行的参数,

分别是 起始行,结束行,起始列,结束列。因为我们需要在第一行显示1个9个格的标题所以行就是从0到0,列是从0到8

同理: 下面2行可以这么设置合并

sheet.addMergedRegion(new CellRangeAddress(1, 1, 1, 2));

sheet.addMergedRegion(new CellRangeAddress(2, 2, 1, 2));

sheet.addMergedRegion(new CellRangeAddress(1, 1, 4, 5));

sheet.addMergedRegion(new CellRangeAddress(2, 2, 4, 5));

sheet.addMergedRegion(new CellRangeAddress(1, 1, 7, 8));

sheet.addMergedRegion(new CellRangeAddress(2, 2, 7, 8));

由于合并单元格,值取的是第一个单元格的值,所以后面的单元格可以直接赋值为空

————————————————



推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • Netty源代码分析服务器端启动ServerBootstrap初始化
    本文主要分析了Netty源代码中服务器端启动的过程,包括ServerBootstrap的初始化和相关参数的设置。通过分析NioEventLoopGroup、NioServerSocketChannel、ChannelOption.SO_BACKLOG等关键组件和选项的作用,深入理解Netty服务器端的启动过程。同时,还介绍了LoggingHandler的作用和使用方法,帮助读者更好地理解Netty源代码。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 本文介绍了Java中Currency类的getInstance()方法,该方法用于检索给定货币代码的该货币的实例。文章详细解释了方法的语法、参数、返回值和异常,并提供了一个示例程序来说明该方法的工作原理。 ... [详细]
  • 本文分析了Wince程序内存和存储内存的分布及作用。Wince内存包括系统内存、对象存储和程序内存,其中系统内存占用了一部分SDRAM,而剩下的30M为程序内存和存储内存。对象存储是嵌入式wince操作系统中的一个新概念,常用于消费电子设备中。此外,文章还介绍了主电源和后备电池在操作系统中的作用。 ... [详细]
author-avatar
福田汽车-唐山万联
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有