Apache POI组件操作Excel,制作报表(三)

来源:互联网 发布:淘宝开店必备 编辑:程序博客网 时间:2024/05/29 03:17
    上一篇介绍了POI组件操作Excel时如何对单元格和行进行设置,合并单元格等操作,最后给出一个综合实例,就是制作复杂报表,原理就是涉及合并行和列的计算。 
    本篇就来详细分析一下复杂报表的分析与设计问题,并用POI通过程序来生成Excel报表。首先说一点文档相关内容。使用POI组件可以生成Office文档,而Office文档也有一些属性,比如作者,分类,公司等信息。我们若通过程序生成时,这一步就直接略过了,但有时我们会需要这些信息,要写入一些文档信息,那么该如何实现呢? 
    我们分2003和2007两个版本说明,因为操作是不太一样的。看下面的代码: 
Java代码  收藏代码
  1. // 设置核心属性  
  2.         POIXMLProperties.CoreProperties props = workbook2007.getProperties()  
  3.                 .getCoreProperties();  
  4.         props.setCreator("Nanlei");  
  5.         props.setCategory("POI程序测试");  
  6.         props.setTitle("学生信息表");  
  7.         // 设置扩展属性  
  8.         POIXMLProperties.ExtendedProperties extProps = workbook2007  
  9.                 .getProperties().getExtendedProperties();  
  10.         // 设置自定义属性  
  11.         POIXMLProperties.CustomProperties customProps = workbook2007  
  12.                 .getProperties().getCustomProperties();  

    生成2007的Excel时,只需上述步骤便可加入我们需要的属性了,具体的属性含义可以参考官方文档,这里仅仅添加作者,分类和标题,生成Excel文档后,我们可以查看到入校内容: 
 
    那么这里就是我们设置的一些信息了。而对于2003,则需要如下的步骤: 
Java代码  收藏代码
  1. // 创建工作簿对象  
  2. HSSFWorkbook workbook2003 = new HSSFWorkbook();  
  3. workbook2003.createInformationProperties();  
  4. SummaryInformation si = workbook2003.getSummaryInformation();  
  5. si.setAuthor("Nanlei");  
  6. si.setTitle("学生信息表");  
  7. si.setComments("POI程序测试");  
  8. DocumentSummaryInformation dsi = workbook2003  
  9.         .getDocumentSummaryInformation();  
  10. dsi.setCompany("Pioneer");  

    要注意的是第二行,必须执行createInformationProperties()方法,之后才可以设置属性,这和2007的做法是不同的。这里只是给出示例,就不深入讨论每个设置项了。 
    回头来看报表。中国式的复杂报表基本上是合计,合计再合计,就是数值分析到一个阶段后出一次合计,这个阶段可以按照业务的不同元素来划分。本例是根据经销商,省份最终到达事业部。那么设计数据库时就要唯一区分开这些元素,根据这些标识来实现划分,合并等,首先来准备一些数据。 
