Java excel合併單元格的導出公共類,避免每次畫表格

之前在網上找的方法都得一個表格一個表格的去畫,每次寫一個導出都要花費很多時間。
最近又要做合併單元格,爲防止以後再去寫那些重複代碼,
於是我就寫了個能滿足大部分合並單元格導出需求的方法。
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;
	}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章