文章目录
场景
导出的数值单元格格式是文本。 客户每次都要手动转为数值,往往一个表格就是好多万数据,转换起来等半天。
解决方案
听说@Excel 设置type=10即可。 实测无效。
设置type=10对不对
这样设置是对的。 但是为什么没有效果呢?
仔细跟代码,发现type=10,会设置单元格的type为 Cell.CELL_TYPE_NUMERIC 。 单元格的值属于CellValue ,和CelType都是属于Cell 。
但是单元格属性 属于 CellStyle。
所以虽然值的输出符合规范了,但是单元格显示还是为文本。
导出用到的主要元素和依赖关系
使用easypoi 导出excel只用简单的一个方法。
ExcelExportUtil.exportExcel(params, StatisticEntity.class, list);
主要元素列表如下:
名称 | 描述 |
---|---|
ExportParams | 定义导出的文件名,中文名,文件类型,导出样式等。 |
ExcelExportStatisticStyler | 继承自ExcelExportStylerDefaultImpl类,定义样式。 通过params.setStyle(ExcelExportStatisticStyler.class); 设置到ExportParams中。 如果不指定,默认使用ExcelExportStylerDefaultImpl |
StatisticEntity | 导出单元格的实体,这里可以设置数据规则 |
List<StatisticEntity> list = new ArrayList<StatisticEntity>(); // TODO list中添加数据根据情况自己写
ExportParams params = new ExportParams("2412312", "测试", ExcelType.XSSF);
params.setStyle(ExcelExportStatisticStyler.class);
Workbook workbook = ExcelExportUtil.exportExcel(params, StatisticEntity.class, list);
打造属于自己的styler
好的,既然ExportParams 可以setStyle,那么我们写个类,继承ExcelExportStylerDefaultImpl 重写 getStyles方法不就成了么。
错误的写法
思路,依照最小改动原则,先获取已有的style,然后添加type=10的处理逻辑。代码:
@Override
public CellStyle getStyles2(boolean noneStyler, ExcelExportEntity entity) {
CellStyle styles = super.getStyles(noneStyler, entity); // 获取style
// type=10 的处理
if (entity != null && 10==entity.getType()) {
styles.setDataFormat((short) BuiltinFormats.getBuiltinFormat("0.00"));
return styles;
}
return styles;
}
测了下发现报错, 是因为子类重写getStyles()方法,super.getStyles()调的就是子类的getStyles()方法。 这不无限循环了么。
重新写逻辑
代码:
@Override
public CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity) {
if (entity != null
&& 10==entity.getType()) {
return numberCellStyle;
}
return super.getStyles(noneStyler, entity);
}
实测成功。
导出的excel单元格格式为 自定义 0.00 。这还不是客户要求的数值格式。但是只能做到这了。
至少我目前没找到如何设置为数值格式。
ExcelExportStatisticStyler 类完整的代码
public class ExcelExportStatisticStyler extends ExcelExportStylerDefaultImpl {
private CellStyle numberCellStyle;
public ExcelExportStatisticStyler(Workbook workbook) {
super(workbook);
createNumberCellStyler();
}
private void createNumberCellStyler() {
numberCellStyle = workbook.createCellStyle();
numberCellStyle.setAlignment(HorizontalAlignment.CENTER);
numberCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
numberCellStyle.setDataFormat((short) BuiltinFormats.getBuiltinFormat("0.00"));
numberCellStyle.setWrapText(true);
}
@Override
public CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity) {
if (entity != null
&& 10==entity.getType()) {
return numberCellStyle;
}
return super.getStyles(noneStyler, entity);
}
}
setDataFormat有2种方式入参形式
1、字符串格式(推荐,可读性更强)
2、_formats 数组的下标
两种方式,本质上没区别, 传入string也是根据字面值,找_formats对应的下标。
见BuiltinFormats源码 (强调 – 自定义格式一定要在_formats 数组内,否则无效):
public static int getBuiltinFormat(String pFmt) {
String fmt = "TEXT".equalsIgnoreCase(pFmt) ? "@" : pFmt;
int i = -1;
for (String f : _formats) {
i++;
if (f.equals(fmt)) {
return i;
}
}
return -1;
}
两种设置的例子:
// 使用字符串定义格式
cellStyle.setDataFormat((short) BuiltinFormats.getBuiltinFormat("0.00"));
// BuiltinFormats._formats 数组中的下标
cellStyle.setDataFormat((short) 1);
解决总结
步骤:
@Excel 中添加 type=10
写个类继承ExcelExportStylerDefaultImpl ,重写getStyles()方法
导出前ExportParams。
既然重写了getStyles(),那么其实type=10并不是唯一的办法。
官网就是根据name中是否包含int,double等类型来判断的,当然@Excel中的name要记得配合。(感觉type=10比改name更方便,更解耦)
setAlignment(HorizontalAlignment.CENTER) 报错
版本的问题。 报错代码如下:
numberCellStyle.setAlignment(HorizontalAlignment.CENTER);
numberCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
调整为如下代码即可:
numberCellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);
numberCellStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
debug跟代码一行一行看
ExcelExportBase类中的createCells方法中,如果类型为double,则进入如下逻辑:
else if (entity.getType() == BaseEntityTypeConstants.DoubleType) {
}
ExcelExportBase类中的createDoubleCell方法(type=10时设置了cellType):
public void createDoubleCell(Row row, int index, String text, CellStyle style,ExcelExportEntity entity) {
Cell cell = row.createCell(index);
if (text != null && text.length() > 0) {
cell.setCellValue(Double.parseDouble(text));
} else {
cell.setCellValue(-1);
}
cell.setCellType(Cell.CELL_TYPE_NUMERIC); // 这里是设置了celltype为0,表示数字格式
if (style != null) { // 这里设置style
cell.setCellStyle(style);
}
addStatisticsData(index, text, entity);
}
是不是type不影响页面excel的文本格式,而是由style控制的呢。
easypoi支持的自定义格式列表
BuiltinFormats类的_formats列表里的自定义格式才有效,否则就会使用文本格式。
private final static String[] _formats = {
"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",
// 0x17 - 0x24 reserved for international and undocumented
// TODO - one junit relies on these values which seems incorrect
"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",
"@"
};