Java代码  收藏代码
  1. static {  
  2.         cruiseServiceLocationList = new ArrayList<CruiseServiceLocation>();  
  3.         csl[0] = new CruiseServiceLocation("T001""北京市""北京总部""bj""清华大学",  
  4.                 20);  
  5.         csl[1] = new CruiseServiceLocation("T001""北京市""北京总部""bj""北京大学",  
  6.                 30);  
  7.         csl[2] = new CruiseServiceLocation("T001""北京市""海淀经销商""bjhd",  
  8.                 "西直门"15);  
  9.         csl[3] = new CruiseServiceLocation("T001""北京市""海淀经销商""bjhd",  
  10.                 "首都机场"50);  
  11.         csl[7] = new CruiseServiceLocation("T001""辽宁省""大连经销商""lndl",  
  12.                 "河口软件园"15);  
  13.         csl[8] = new CruiseServiceLocation("T001""辽宁省""大连经销商""lndl",  
  14.                 "七贤岭腾飞软件园"13);  
  15.         csl[9] = new CruiseServiceLocation("T001""辽宁省""大连经销商""lndl",  
  16.                 "高新园区信达街"11);  
  17.         csl[19] = new CruiseServiceLocation("T003""河北省""石家庄经销商""hbsjz",  
  18.                 "火车站"4);  
  19.         csl[20] = new CruiseServiceLocation(""""""""""0);// 合并算法捕捉最后一行有问题,增补一行无效数据,计算时去除  
  20.         cruiseServiceLocationList.addAll(Arrays.asList(csl));  
  21.     }  

    (具体数据请参考源码,附件中下载) 
    注意在最后增补一条无效数据,因为算法的限制,读取最后一行比较时可能会将其略过,所以这样保证所有数据都能被正常读出。这个数据结构根据事业部代号,省份和经销商名称来区分各个元素。 
    算法就是根据标识位的不同来区分是否该进行特殊处理了,这之前数据要排好顺序,就可以分门别类进行了,来看一下合计算法: 
Java代码  收藏代码
  1. // 合计量的计算  
  2.         CruiseServiceLocation cslTotal = null;  
  3.         List<CruiseServiceLocation> cslList = new ArrayList<CruiseServiceLocation>();  
  4.         // 合并计算控制  
  5.         double totalDealer = 0;  
  6.         double totalProvince = 0;  
  7.         double totalDivision = 0;  
  8.         int locationNum = 0;  
  9.         // 循环遍历List  
  10.         for (int i = 0; i < cruiseServiceLocationList.size(); i++) {  
  11.             cslList.add(cruiseServiceLocationList.get(i));  
  12.             // 是否是最后一条记录的开关  
  13.             boolean last = (i == cruiseServiceLocationList.size() - 1);  
  14.             // 取出相邻的两条记录进行比较  
  15.             CruiseServiceLocation csl1 = null;  
  16.             CruiseServiceLocation csl2 = null;  
  17.             if (!last) {  
  18.                 csl1 = cruiseServiceLocationList.get(i);  
  19.                 csl2 = cruiseServiceLocationList.get(i + 1);  
  20.             } else {  
  21.                 // 防止最后一条记录无法加入集合  
  22.                 csl1 = cruiseServiceLocationList.get(i);  
  23.                 if (cruiseServiceLocationList.size() != 1)  
  24.                     csl2 = cruiseServiceLocationList.get(i - 1);  
  25.                 else  
  26.                     csl2 = cruiseServiceLocationList.get(i);  
  27.             }  
  28.             // 开始处理  
  29.             if (csl1.getDealerName().equals(csl2.getDealerName())) {  
  30.                 locationNum++;  
  31.                 totalDealer += csl1.getMiles();  
  32.             } else {  
  33.                 locationNum++;  
  34.                 totalDealer += csl1.getMiles();  
  35.                 cslTotal = new CruiseServiceLocation();  
  36.                 cslTotal.setTotalDealer(totalDealer);  
  37.                 cslTotal.setLocationNum(locationNum);  
  38.                 cslList.add(cslTotal);  
  39.                 totalDealer = 0;  
  40.                 locationNum = 0;  
  41.             }  
  42.             if (csl1.getProvince().equals(csl2.getProvince())) {  
  43.                 totalProvince += csl1.getMiles();  
  44.             } else {  
  45.                 totalProvince += csl1.getMiles();  
  46.                 cslTotal = new CruiseServiceLocation();  
  47.                 cslTotal.setTotalProvince(totalProvince);  
  48.                 cslList.add(cslTotal);  
  49.                 totalProvince = 0;  
  50.             }  
  51.             if (csl1.getDivision().equals(csl2.getDivision())) {  
  52.                 totalDivision += csl1.getMiles();  
  53.             } else {  
  54.                 totalDivision += csl1.getMiles();  
  55.                 cslTotal = new CruiseServiceLocation();  
  56.                 cslTotal.setTotalDivision(totalDivision);  
  57.                 cslList.add(cslTotal);  
  58.                 totalDivision = 0;  
  59.             }  
  60.         }  

    其中Bean的设计如下: 
Java代码  收藏代码
  1. package org.ourpioneer.excel.bean;  
  2.   
  3. /** 
  4.  * 巡航服务地点bean 
  5.  *  
  6.  * @author Nanlei 
  7.  *  
  8.  */  
  9. public class CruiseServiceLocation {  
  10.     private String division;// 事业部  
  11.     private String province;// 省份  
  12.     private String dealerName;// 经销商名称  
  13.     private String dealerCode;// 经销商代码  
  14.     private String location;// 巡航服务地点  
  15.     private double miles;// 巡航服务里程  
  16.     private int locationNum;// 地点条数  
  17.     private double totalDealer;// 经销商合计  
  18.     private double totalProvince;// 省份合计  
  19.     private double totalDivision;// 事业部合计  
  20.   
  21.     public CruiseServiceLocation() {  
  22.         super();  
  23.     }  
  24.   
  25.     public CruiseServiceLocation(String division, String province,  
  26.             String dealerName, String dealerCode, String location, double miles) {  
  27.         super();  
  28.         this.division = division;  
  29.         this.province = province;  
  30.         this.dealerName = dealerName;  
  31.         this.dealerCode = dealerCode;  
  32.         this.location = location;  
  33.         this.miles = miles;  
  34.     }  
  35. // 省略getter和setter方法  
  36. }  

    下面来分析一下这个算法,思路很简单,就是逐条记录进行比较,发现不同后立即处理,按照从小到大的顺序,一次处理经销商,省份和事业部,取出两条相邻元素之后,首先比较的是经销商是否一致,如果一致,经销商数量加1,里程累加,不一致时,这两个量也要相应计算并放入新的对象中,加入list里,这样一个合计行就加好了,后面的省份和事业部处理也是这个思路。循环完成,我们就得到了按顺序排好的最终列表,剩下的就是遍历这个列表来生成表格了。 
    要注意我们之前填补了一条无效记录,那么在合并的时候也会多处三行合计,要把这四项排除在外,不能忘记了。 
    计算合并行是需要一些辅助变量,比如: 
Java代码  收藏代码
  1. // 省份合计和事业部合计时跨行的计算数据  
  2. int comboProvince = 0;  
  3. int comboDivision = 0;  
  4. List<Integer> indexComboProvice = new ArrayList<Integer>();  
  5. List<Integer> indexComboDivision = new ArrayList<Integer>();  

    它们是用来计算合并数量的,最后用在跨行数量上,因为每出一条合计,整体跨行数就要增加,那么需要将这些数据记录到List中,方便处理。我们就引入了这些辅助变量。 
    先来看经销商合并算法: 
Java代码  收藏代码
  1. if (csl.getTotalDealer() != 0) {  
  2.     row.createCell(0).setCellStyle(style);  
  3.     row.createCell(1).setCellStyle(style);  
  4.     XSSFCell t_dealer = row.createCell(2);  
  5.     t_dealer.setCellValue("经销商合计");  
  6.     t_dealer.setCellStyle(biStyle);  
  7.     sheet.addMergedRegion(new CellRangeAddress(i + 1, i + 124));  
  8.     XSSFCell t_dealer_value = row.createCell(5);  
  9.     t_dealer_value.setCellValue(csl.getTotalDealer());  
  10.     t_dealer_value.setCellStyle(biStyle);  
  11.     sheet.addMergedRegion(new CellRangeAddress(i  
  12.             - csl.getLocationNum() + 1, i, 22));  
  13.     sheet.addMergedRegion(new CellRangeAddress(i  
  14.             - csl.getLocationNum() + 1, i, 33));  
  15. }  

    这个就很简单了,因为不涉及到跨行,只是跨列而已,这都是预先已经设定好的,不难。而省份和经销商的跨行计算就略微复杂: 
Java代码  收藏代码
  1. if (csl.getTotalProvince() != 0) {  
  2.                 XSSFCell t_province = row.createCell(1);  
  3.                 row.createCell(0).setCellStyle(style);  
  4.                 row.createCell(3).setCellStyle(style);  
  5.                 row.createCell(4).setCellStyle(style);  
  6.                 t_province.setCellValue("省份合计");  
  7.                 t_province.setCellStyle(biStyle);  
  8.                 sheet.addMergedRegion(new CellRangeAddress(i + 1, i + 114));  
  9.                 XSSFCell t_province_value = row.createCell(5);  
  10.                 t_province_value.setCellValue(csl.getTotalProvince());  
  11.                 t_province_value.setCellStyle(biStyle);  
  12.                 indexComboProvice.add(i);  
  13.                 // 合并行  
  14.                 if (comboProvince == 0) {  
  15.                     sheet.addMergedRegion(new CellRangeAddress(1, i, 11));  
  16.                 } else if (comboProvince == 1) {  
  17.                     sheet.addMergedRegion(new CellRangeAddress(  
  18.                             indexComboProvice.get(comboProvince - 1)  
  19.                                     + comboProvince + 1, i, 11));  
  20.                 } else {  
  21.                     sheet.addMergedRegion(new CellRangeAddress(  
  22.                             indexComboProvice.get(comboProvince - 1)  
  23.                                     + comboProvince, i, 11));  
  24.   
  25.                 }  
  26.                 comboProvince++;  
  27.             }  

    这里为了将所有单元格都加入样式,所以没有数据填充的单元格仅做样式处理即可。合并行的时候要看这是第几次合并,因为算法不同。第一次时比较简单,只需数出有第一个合计中有多少经销商即可。而后续的就需要记录上次合并出现的位置,然后再加上第二个经销商的数量,之后进行合并。那么事业部的算法也是如此。 
Java代码  收藏代码
  1. if (csl.getTotalDivision() != 0) {  
  2.     XSSFCell t_division = row.createCell(0);  
  3.     row.createCell(1).setCellStyle(style);  
  4.     row.createCell(2).setCellStyle(style);  
  5.     row.createCell(3).setCellStyle(style);  
  6.     row.createCell(4).setCellStyle(style);  
  7.     t_division.setCellValue("事业部合计");  
  8.     t_division.setCellStyle(biStyle);  
  9.     sheet.addMergedRegion(new CellRangeAddress(i + 1, i + 104));  
  10.     XSSFCell t_division_value = row.createCell(5);  
  11.     t_division_value.setCellValue(csl.getTotalDivision());  
  12.     t_division_value.setCellStyle(biStyle);  
  13.     indexComboDivision.add(i);  
  14.     // 合并行  
  15.     if (comboDivision == 0) {  
  16.         sheet.addMergedRegion(new CellRangeAddress(1, i, 00));  
  17.     } else if (comboDivision == 1) {  
  18.         sheet.addMergedRegion(new CellRangeAddress(  
  19.                 indexComboDivision.get(comboDivision - 1)  
  20.                         + comboDivision + 1, i, 00));  
  21.     } else {  
  22.         sheet.addMergedRegion(new CellRangeAddress(  
  23.                 indexComboDivision.get(comboDivision - 1)  
  24.                         + comboDivision, i, 00));  
  25.     }  
  26.     comboDivision++;  
  27. }  

    最后生成文件,就得到了我们要的报表了。 
 
    综合实例就介绍完了,下一篇将结合Spring MVC来说明在Web应用程序中如何生成Excel文件并进行下载操作。
  • exceltest.rar (34.1 KB)
  • 下载次数: 532
  • 查看图片附件
转载自:http://sarin.iteye.com/blog/853418
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 如果别人欠我钱不还 我该怎么办 老婆欠信用卡的钱我该怎么办 我欠了很多钱我该怎么办 一个人欠我钱跑了我该怎么办 买房子时间长了成危楼了怎么办 别人欠我钱人找不到了怎么办 欠银行信用卡钱人失踪了怎么办 欧洲卡车模拟2没油了怎么办 蓝牙安装包里没有微信怎么办 腾讯手游模拟器玩全军出击卡怎么办 手游cf用模拟器画面很抖怎么办 腾讯手游助手模拟器游戏卡死怎么办 腾讯手游模拟器提示注册以满怎么办 手游绝地求生被检测成模拟器怎么办 腾讯手游助手模拟器内存不够怎么办 微信视频对方听不到我的声音怎么办 学生考试传纸条作弊老师应怎么办 苹果手机微信小游戏没有声音怎么办 红米手机游戏下好了安装不了怎么办 小米手机sd卡存储已满怎么办 英雄联盟峡谷之巅资格被收回怎么办 人进监狱之前信用卡没还怎么办 荒野行动资源文件下载卡住了怎么办 荒野行动绑定的手机号停机了怎么办 不小心误点了爱奇艺扣款了怎么办 和别人吵架别人先骂你怎么办 自己人被带到了缅甸黑社会了怎么办 绝地求生忘记复制钥匙激活码怎么办 绝地求生买的钥匙激活码丢了怎么办 蓝河奶粉宝宝吃了不长肉怎么办 苹果ios版本太低激活不了怎么办 脸上被油烫伤了 起了水泡怎么办 皮肤被油烫伤起来个水泡怎么办 去泰国旅游没来得及兑换泰铢怎么办 游戏和安卓 不和 出现黑屏怎么办 邻居把垃圾放在楼梯口不丢怎么办 58热敏小票打印机口松了怎么办 王鹏的眼睛今天起疙瘩了怎么办 背包带子老从肩膀滑下来怎么办 绝地求生用手机流量更新不成怎么办 手机拍的视频在电脑上放不了怎么办