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


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

    博客分类: 
     

    1 设计思路

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

    2 设计步骤

    2.1 初始步骤

    2.1.1创建Excel 报表模板

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



    2.1.2创建ExcelTemplate类

    Java代码  收藏代码
    1. /** 
    2.  * 该类实现了基于模板的导出 
    3.  * 如果要导出序号,需要在excel中定义一个标识为sernums 
    4.  * 如果要替换信息,需要传入一个Map,这个map中存储着要替换信息的值,在excel中通过#来开头 
    5.  * 要从哪一行那一列开始替换需要定义一个标识为datas 
    6.  * 如果要设定相应的样式,可以在该行使用styles完成设定,此时所有此行都使用该样式 
    7.  * 如果使用defaultStyls作为表示,表示默认样式,如果没有defaultStyles使用datas行作为默认样式 
    8.  */  
    9. public class ExcelTemplate {  
    10.         private ExcelTemplate() {  
    11.   
    12.     }  
    13.   
    14.     public static ExcelTemplate getInstance() {  
    15.         return et;  
    16.     }  
    17. }  

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

    Java代码  收藏代码
    1. /** 
    2.      * 数据行标识 
    3.      */  
    4.     public final static String DATA_LINE = "datas";  
    5.     /** 
    6.      * 默认样式标识 
    7.      */  
    8.     public final static String DEFAULT_STYLE = "defaultStyles";  
    9.     /** 
    10.      * 行样式标识 
    11.      */  
    12.     public final static String STYLE = "styles";  
    13.     /** 
    14.      * 插入序号样式标识 
    15.      */  
    16.     public final static String SER_NUM = "sernums";  
    17.     private static ExcelTemplate et = new ExcelTemplate();  
    18.       
    19.     private Workbook wb;  
    20.       
    21.     private Sheet sheet;  
    22.     /** 
    23.      * 数据的初始化列数 
    24.      */  
    25.     private int initColIndex;  
    26.     /** 
    27.      * 数据的初始化行数 
    28.      */  
    29.     private int initRowIndex;  
    30.     /** 
    31.      * 当前列数 
    32.      */  
    33.     private int curColIndex;  
    34.     /** 
    35.      * 当前行数 
    36.      */  
    37.     private int curRowIndex;  
    38.     /** 
    39.      * 当前行对象 
    40.      */  
    41.     private Row curRow;  
    42.     /** 
    43.      * 最后一行的数据 
    44.      */  
    45.     private int lastRowIndex;  
    46.     /** 
    47.      * 默认样式 
    48.      */  
    49.     private CellStyle defaultStyle;  
    50.     /** 
    51.      * 默认行高 
    52.      */  
    53.     private float rowHeight;  
    54.     /** 
    55.      * 存储某一方所对于的样式 
    56.      */  
    57.     private Map<Integer,CellStyle> styles;  
    58.     /** 
    59.      * 序号的列 
    60.      */  
    61.     private int serColIndex;  

    2.2 读取excel报表模板的数据

    Java代码  收藏代码
    1. /** 
    2.      * 1、读取相应的模板文档 
    3.      */  
    4.     public ExcelTemplate readTemplateByClasspath(String path){  
    5.         try {  
    6.             wb=WorkbookFactory.create(ExcelTemplate.class.getResourceAsStream(path));  
    7.             initTemplate();  
    8.         } catch (InvalidFormatException e) {  
    9.             e.printStackTrace();  
    10.             throw new RuntimeException("InvalidFormatException, please check.");  
    11.         } catch (IOException e) {  
    12.             e.printStackTrace();  
    13.             throw new RuntimeException("The template is not exist, please check.");  
    14.         }  
    15.         return this;  
    16.     }  
    17.       
    18.     public ExcelTemplate readTemplateByPath(String path){  
    19.         try {  
    20.             wb=WorkbookFactory.create(new File(path));  
    21.         } catch (InvalidFormatException e) {  
    22.             e.printStackTrace();  
    23.             throw new RuntimeException("InvalidFormatException, please check.");  
    24.         } catch (IOException e) {  
    25.             e.printStackTrace();  
    26.             throw new RuntimeException("The template is not exist, please check.");  
    27.         }  
    28.         return this;  
    29.     }  

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

    Java代码  收藏代码
    1. private void initTemplate(){  
    2.         sheet=wb.getSheetAt(0);  
    3.         initConfigData();  
    4.         lastRowIndex = sheet.getLastRowNum();  
    5.         curRow=sheet.getRow(curRowIndex);  
    6.     }  
    7. /** 
    8.      * 循环遍历,找到有datas字符的那个单元,记录initColIndex,initRowIndex,curColIndex,curRowIndex 
    9.      * 调用initStyles()方法 
    10.      * 在寻找datas字符的时候会顺便找一下sernums,如果有则记录其列号serColIndex;如果没有则调用initSer()方法,重新循环查找 
    11.      */  
    12.     private void initConfigData() {  
    13.         boolean findData=false;  
    14.         boolean findSer = false;  
    15.         for(Row row : sheet){  
    16.             if(findData) break;  
    17.             for(Cell c: row){  
    18.                 if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;  
    19.                 String str=c.getStringCellValue().trim();  
    20.                 if(str.equals(SER_NUM)){  
    21.                     serColIndex=c.getColumnIndex();  
    22.                     findSer=true;  
    23.                 }  
    24.                 if(str.equals(DATA_LINE)){  
    25.                     initColIndex=c.getColumnIndex();  
    26.                     initRowIndex=row.getRowNum();  
    27.                     curColIndex=initColIndex;  
    28.                     curRowIndex=initRowIndex;  
    29.                     findData=true;  
    30.                     break;  
    31.                 }  
    32.             }  
    33.         }  
    34.         if(!findSer){  
    35.             initSer();  
    36.         }  
    37.         initStyles();  
    38.     }  
    39. /** 
    40.      * 初始化序号位置 
    41.      */  
    42.     private void initSer() {  
    43.         for(Row row:sheet) {  
    44.             for(Cell c:row) {  
    45.                 if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;  
    46.                 String str = c.getStringCellValue().trim();  
    47.                 if(str.equals(SER_NUM)) {  
    48.                     serColIndex = c.getColumnIndex();  
    49.                 }  
    50.             }  
    51.         }  
    52.     }  
    53.       
    54.     /** 
    55.      * 初始化样式信息 
    56.      */  
    57.     private void initStyles(){  
    58.         styles = new HashMap<Integer, CellStyle>();  
    59.         for(Row row:sheet) {  
    60.             for(Cell c:row) {  
    61.                 if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;  
    62.                 String str = c.getStringCellValue().trim();  
    63.                 if(str.equals(DEFAULT_STYLE)) {  
    64.                     defaultStyle = c.getCellStyle();  
    65.                     rowHeight=row.getHeightInPoints();  
    66.                 }  
    67.                 if(str.equals(STYLE)||str.equals(SER_NUM)) {  
    68.                     styles.put(c.getColumnIndex(), c.getCellStyle());  
    69.                 }  
    70.             }  
    71.         }  
    72.     }  

    2.3 新建excel并向其写数据

    Java代码  收藏代码
    1. public void writeToFile(String filepath){  
    2.         FileOutputStream fos=null;  
    3.         try {  
    4.             fos=new FileOutputStream(filepath);  
    5.             wb.write(fos);  
    6.         } catch (FileNotFoundException e) {  
    7.             e.printStackTrace();  
    8.             throw new RuntimeException("写入的文件不存在"+e.getMessage());  
    9.         } catch (IOException e) {  
    10.             e.printStackTrace();  
    11.             throw new RuntimeException("写入数据失败"+e.getMessage());  
    12.         } finally{  
    13.             if(fos!=null)  
    14.                 try {  
    15.                     fos.close();  
    16.                 } catch (IOException e) {  
    17.                     e.printStackTrace();  
    18.                 }  
    19.         }  
    20.     }  

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

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

    Java代码  收藏代码
    1. public void createCell(String value){  
    2.         Cell c =curRow.createCell(curColIndex);  
    3.         setCellStyle(c);  
    4.         c.setCellValue(value);  
    5.         curColIndex++;  
    6.     }  
    7.     public void createCell(int value) {  
    8.         Cell c = curRow.createCell(curColIndex);  
    9.         setCellStyle(c);  
    10.         c.setCellValue((int)value);  
    11.         curColIndex++;  
    12.     }  
    13.     public void createCell(Date value) {  
    14.         Cell c = curRow.createCell(curColIndex);  
    15.         setCellStyle(c);  
    16.         c.setCellValue(value);  
    17.         curColIndex++;  
    18.     }  
    19.     public void createCell(double value) {  
    20.         Cell c = curRow.createCell(curColIndex);  
    21.         setCellStyle(c);  
    22.         c.setCellValue(value);  
    23.         curColIndex++;  
    24.     }  
    25.     public void createCell(boolean value) {  
    26.         Cell c = curRow.createCell(curColIndex);  
    27.         setCellStyle(c);  
    28.         c.setCellValue(value);  
    29.         curColIndex++;  
    30.     }  
    31.         public void createCell(Calendar value) {  
    32.         Cell c = curRow.createCell(curColIndex);  
    33.         setCellStyle(c);  
    34.         c.setCellValue(value);  
    35.         curColIndex++;  
    36.     }  

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

    Java代码  收藏代码
    1. /** 
    2.      * 设置某个单元格的样式 
    3.      * @param c 
    4.      */  
    5.     private void setCellStyle(Cell c) {  
    6.         if(styles.containsKey(c.getColumnIndex())) {  
    7.             c.setCellStyle(styles.get(c.getColumnIndex()));  
    8.         } else {  
    9.             c.setCellStyle(defaultStyle);  
    10.         }  
    11.     }  

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

    Java代码  收藏代码
    1. public void createNewRow(){  
    2.         if(lastRowIndex>curRowIndex&&curRowIndex!=initRowIndex) {  
    3.             sheet.shiftRows(curRowIndex, lastRowIndex, 1,true,true);  
    4.             lastRowIndex++;  
    5.         }  
    6.         curRow = sheet.createRow(curRowIndex);  
    7.         curRow.setHeightInPoints(rowHeight);  
    8.         curRowIndex++;  
    9.         curColIndex=initColIndex;  
    10.     }  

    2.5 实现增加序号

    Java代码  收藏代码
    1. /** 
    2.      * 插入序号,会自动找相应的序号标示的位置完成插入 
    3.      */  
    4.     public void insertSer() {  
    5.         int index = 1;  
    6.         Row row = null;  
    7.         Cell c = null;  
    8.         for(int i=initRowIndex;i<curRowIndex;i++) {  
    9.             row = sheet.getRow(i);  
    10.             c = row.createCell(serColIndex);  
    11.             setCellStyle(c);  
    12.             c.setCellValue(index++);  
    13.         }  
    14.     }  

     2.6 替换模板中#开头的值

    Java代码  收藏代码
    1. /** 
    2.      * 根据map替换相应的常量,通过Map中的值来替换#开头的值 
    3.      * @param datas 
    4.      */  
    5.     public void replaceFinalData(Map<String,String> datas) {  
    6.         if(datas==null) return;  
    7.         for(Row row:sheet) {  
    8.             for(Cell c:row) {  
    9.                 if(c.getCellType()!=Cell.CELL_TYPE_STRING) continue;  
    10.                 String str = c.getStringCellValue().trim();  
    11.                 if(str.startsWith("#")) {  
    12.                     if(datas.containsKey(str.substring(1))) {  
    13.                         c.setCellValue(datas.get(str.substring(1)));  
    14.                     }  
    15.                 }  
    16.             }  
    17.         }  
    18.     }  

     3实现步骤

    Java代码  收藏代码
    1. @Test  
    2.     public void test01() {  
    3.         ExcelTemplate et = ExcelTemplate.getInstance()  
    4.                     .readTemplateByClasspath("/excel/default.xls");  
    5.         et.createNewRow();  
    6.         et.createCell("1111111");  
    7.         et.createCell("aaaaaaaaaaaa");  
    8.         et.createCell("a1");  
    9.         et.createCell("a2a2");  
    10.         et.createNewRow();  
    11.         et.createCell("222222");  
    12.         et.createCell("bbbbb");  
    13.         et.createCell("b");  
    14.         et.createCell("dbbb");  
    15.         et.createNewRow();  
    16.         et.createCell("3333333");  
    17.         et.createCell("cccccc");  
    18.         et.createCell("a1");  
    19.         et.createCell(12333);  
    20.         et.createNewRow();  
    21.         et.createCell("4444444");  
    22.         et.createCell("ddddd");  
    23.         et.createCell("a1");  
    24.         et.createCell("a2a2");  
    25.         et.createNewRow();  
    26.         et.createCell("555555");  
    27.         et.createCell("eeeeee");  
    28.         et.createCell("a1");  
    29.         et.createCell(112);  
    30.         et.createNewRow();  
    31.         et.createCell("555555");  
    32.         et.createCell("eeeeee");  
    33.         et.createCell("a1");  
    34.         et.createCell("a2a2");  
    35.         et.createNewRow();  
    36.         et.createCell("555555");  
    37.         et.createCell("eeeeee");  
    38.         et.createCell("a1");  
    39.         et.createCell("a2a2");  
    40.         et.createNewRow();  
    41.         et.createCell("555555");  
    42.         et.createCell("eeeeee");  
    43.         et.createCell("a1");  
    44.         et.createCell("a2a2");  
    45.         et.createNewRow();  
    46.         et.createCell("555555");  
    47.         et.createCell("eeeeee");  
    48.         et.createCell("a1");  
    49.         et.createCell("a2a2");  
    50.         et.createNewRow();  
    51.         et.createCell("555555");  
    52.         et.createCell("eeeeee");  
    53.         et.createCell("a1");  
    54.         et.createCell("a2a2");  
    55.         et.createNewRow();  
    56.         et.createCell("555555");  
    57.         et.createCell("eeeeee");  
    58.         et.createCell("a1");  
    59.         et.createCell("a2a2");  
    60.         Map<String,String> datas = new HashMap<String,String>();  
    61.         datas.put("title","测试用户信息");  
    62.         datas.put("date","2012-06-02 12:33");  
    63.         datas.put("dep","昭通师专财务处");  
    64.         et.replaceFinalData(datas);  
    65.         et.insertSer();  
    66.         et.writeToFile("d:/test/poi/test01.xls");  
    67.     }  

     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));
    由于合并单元格,值取的是第一个单元格的值,所以后面的单元格可以直接赋值为空
    ————————————————

  • 相关阅读:
    asp.net Forms验证跨域页面不能访问的问题
    JavaScript创建命名空间
    DataTable转换成JSON字符串的函数
    javascript 正确截取单字节和双字节混和字符串的方法
    异常详细信息: 不能通过已删除的行访问该行的信息
    HttpUtility.ParseQueryString直接从字符串URL中提取参数
    支持函数,变量的算术表达式计算(三、加入函数)
    mp3 分类管理工具
    我好累
    电饭煲是如何知道饭已熟了的
  • 原文地址:https://www.cnblogs.com/xinxihua/p/12683058.html
Copyright © 2020-2023  润新知