JXLS2.4.0系列教程(四)——多sheet是怎么做到的
注:本⽂代码在第⼀篇⽂章基础上修改⽽成,请务必先阅读第⼀篇⽂章。
本⽂也不会过多的讲解模板中遍历表达式的写法和说明,请先阅读第⼆篇⽂章。
好吧,今天是国庆第⼆天,⼤清早起来先把⽂章给写了吧!
这篇内容主要讲解⼀些如何导出多sheet的报表,我将⽤⼀个学⽣成绩表作为讲解案例。多sheet的导出不单单是简单分sheet⽽已,还加⼊了分页的功能。效果先看下图:
Sheet1
Sheet2
⼤家可以看到,两个不同sheet中,表结构是⼀样,但是⾥⾯的学⽣数据有了个分页的效果。这是怎么做到的呢?
这⾥,我们得从模板制作讲起,知道了多sheet模板的原理后,写代码就轻松多了。
⼤家看红框⾥的注释:
jx:area(lastCell="I8")
jx:each(items="pages", var="page", lastCell="I8" multisheet="sheetNames")
第⼀⾏不⽤说,是划定模板区域。第⼆⾏看起来是⼀个很正常的遍历注释,但是⾥⾯多了⼀个multisheet="sheetNames"参数,这个参数就是分sheet的参数。我们连起来看就是,以整个Excel报表为⼀个遍历输出的对象,每⼀个sheet就是⼀条输出的记录。简单的说,就是遍历⼀个集合(List)items,将集合内每⼀个对象输出到每⼀个独⽴的sheet中。
只要写上“multisheet”属性JXLS就会⾃动的分sheet了。那么还有⼀个问题,就是multisheet属性的值sheetNames是什么意思?这个值是从model中传来的⼀个集合(⼀般⽤List),⾥⾯存放着每⼀页sheet应该起的名字,JXLS在读取pages进⾏分sheet遍历的时候,也会读
取sheetNames进⾏遍历给每⼀个sheet改名。
接下来我们看看A4单元格的注释,⼤家看过第三篇⽂章应该知道怎么嵌套循环了,这⾥本质上也是⼀个嵌套,所以A4⾥的遍历标签
的items写的值就是A1(红框)遍历标签⾥的page,然后点上对应的属性。
标签讲完了,具体怎么做呢?我们打算⽤⼀个叫做Page的类作为分页的javaBean对象,这个对象⾥存放着这⼀页应该显⽰的学⽣记录(List<Student>)和每⼀页的页名(sheet名)。然后将这个page对象放进⼀个链表中,传给model。
上图就是⽂章开头两张图⽚中学⽣成绩分页的链表⽰意图。
接下来我们开始写代码,假设数据库查询出来的结果就是⼀个装有学⽣对象的⼀长串链表。那么我们从最基本的学⽣类(student)开始写起,
public class Student {
String id;
String name;
Integer chinese;
Integer math;
Integer english;
Integer politics;
Integer history;
Integer geography;
public Student(String id, String name, Integer chinese, Integer math, Integer english, Integer politics, Integer history, Integer geography) { super();
this.id = id;
this.name = name;
this.chinese = chinese;
this.math = math;
this.politics = politics;
this.history = history;
}
public Student() {
}
/** 以下省略了get/set⽅法,请⾃⾏补全 */
}
学⽣类没什么好说的,学⽣的基本信息。接下来我们写页⾯类(Page),也就是每⼀个sheet应该包含什么信息。
/**
* 该类⽤来封装每⼀页的数据
*/
public class Page {
/**
* 页⾯信息
*/
private String sheetName; // 每个sheet名字
private String currentPage; // 当前页
private String tolalPage; // 总页
/**
* 页⾯遍历的数据 List 的泛型⾃⾏设置,如果所有数据都来着同⼀个类就写那个类,
* 不是同⼀个类有继承就写继承类的泛型,没有就写问号。
*/
private List<?> data;
public Page(String sheetName, String currentPage, String tolalPage, List<?> data) {
super();
this.sheetName = sheetName;
this.currentPage = currentPage;
this.data = data;
}
public Page() {
}
/** 以下省略了get/set⽅法,请⾃⾏补全 */
}
这个类说两句,属性data的类型的List,泛型如果你能确定传进来的对象就写上该对象,或者泛型继承,不能就写上问号。还有两个属性:currentPage和tolalPage这算是保留属性,本篇教程中没有⽤到,但是我还是写上了,建议同学们也可以写上,因为当前页或总页码可以往后使⽤⼯具标签时候可以判断是否最后⼀页。
接下来是重点了,我们已经有了从数据库中查询的⼀长串装有学⽣对象的链表,有了每⼀页应该装什么数据的页⾯对象,接下来我们要做的就是分页了。
把⼀长串装有学⽣对象的链表截成⼀段段的数据,然后装进page对象中,然后再把⼀节⼀节装有数据的page对象装进⼀个新链表中。返回给JXSL的model。
我们看下分页代码:
/**
* 此类⽤于分页,就是把从数据库查询出来的⼀个完整的List链表变成⼀截⼀截是数据。
* @author foxlee1024
*/
public class DataByPage {
static int pagesize = 3; // 每页记录数
/**
* 根据每页显⽰多少条数据计算总页数
* @param dataList 数据库查询的数据
*/
public static int countPages(List<?> dataList) {
int recordcount = dataList.size(); // 总记录数
return (recordcount + pagesize - 1) / pagesize;
}
public static List<Page> byPage(List<?> dataList) {
int pagecount; // 总页数
int nowDataListPoint = 0; // 读取到接收的哪⼀条数据
pagecount = countPages(dataList); // 计算页码
List<Page> pageList = new ArrayList<Page>(); // 页⾯分页
for (int i = 0; i < pagecount; i++) {
List<Object> pagedata = new ArrayList<Object>();
// 把传来的数据取出
while (nowDataListPoint < dataList.size()) {
pagedata.(nowDataListPoint));
nowDataListPoint += 1;
if (nowDataListPoint != 0 && nowDataListPoint % pagesize == 0) {
break;
}
}
Page page = new Page("page_" + (i + 1), (i + 1) + "", pagecount + "", pagedata);
pageList.add(page);
}
return pageList;
}
}
原理就是遍历传进来的⼀长串链表,然后根据判断将他截成⼀段后装进链表中,然后把链表封装进page类的data属性⾥,接着再把page类装进链表中,然后返回装有page对象的链表。
好了,全都齐全了,我们可以开始写main⽅法了。
public class TestMain {
public static void main(String[] args) throws Exception {
// 模板位置,输出流
String templatePath = "E:/template5.xls";
OutputStream os = new FileOutputStream("E:/out5.xls");
List<Student> list = generateData(); // 模拟数据库获取数据
List<Page> page = DataByPage.byPage(list); // 把获取的数据进⾏分页转换
Map<String, Object> model = new HashMap<String, Object>();
model.put("pages", page);
model.put("sheetNames", getSheetName(page));
model.put("className", "六年三班");
model.put("teacherComment", "已核实");
model.put("directorComment", "已核实");
os.close();
System.out.println("完成");
}
/**
* Excel 的分页名(页码)的封装
* 此⽅法⽤来获取分好页的页名信息,将信息放⼊⼀个链表中返回
*/
public static ArrayList<String> getSheetName(List<Page> page) {
ArrayList<String> al = new ArrayList<String>();
for (int i = 0; i < page.size(); i++) {
al.(i).getSheetName());
}
return al;
}
/**
* 模拟⽣成数据
*/
public static List<Student> generateData(){
List<Student> list = new ArrayList<Student>();
Student stu1 = new Student("001", "AAA", 10, 20, 30, 40, 50, 60);
Student stu2 = new Student("002", "BBB", 20, 30, 40, 50, 60, 70);
Student stu3 = new Student("003", "CCC", 30, 40, 50, 60, 70, 80);
Student stu4 = new Student("004", "DDD", 40, 50, 60, 70, 80, 90);
Student stu5 = new Student("005", "EEE", 50, 60, 70, 80, 90, 100);
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
list.add(stu5);
return list;
}
}
其他没要讲的,唯⼀有⼀个就是需要⼀个getSheetName() ⽅法,遍历获取每⼀个page的sheeName,然后装进链表中。然
后put⼊model的sheetNames键⾥。
模板就按照前边开头我们讲解的那个模板写,接下来我们运⾏⼀下代码。
当你看到控制台打出“完成”,欣喜的打开excel⽂件时候,你会发现第⼀页sheet是空⽩的......从第⼆页开始才是真正的内容。然后你看到第⼀页的sheet名是你模板的sheet名,你就知道肯定是JXLS在复制模板时候没有删除模板页⾯造成的。
这个问题我没办法解决,我尝试过在JxlsUtils中设置JxlsHelper的属性:jxlsHelper.setDeleteTemplateSheet(true); 然⽽并没什么卵⽤,不知道是我设置的地⽅不对,还是别的原因。请知道解决⽅案的同学务必留⾔告知⼀下,万分感谢!
虽然没办法从根本上解决,但是可以到凑活解决的办法,就是利⽤POI把多余的sheet给删掉,写⼀个⼯具类,代码如下:
public class DelSheet {
/**
* 删除指定的Sheet
* @param targetFile ⽬标⽂件
* @param sheetName Sheet名称
*/
public static void deleteSheet(String targetFile,String sheetName) {
try {
FileInputStream fis = new FileInputStream(targetFile);
HSSFWorkbook wb = new HSSFWorkbook(fis);
fileWrite(targetFile, wb);
//删除Sheet
fileWrite(targetFile, wb);
javabean是干嘛的fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 写隐藏/删除后的Excel⽂件
* @param targetFile ⽬标⽂件
* @param wb Excel对象
* @throws Exception
*/
public static void fileWrite(String targetFile,HSSFWorkbook wb) throws Exception{
FileOutputStream fileOut = new FileOutputStream(targetFile);
wb.write(fileOut);
fileOut.flush();
fileOut.close();
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论