日期:2014-5-16 浏览次数:20190次

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

?上一篇介绍了POI组件操作Excel时如何对单元格和行进行设置,合并单元格等操作,最后给出一个综合实例,就是制作复杂报表,原理就是涉及合并行和列的计算。
??? 本篇就来详细分析一下复杂报表的分析与设计问题,并用POI通过程序来生成Excel报表。首先说一点文档相关内容。使用POI组件可以生成Office文档,而Office文档也有一些属性,比如作者,分类,公司等信息。我们若通过程序生成时,这一步就直接略过了,但有时我们会需要这些信息,要写入一些文档信息,那么该如何实现呢?
??? 我们分2003和2007两个版本说明,因为操作是不太一样的。看下面的代码:

// 设置核心属性
		POIXMLProperties.CoreProperties props = workbook2007.getProperties()
				.getCoreProperties();
		props.setCreator("Nanlei");
		props.setCategory("POI程序测试");
		props.setTitle("学生信息表");
		// 设置扩展属性
		POIXMLProperties.ExtendedProperties extProps = workbook2007
				.getProperties().getExtendedProperties();
		// 设置自定义属性
		POIXMLProperties.CustomProperties customProps = workbook2007
				.getProperties().getCustomProperties();

?? 生成2007的Excel时,只需上述步骤便可加入我们需要的属性了,具体的属性含义可以参考官方文档,这里仅仅添加作者,分类和标题,生成Excel文档后,我们可以查看到入校内容:

??? 那么这里就是我们设置的一些信息了。而对于2003,则需要如下的步骤:

		// 创建工作簿对象
		HSSFWorkbook workbook2003 = new HSSFWorkbook();
		workbook2003.createInformationProperties();
		SummaryInformation si = workbook2003.getSummaryInformation();
		si.setAuthor("Nanlei");
		si.setTitle("学生信息表");
		si.setComments("POI程序测试");
		DocumentSummaryInformation dsi = workbook2003
				.getDocumentSummaryInformation();
		dsi.setCompany("Pioneer");

? 要注意的是第二行,必须执行createInformationProperties()方法,之后才可以设置属性,这和2007的做法是不同的。这里只是给出示例,就不深入讨论每个设置项了。
??? 回头来看报表。中国式的复杂报表基本上是合计,合计再合计,就是数值分析到一个阶段后出一次合计,这个阶段可以按照业务的不同元素来划分。本例是根据经销商,省份最终到达事业部。那么设计数据库时就要唯一区分开这些元素,根据这些标识来实现划分,合并等,首先来准备一些数据。

static {
		cruiseServiceLocationList = new ArrayList<CruiseServiceLocation>();
		csl[0] = new CruiseServiceLocation("T001", "北京市", "北京总部", "bj", "清华大学",
				20);
		csl[1] = new CruiseServiceLocation("T001", "北京市", "北京总部", "bj", "北京大学",
				30);
		csl[2] = new CruiseServiceLocation("T001", "北京市", "海淀经销商", "bjhd",
				"西直门", 15);
		csl[3] = new CruiseServiceLocation("T001", "北京市", "海淀经销商", "bjhd",
				"首都机场", 50);
		csl[7] = new CruiseServiceLocation("T001", "辽宁省", "大连经销商", "lndl",
				"河口软件园", 15);
		csl[8] = new CruiseServiceLocation("T001", "辽宁省", "大连经销商", "lndl",
				"七贤岭腾飞软件园", 13);
		csl[9] = new CruiseServiceLocation("T001", "辽宁省", "大连经销商", "lndl",
				"高新园区信达街", 11);
		csl[19] = new CruiseServiceLocation("T003", "河北省", "石家庄经销商", "hbsjz",
				"火车站", 4);
		csl[20] = new CruiseServiceLocation("", "", "", "", "", 0);// 合并算法捕捉最后一行有问题,增补一行无效数据,计算时去除
		cruiseServiceLocationList.addAll(Arrays.asList(csl));
	}

??? (具体数据请参考源码,附件中下载)
??? 注意在最后增补一条无效数据,因为算法的限制,读取最后一行比较时可能会将其略过,所以这样保证所有数据都能被正常读出。这个数据结构根据事业部代号,省份和经销商名称来区分各个元素。
??? 算法就是根据标识位的不同来区分是否该进行特殊处理了,这之前数据要排好顺序,就可以分门别类进行了,来看一下合计算法:

// 合计量的计算
		CruiseServiceLocation cslTotal = null;
		List<CruiseServiceLocation> cslList = new ArrayList<CruiseServiceLocation>();
		// 合并计算控制
		double totalDealer = 0;
		double totalProvince = 0;
		double totalDivision = 0;
		int locationNum = 0;
		// 循环遍历List
		for (int i = 0; i < cruiseServiceLocationList.size(); i++) {
			cslList.add(cruiseServiceLocationList.get(i));
			// 是否是最后一条记录的开关
			boolean last = (i == cruiseServiceLocationList.size() - 1);
			// 取出相邻的两条记录进行比较
			CruiseServiceLocation csl1 = null;
			CruiseServiceLocation csl2 = null;
			if (!last) {
				csl1 = cruiseServiceLocationList.get(i);
				csl2 = cruiseServiceLocationList.get(i + 1);
			} else {
				// 防止最后一条记录无法加入集合
				csl1 = cruiseServiceLocationList.get(i);
				if (cruiseServiceLocationList.size() != 1)
					csl2 = cruiseServiceLocationList.get(i - 1);
				else
					csl2 = cruiseServiceLocationList.get(i);
			}
			// 开始处理
			if (csl1.getDealerName().equals(csl2.getDealerName())) {
				locationNum++;
				totalDealer += csl1.getMiles();
			} else {
				locationNum++;