之前在網上找的方法都得一個表格一個表格的去畫,每次寫一個導出都要花費很多時間。
最近又要做合併單元格,爲防止以後再去寫那些重複代碼,
於是我就寫了個能滿足大部分合並單元格導出需求的方法。
package com.ptgl.util;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.poi.hssf.util.CellRangeAddress;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import com.alibaba.fastjson.JSONObject;
/*
* 使用方式示例:
*
* public void export(){
* List<Map<String, Object>> list=null;//表內容集合,從數據庫查,需要合併的列要進行分組,否則需要做合併的時候可能達不到理想結果
* List<Map<String, Object>> listmap=new ArrayList<Map<String, Object>>();
* Map<String,Object> map=new LinkedHashMap<String,Object>();
* //標題
* map.put("head_C11", findYear+"年單位通車年限統計");
* listmap.add(map);
* //第一行列頭(_*表示表頭從第幾列開始,默認0,_C*表示合併*列,_R*表示合併*行)
* map=new LinkedHashMap<String,Object>();
* map.put("column1_C2_R2", "管養單位");
* map.put("column2_R2", "通車裏程(km)");
* map.put("column3_C8", "通車時間(年)");
* listmap.add(map);
* //第二行列頭
* map=new LinkedHashMap<String,Object>();
* map.put("column4_3", "10年以上");
* map.put("column5_4", "佔比%");
* map.put("column6_5", "5-10年");
* map.put("column7_6", "佔比%");
* map.put("column8_7", "3-5年");
* map.put("column9_8", "佔比%");
* map.put("column10_9", "3年以下");
* map.put("column11_10", "佔比%");
* listmap.add(map);
* //sql語句查詢的順序
* String[] colOrder={"nature","remark","mileage","tenMileage","tenZb","fiveToTenMileage","fiveToTenZb","threeToFiveMileage","threeToFiveZb","threeLessThanMileage","threeLessThanZb"};
* //可能需要做跨行合併的行,將某一列中相同內容的行進行合併
* String[] mergeCols={"nature"};
* exportXlsx(findYear+"年單位通車年限統計",listmap,list,mergeCols,colOrder);
* }
*
* private void exportXlsx(String fileName,List<Map<String, Object>> headListMap,List<Map<String, Object>> dataListMap,String[] mergeCols,String[] colOrder) {
* try {
* XSSFWorkbook wb = new XSSFWorkbook();
* Map<String,Object> map=new HashMap<String,Object>();
* XSSFSheet sheet1 = wb.createSheet("統計");
*
* //創建表頭
* ExcelNewUtil.createExcelHeader(wb, sheet1, headListMap);
* //填入表內容
* ExcelNewUtil.fillExcel(headListMap.size(),mergeCols,colOrder,wb,sheet1,dataListMap);
*
* response.setContentType("application/x-download; charset=utf-8");//
* response.setHeader("Content-disposition", "attachment; filename="+new String( (fileName+".xlsx").getBytes("gb2312"), "ISO8859-1" ));
* //導出
* wb.write(response.getOutputStream());
*
* response.getOutputStream().close();
*
* } catch (FileNotFoundException e) {
* e.printStackTrace();
* } catch (IOException e) {
* e.printStackTrace();
* }
*
* }
*
* */
/**
* Excel合併單元格的導出Tool
* @ClassName: ExcelNewUtil
* @Description: TODO(適合在需要做合併單元格的情況下,減少重複代碼。普通導出建議原方法導出)
* @author 周玉波
* @date 2018年7月10日 上午8:45:44
*
*/
public class ExcelNewUtil {
/**
* 設置表頭
* @param wb
* @param sheet
* @param headListMap (_*表示列頭從第幾列開始,默認0,_C*表示合併*列,_R*表示合併*行)
* CellRangeAddress(起始行號,結束行號,起始列號,結束列號)
* 合併行或列
*/
public static void createExcelHeader(XSSFWorkbook wb,XSSFSheet sheet,List<Map<String, Object>> headListMap){
sheet.setDefaultColumnWidth(7);
XSSFRow sr = null;
XSSFCell sc = null;
int j=0;//行
//遍歷表頭集合
for (int i = 0; i < headListMap.size(); i++) {
Map<String, Object> map=headListMap.get(i);
//創建行
sr = sheet.createRow(i);
sc = null;
int k=0;//列
int front=0;//上一次的單元格位置
//表頭
for (String key : map.keySet()) {
//如果是標題
if(key.startsWith("head")){
int r=0,c=0;
//判斷是否跨行或者跨列
if(key.indexOf("_R")>-1||key.indexOf("_C")>-1){
String[] keys=key.split("_");//分組
for (int l = 0; l < keys.length; l++) {
//跨行
if(keys[l].startsWith("R")){
//實際值從0開始,因此減一
r=Integer.parseInt(keys[l].replaceAll("R", ""))-1;
}
//跨列
else if(keys[l].startsWith("C")){
c=Integer.parseInt(keys[l].replaceAll("C", ""))-1;
}
}
}
//創建單元格
sc = sr.createCell(k);
sc.setCellValue(map.get(key).toString());
//合併列、合併行
sheet.addMergedRegion(new CellRangeAddress(0, r, 0, c));
//設置標題樣式
sc.setCellStyle(createHeaderStyle(wb));
//設置高度
sr.setHeight((short) 800);
}
//列頭
else{
int r=0,c=0;
//判斷是否跨行或者跨列
if(key.indexOf("_R")>-1||key.indexOf("_C")>-1){
String[] keys=key.split("_");//分組
for (int l = 0; l < keys.length; l++) {
//跨行
if(keys[l].startsWith("R")){
r=Integer.parseInt(keys[l].replaceAll("R", ""))-1;
}
//跨列
else if(keys[l].startsWith("C")){
c=Integer.parseInt(keys[l].replaceAll("C", ""))-1;
//跨列會讓跨過的單元格沒有邊框線,設置邊框線
setCellBorder(k+1,k+c,sr,createBorderStyle(wb));
//設置單元格寬度
sheet.setColumnWidth(k+c, 10 * 256);
}
}
}
//如果前面有跨行的列,第二行需要單元格位置,
//如上一行第一列爲column_R2,垮了兩行,下面那行如果有列,則需要設置列起始位置,如column_1(單元格位置從0開始),從第二列開始
else{
if(key.indexOf("_")>-1){
k=Integer.parseInt(key.split("_")[1]);
//判斷當前位置與前一次位置是否是連續的,非連續則證明中間有跨行的列,需要設置被跨行的單元格邊框線
if(k-front>1){
setCellBorder(front,k-1,sr,createBorderStyle(wb));
}
//賦值本次座標
front=k;
}
}
//創建單元格
sc = sr.createCell(k);
sc.setCellValue(map.get(key).toString());
//設置跨行或跨列
sheet.addMergedRegion(new CellRangeAddress(j, j+r, k, k+c));
//設置單元格寬度
sheet.setColumnWidth(k, 10 * 256);
//設置單元格高度
sr.setHeight((short) 600);
//設置單元格樣式
sc.setCellStyle(createBorderStyle(wb));
k=k+c;
k++;
}
}
j++;
}
}
private static String[] setRowStyle={"全省合計","全省總計","總計","小計"};//如果包含這些的行,字體樣式設爲粗體
/**
* 表內容
* @param startRow 開始行
* @param mergeCols 需要做跨行合併的列(數據庫查詢出的列名)
* @param colOrder 列順序(數據庫查詢出的列名)Map無序,需要設定排列順序
* @param wb
* @param sheet
* @param list 表內容集合
*/
public static void fillExcel(int startRow,String[] mergeCols,String[] colOrder,XSSFWorkbook wb,XSSFSheet sheet,List<Map<String,Object>> list) {
@SuppressWarnings("deprecation")
XSSFRow sr = null;
XSSFCell sc = null;
Map<String, Object> frontMap=null;//上一行的數據
JSONObject merge=new JSONObject();//需要合併的位置
String mer="";//記錄首次出現的行
for (int i = 0; i < list.size(); i++,startRow++) {
Map<String, Object> map=list.get(i);
//創建行
sr = sheet.createRow(startRow);
Boolean isBold=false;
for (int k = 0; k < colOrder.length; k++) {
//判斷是否有需要合併的行
if(mergeCols.length>0){
for (int j = 0, l=0; j < mergeCols.length; j++,l++) {
//相等
if(colOrder[k].equals(mergeCols[j])){
//判斷之前是否已經出現過
if(mer.indexOf(map.get(colOrder[k]).toString())==-1){
//創建單元格
sc = sr.createCell(k);
sc.setCellValue(map.get(colOrder[k]).toString());
//設置單元格樣式
sc.setCellStyle(createBorderStyle(wb));
if(!isBold){
if(ArrayUtils.contains(setRowStyle,map.get(colOrder[k]).toString())){
isBold=true;
sc.setCellStyle(createBorderBoldStyle(wb));
}
}else{
sc.setCellStyle(createBorderBoldStyle(wb));
}
mer+=map.get(colOrder[k]).toString()+",";//記錄
}else{
mer+=map.get(colOrder[k]).toString()+",";//記錄
}
//如果爲空,則代表這是第一行,否則需要判斷需要合併列值是否與上一次的值相同,相同則合併
if(frontMap!=null){
//判斷值相同
if(map.get(colOrder[k]).equals(frontMap.get(colOrder[k]))){
//如果merge對象中存在當前值,則修改結束行,其他不變。
if(merge.containsKey(map.get(colOrder[k]).toString())){
//取出之前值
String merges=merge.get(map.get(colOrder[k])).toString();
String[] arr=merges.split(",");
//修改結束行
merge.put(map.get(colOrder[k]).toString(), arr[0]+","+(startRow)+","+arr[2]+","+arr[2]);
}else{
merge.put(map.get(colOrder[k]).toString(), (startRow-1)+","+(startRow)+","+k+","+k);
}
}
}
break;
}else{
if(l==mergeCols.length-1){
if(map.get(colOrder[k])==null){
if(k>0){
sc = sr.getCell(k-1);
if(ArrayUtils.contains(setRowStyle,sc.getStringCellValue())){
sheet.addMergedRegion(new CellRangeAddress(startRow, startRow, k-1, k));
}
}
}else{
//創建單元格
sc = sr.createCell(k);
sc.setCellValue(map.get(colOrder[k])+"");
//設置單元格樣式
sc.setCellStyle(createBorderStyle(wb));
if(!isBold){
if(ArrayUtils.contains(setRowStyle,map.get(colOrder[k]).toString())){
isBold=true;
sc.setCellStyle(createBorderBoldStyle(wb));
}
}else{
sc.setCellStyle(createBorderBoldStyle(wb));
}
}
}
}
}
}else{
//創建單元格
sc = sr.createCell(k);
sc.setCellValue(map.get(colOrder[k]).toString());
//設置單元格樣式
sc.setCellStyle(createBorderStyle(wb));
if(!isBold){
if(ArrayUtils.contains(setRowStyle,map.get(colOrder[k]).toString())){
isBold=true;
sc.setCellStyle(createBorderBoldStyle(wb));
}
}else{
sc.setCellStyle(createBorderBoldStyle(wb));
}
}
}
frontMap=list.get(i);
}
//遍歷merge對象,該合併的合併
for (String key : merge.keySet()) {
String merges=merge.get(key).toString();
String[] arr=merges.split(",");
//設置跨行或跨列
sheet.addMergedRegion(new CellRangeAddress(Integer.parseInt(arr[0]), Integer.parseInt(arr[1]), Integer.parseInt(arr[2]), Integer.parseInt(arr[3])));
for (int j = Integer.parseInt(arr[0])+1; j <= Integer.parseInt(arr[1]); j++) {
setCellBorder(Integer.parseInt(arr[2]),Integer.parseInt(arr[3]),sheet.getRow(j),createBorderStyle(wb));
}
}
}
/**
* 設置單元格
* @param wb
* @return
*/
public static CellStyle createAlignStyle(XSSFWorkbook wb){
CellStyle style = wb.createCellStyle();
style.setWrapText(true);
style.setAlignment(XSSFCellStyle.ALIGN_CENTER); //字體居中
style.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);//垂直居中
return style;
}
/**
* 設置邊框樣式
* @param wb
* @return
*/
public static CellStyle createBorderStyle(XSSFWorkbook wb){
CellStyle style = wb.createCellStyle();
style.setWrapText(true);
style.setBorderBottom(XSSFCellStyle.BORDER_THIN); //下邊框
style.setBorderLeft(XSSFCellStyle.BORDER_THIN);//左邊框
style.setBorderTop(XSSFCellStyle.BORDER_THIN);//上邊框
style.setBorderRight(XSSFCellStyle.BORDER_THIN);//右邊框
style.setAlignment(XSSFCellStyle.ALIGN_CENTER); //字體居中
style.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);//垂直居中
XSSFFont font = wb.createFont();
font.setFontName("宋體");
font.setFontHeightInPoints((short) 10);//設置字體大小
style.setFont(font);
return style;
}
/**
* 設置邊框樣式 粗體
* @param wb
* @return
*/
public static CellStyle createBorderBoldStyle(XSSFWorkbook wb){
CellStyle style = wb.createCellStyle();
style.setWrapText(true);
style.setBorderBottom(XSSFCellStyle.BORDER_THIN); //下邊框
style.setBorderLeft(XSSFCellStyle.BORDER_THIN);//左邊框
style.setBorderTop(XSSFCellStyle.BORDER_THIN);//上邊框
style.setBorderRight(XSSFCellStyle.BORDER_THIN);//右邊框
style.setAlignment(XSSFCellStyle.ALIGN_CENTER); //字體居中
style.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);//垂直居中
XSSFFont font = wb.createFont();
font.setFontName("宋體");
font.setFontHeightInPoints((short) 10);//設置字體大小
font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//粗體顯示
style.setFont(font);
return style;
}
/**
* 設置邊框
* @param start 開始列
* @param end 結束列
* @param row 行
* @param style
*/
public static void setCellBorder(int start, int end, XSSFRow row, CellStyle style) {
for(int i=start;i<=end;i++){
XSSFCell cell = row.createCell(i);
cell.setCellValue("");
cell.setCellStyle(style);
}
}
/**
* 設置邊框
* @param start 開始列
* @param end 結束列
* @param row 行
* @param style
*/
public static void setCellBorder1(int start, int end, XSSFRow row, CellStyle style) {
for(int i=end+1;i>=start;i--){
XSSFCell cell = row.createCell(i);
cell.setCellValue("");
cell.setCellStyle(style);
}
}
/**
* 設置邊框
* @param start 開始列
* @param end 結束列
* @param row 行
* @param val 值
* @param style
*/
public static void setCellBorder(int i, XSSFRow row, CellStyle style, String val) {
XSSFCell cell = row.createCell(i);
cell.setCellValue(val);
cell.setCellStyle(style);
}
/**
* 創建頭部樣式
* @param wb
* @return
*/
public static CellStyle createHeaderStyle(XSSFWorkbook wb){
CellStyle style = wb.createCellStyle();
/*style.setBorderBottom(XSSFCellStyle.BORDER_THIN); //下邊框
style.setBorderLeft(XSSFCellStyle.BORDER_THIN);//左邊框
style.setBorderTop(XSSFCellStyle.BORDER_THIN);//上邊框
style.setBorderRight(XSSFCellStyle.BORDER_THIN);//右邊框
*/ style.setAlignment(XSSFCellStyle.ALIGN_CENTER); //字體居中
style.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);//垂直
XSSFFont font = wb.createFont();
font.setFontName("黑體");
font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//粗體顯示
font.setFontHeightInPoints((short) 16);//設置字體大小
style.setFont(font);
return style;
}
/**
* 創建頭部樣式
* @param wb
* @param size 字體大小
* @return
*/
public static CellStyle createHeaderStyle(XSSFWorkbook wb,int size){
CellStyle style = wb.createCellStyle();
/*style.setBorderBottom(XSSFCellStyle.BORDER_THIN); //下邊框
style.setBorderLeft(XSSFCellStyle.BORDER_THIN);//左邊框
style.setBorderTop(XSSFCellStyle.BORDER_THIN);//上邊框
style.setBorderRight(XSSFCellStyle.BORDER_THIN);//右邊框
*/ style.setAlignment(XSSFCellStyle.ALIGN_CENTER); //字體居中
style.setVerticalAlignment(XSSFCellStyle.VERTICAL_CENTER);//垂直
XSSFFont font = wb.createFont();
font.setFontName("黑體");
font.setBoldweight(XSSFFont.BOLDWEIGHT_BOLD);//粗體顯示
font.setFontHeightInPoints((short) size);//設置字體大小
style.setFont(font);
return style;
}
}