实际使用中,多数情况是使用Map读和写
自定义行宽
用easyExcel中的自动列宽很不好看,最后的导出单元格会非常宽,所以
- 可以读取标题宽度,然后去动态设置每一列的宽度,并设置一个最大值保证不会太长
- 或者根据导出的数据,设置一个固定的宽度,然后让数据自动换行(推荐这样)
动态设置列宽
需要自定义一个拦截器,继承AbstractColumnWidthStyleStrategy
通过单元格数据计算宽度并缓存起来
package com.gwssi.tjj.xmgl.pm.util;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Author shixiaohao
* @Date 2020/10/28 18:18
* @Description: CustomHandler
*/
public class CustomHandler extends AbstractColumnWidthStyleStrategy {
private static final int MAX_COLUMN_WIDTH = 200;
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);
public CustomHandler() {
}
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
Map<Integer, Integer> maxColumnWidthMap = (Map)CACHE.get(writeSheetHolder.getSheetNo());
if (maxColumnWidthMap == null) {
maxColumnWidthMap = new HashMap(16);
CACHE.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap);
}
Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
if (columnWidth >= 0) {
if (columnWidth > 255) {
columnWidth = 255;
}
Integer maxColumnWidth = (Integer)((Map)maxColumnWidthMap).get(cell.getColumnIndex());
if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
((Map)maxColumnWidthMap).put(cell.getColumnIndex(), columnWidth);
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
}
}
}
}
private Integer dataLength(List<CellData> cellDataList, Cell cell, Boolean isHead) {
if (isHead) {
return cell.getStringCellValue().getBytes().length;
} else {
CellData cellData = (CellData)cellDataList.get(0);
CellDataTypeEnum type = cellData.getType();
if (type == null) {
return -1;
} else {
switch(type) {
case STRING:
return cellData.getStringValue().getBytes().length;
case BOOLEAN:
return cellData.getBooleanValue().toString().getBytes().length;
case NUMBER:
return cellData.getNumberValue().toString().getBytes().length;
default:
return -1;
}
}
}
}
}
设置固定列宽
动态设置列宽有时候依然不好用,可以将列宽设置为固定宽度,然后让内容自动换行,导出的样式会比较整洁
使用:.registerWriteHandler((new CustomHandler()))
进行注册使用即可
/**
* @Author shixiaohao
* @Date 2020/10/28 18:18
* @Description: CustemHandler
*/
public class CustomHandler extends AbstractColumnWidthStyleStrategy {
private static final int MAX_COLUMN_WIDTH = 3000;
private Map<Integer, Map<Integer, Integer>> CACHE = new HashMap(8);
public CustomHandler() {
}
@Override
protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> cellDataList, Cell cell, Head head, Integer integer, Boolean isHead) {
boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
if (needSetWidth) {
// 所有是head的cell设置固定的宽度 MAX_COLUMN_WIDTH
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), MAX_COLUMN_WIDTH);
// 设置默认行高
// writeSheetHolder.getCachedSheet().setDefaultRowHeightInPoints();
}
}
}
自定义样式
标题样式
public static WriteCellStyle getHeadStyle(){
// 头的策略
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
// 背景颜色
headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
headWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 字体
WriteFont headWriteFont = new WriteFont();
headWriteFont.setFontName("黑体");//设置字体名字
headWriteFont.setFontHeightInPoints((short)12);//设置字体大小
headWriteFont.setBold(true);//字体加粗
headWriteCellStyle.setWriteFont(headWriteFont); //在样式用应用设置的字体;
// 样式
headWriteCellStyle.setBorderBottom(BorderStyle.THIN);//设置底边框;
headWriteCellStyle.setBottomBorderColor((short) 0);//设置底边框颜色;
headWriteCellStyle.setBorderLeft(BorderStyle.THIN); //设置左边框;
headWriteCellStyle.setLeftBorderColor((short) 0);//设置左边框颜色;
headWriteCellStyle.setBorderRight(BorderStyle.THIN);//设置右边框;
headWriteCellStyle.setRightBorderColor((short) 0);//设置右边框颜色;
headWriteCellStyle.setBorderTop(BorderStyle.THIN);//设置顶边框;
headWriteCellStyle.setTopBorderColor((short) 0); //设置顶边框颜色;
headWriteCellStyle.setWrapped(true); //设置自动换行;
headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);//设置水平对齐的样式为居中对齐;
headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); //设置垂直对齐的样式为居中对齐;
// headWriteCellStyle.setShrinkToFit(true);//设置文本收缩至合适
return headWriteCellStyle;
}
内容样式
public static WriteCellStyle getContentStyle(){
// 内容的策略
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
// 背景绿色
// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
// 设置字体
WriteFont contentWriteFont = new WriteFont();
contentWriteFont.setFontHeightInPoints((short) 9);//设置字体大小
contentWriteFont.setFontName("宋体"); //设置字体名字
contentWriteCellStyle.setWriteFont(contentWriteFont);//在样式用应用设置的字体;
//设置样式;
contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);//设置底边框;
contentWriteCellStyle.setBottomBorderColor((short) 0);//设置底边框颜色;
contentWriteCellStyle.setBorderLeft(BorderStyle.THIN); //设置左边框;
contentWriteCellStyle.setLeftBorderColor((short) 0);//设置左边框颜色;
contentWriteCellStyle.setBorderRight(BorderStyle.THIN);//设置右边框;
contentWriteCellStyle.setRightBorderColor((short) 0);//设置右边框颜色;
contentWriteCellStyle.setBorderTop(BorderStyle.THIN);//设置顶边框;
contentWriteCellStyle.setTopBorderColor((short) 0); ///设置顶边框颜色;
contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 水平居中
contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直居中
contentWriteCellStyle.setWrapped(true); //设置自动换行;
contentWriteCellStyle.setDataFormat((short)49);//设置单元格格式是:文本格式,方式长数字文本科学计数法
// contentWriteCellStyle.setShrinkToFit(true);//设置文本收缩至合适
return contentWriteCellStyle;
}
easyExcel设置单元格格式:传一个short类型的下标,从内建格式中选择需要设置的格式
防止长数字字符串自动科学计数法
下面是poi内建格式:BuiltinFormats中定义的静态数组,初始化了50个格式
private static final String[] _formats = new String[]{
"General",
"0",
"0.00",
"#,##0",
"#,##0.00",
"\"$\"#,##0_);(\"$\"#,##0)",
"\"$\"#,##0_);[Red](\"$\"#,##0)",
"\"$\"#,##0.00_);(\"$\"#,##0.00)",
"\"$\"#,##0.00_);[Red](\"$\"#,##0.00)",
"0%", "0.00%", "0.00E+00",
"# ?/?", "# ??/??",
"m/d/yy", "d-mmm-yy",
"d-mmm", "mmm-yy",
"h:mm AM/PM",
"h:mm:ss AM/PM",
"h:mm", "h:mm:ss",
"m/d/yy h:mm",
"reserved-0x17",
"reserved-0x18",
"reserved-0x19",
"reserved-0x1A",
"reserved-0x1B",
"reserved-0x1C",
"reserved-0x1D",
"reserved-0x1E",
"reserved-0x1F",
"reserved-0x20",
"reserved-0x21",
"reserved-0x22",
"reserved-0x23",
"reserved-0x24",
"#,##0_);(#,##0)",
"#,##0_);[Red](#,##0)",
"#,##0.00_);(#,##0.00)",
"#,##0.00_);[Red](#,##0.00)",
"_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",
"_(\"$\"* #,##0_);_(\"$\"* (#,##0);_(\"$\"* \"-\"_);_(@_)",
"_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",
"_(\"$\"* #,##0.00_);_(\"$\"* (#,##0.00);_(\"$\"* \"-\"??_);_(@_)",
"mm:ss",
"[h]:mm:ss",
"mm:ss.0",
"##0.0E+0",
"@" // 文本格式
}
使用样式
private void writeDataToExcelCustomize(String path, List<List<String>> head, List<List<Object>> data) throws OptimusException {
File dir = new File(path);
if (!dir.exists()) {
boolean mkdirs = dir.mkdirs();
if (!mkdirs) {
throw new OptimusException("文件夹创建失败");
}
}
String filePath = path + System.currentTimeMillis() + ".xlsx";
// 标题样式
WriteCellStyle headWriteCellStyle = StyleUtils.getHeadStyle();
// 内容样式
WriteCellStyle contentWriteCellStyle = StyleUtils.getContentStyle();
// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
HorizontalCellStyleStrategy horizontalCellStyleStrategy =
new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(filePath).head(head).registerWriteHandler(horizontalCellStyleStrategy).sheet("Data")
.doWrite(data);
}
*注:
- 如果存在多个自定义的writeHandler的话,同时注册可能会出现部分自定义的拦截器没有起效果,目前看来是因为拦截器之间有优先执行和覆盖的关系(比方说自定义了样式,又在其他拦截器修改了某个单元格样式、单元格格式,有时候不起作用)
- 所以尽量每个拦截器功能单一/尽量少的拦截器