@Override public void run() { Long startTime = System.currentTimeMillis(); ExcelWriter excelWriter = null; InputStream inputStream = null; StringBuilder fileIsExitSB = new StringBuilder(); fileIsExitSB.append("oriFile: ").append(excelResult.getFilePath()); fileIsExitSB.append("errFile:").append(excelResult.getErrorFilePath()).append("; "); try { // 追踪文件是否存在 fileLog(fileIsExitSB,"start"); FileUtil.copy(excelResult.getFilePath(), excelResult.getErrorFilePath(), true); fileLog(fileIsExitSB,"copy"); logger.info("错误导出-复制错误文件:{}",System.currentTimeMillis()-startTime); byte[] errorTemplateBytes = EasyExcelUtil.createErrorTemplate(excelResult.getErrorFilePath(),excelResult.getTableName()); fileLog(fileIsExitSB,"createErrorTemplate"); logger.info("错误导出-生成错误模版:{}",System.currentTimeMillis()-startTime); inputStream = new ByteArrayInputStream(errorTemplateBytes); excelWriter = EasyExcel.write(excelResult.getErrorFilePath()).withTemplate(inputStream).inMemory(Boolean.FALSE).build(); fileLog(fileIsExitSB,"write"); // 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了 WriteSheet writeSheet = EasyExcel.writerSheet(0).needHead(Boolean.FALSE).build(); while (true) { List<RowData> validateRowList = validateBlockingQueue.take(); if (CollectionUtil.isEmpty(validateRowList)) { break; } //强制计算所有公式(可选) Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook(); workbook.setForceFormulaRecalculation(true); // 确保批注刷新(可选) Sheet sheet = workbook.getSheetAt(0); sheet.getDrawingPatriarch(); // 触发批注重新渲染 List<Map<String, Object>> rowList = EasyExcelUtil.getErrorExcelRowList(validateRowList); excelWriter.fill(rowList, writeSheet); // 记录导出数据量,并回调 excelResult.setErrorExportCount(excelResult.getErrorExportCount() + rowList.size()); callback.apply(excelResult); logger.info("错误导出-导出数据-{}:{}",excelResult.getErrorExportCount(),System.currentTimeMillis()-startTime); } fileLog(fileIsExitSB,"end"); } catch (InterruptedException e) { logger.error("错误导出-写入错误excel错误", e); throw new RuntimeException(e); } finally { // 更新追踪文件是否存在记录 fileLog(fileIsExitSB,"finally"); excelResult.setFileIsExitStr(fileIsExitSB.toString()); callback.apply(excelResult); IoUtil.close(inputStream); IoUtil.close(excelWriter); // 更新错误文件大小 updateFileSize(); countDownLatch.countDown(); logger.info("错误导出-结束:{}", System.currentTimeMillis() - startTime); } } 现在的问题是批注不显示,其他列批注显示正常,公式列的批注不会正常显示,应该怎么改
时间: 2025-07-20 22:21:26 浏览: 4
<think>我们使用EasyExcel时,如果写入的列包含公式,同时想为这些公式单元格添加批注,可能会遇到批注不显示的问题。这通常是由于公式未计算导致单元格值未更新,或者批注未被正确触发渲染。以下是一些解决方案:
### 1. 强制公式计算
在写入Excel后,我们需要确保公式被计算,这样在打开文件时公式结果才会显示。对于EasyExcel,我们可以通过以下方式设置工作簿在打开时强制重新计算所有公式:
```java
ExcelWriter excelWriter = EasyExcel.write(fileName).build();
// ... 其他写入操作
// 获取Workbook对象
Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
// 设置强制公式重新计算
workbook.setForceFormulaRecalculation(true);
```
### 2. 触发批注重新渲染
有时即使添加了批注,但由于Excel的渲染机制,批注可能不会立即显示。我们可以通过获取工作表的绘图容器(DrawingPatriarch)来触发批注的重新渲染:
```java
Sheet sheet = workbook.getSheetAt(0);
// 触发批注重新渲染
if (sheet.getDrawingPatriarch() == null) {
sheet.createDrawingPatriarch();
}
```
### 3. 使用自定义WriteHandler添加批注
EasyExcel允许我们通过注册WriteHandler来自定义写入过程。我们可以创建一个WriteHandler,在单元格写入完成后添加批注。以下是一个示例:
```java
public class CommentWriteHandler implements CellWriteHandler {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
// 判断是否为公式单元格,并且是我们要添加批注的列
if (cell.getCellType() == CellType.FORMULA) {
Sheet sheet = context.getWriteSheetHolder().getSheet();
Drawing<?> drawing = sheet.getDrawingPatriarch();
if (drawing == null) {
drawing = sheet.createDrawingPatriarch();
}
CreationHelper helper = sheet.getWorkbook().getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex() + 2);
anchor.setRow1(cell.getRowIndex());
anchor.setRow2(cell.getRowIndex() + 4);
Comment comment = drawing.createCellComment(anchor);
RichTextString str = helper.createRichTextString("公式计算结果");
comment.setString(str);
cell.setCellComment(comment);
}
}
}
```
然后在写入时注册这个处理器:
```java
EasyExcel.write(fileName, DemoData.class)
.registerWriteHandler(new CommentWriteHandler())
.sheet("Sheet1")
.doWrite(data);
```
### 4. 注意公式列的数据类型
确保公式列在Java实体类中定义为字符串类型,因为公式本身是一个字符串。例如:
```java
public class DemoData {
@ExcelProperty("公式列")
private String formula;
// 其他字段...
}
```
然后在写入数据时,将公式字符串赋值给该字段,注意公式应以`=`开头,例如:`cellValue.setFormula("=A1+B1")`。
### 5. 使用最新版本的EasyExcel
确保你使用的EasyExcel版本是最新的,因为旧版本可能存在一些已知问题。
### 总结
通过以上步骤,我们可以在写入公式列的同时成功添加批注:
1. 设置工作簿强制重新计算公式
2. 触发批注重新渲染
3. 使用自定义WriteHandler在写入单元格时添加批注
4. 确保公式列的数据类型正确
这样在打开Excel文件时,公式会被计算,批注也会正常显示。
§§相关问题§§
1. 如何在EasyExcel中为不同的单元格动态添加不同的批注?
2. EasyExcel中如何设置批注的样式(如字体、颜色)?
3. 使用EasyExcel导出包含公式的Excel文件时,如何避免公式计算错误?
4. EasyExcel中如何读取单元格的批注内容?</think>### 解决 EasyExcel 公式列批注不显示问题
在 EasyExcel 中为公式列添加批注时,批注不显示通常由两个核心原因导致:
1. **公式未及时计算**:公式结果未更新导致批注无法关联到有效值
2. **批注渲染机制问题**:Excel 的批注渲染需要特殊触发
#### ✅ 完整解决方案
```java
// 1. 创建自定义批注处理器
public class FormulaCommentHandler implements CellWriteHandler {
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
if (cell.getCellType() == CellType.FORMULA) {
Sheet sheet = context.getWriteSheetHolder().getSheet();
Workbook workbook = sheet.getWorkbook();
// 2. 强制公式计算
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateFormulaCell(cell); // 计算当前单元格公式
// 3. 添加批注
Drawing<?> drawing = sheet.getDrawingPatriarch();
if (drawing == null) drawing = sheet.createDrawingPatriarch();
CreationHelper helper = workbook.getCreationHelper();
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setCol2(cell.getColumnIndex() + 1);
anchor.setRow1(cell.getRowIndex());
anchor.setRow2(cell.getRowIndex() + 3);
Comment comment = drawing.createCellComment(anchor);
RichTextString str = helper.createRichTextString("公式计算结果: " + cell.getNumericCellValue());
comment.setString(str);
cell.setCellComment(comment);
// 4. 强制刷新批注
sheet.getDrawingPatriarch(); // 关键:触发批注渲染
}
}
}
// 5. 在导出时注册处理器
ExcelWriter excelWriter = EasyExcel.write(fileName, YourDataClass.class)
.registerWriteHandler(new FormulaCommentHandler()) // 注册批注处理器
.build();
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
excelWriter.write(dataList, writeSheet);
// 6. 设置全局公式重算
Workbook workbook = excelWriter.writeContext().writeWorkbookHolder().getWorkbook();
workbook.setForceFormulaRecalculation(true); // 强制Excel打开时重算公式
excelWriter.finish();
```
#### 关键点解析
1. **公式计算时机**:
- 使用 `FormulaEvaluator.evaluateFormulaCell()` 在写入时立即计算公式结果
- 设置 `workbook.setForceFormulaRecalculation(true)` 确保Excel打开时重算[^1]
2. **批注渲染触发**:
```java
sheet.getDrawingPatriarch(); // 强制触发批注渲染引擎
```
这行代码看似无意义,实则是触发Excel内部批注渲染的关键[^1]
3. **批注定位技巧**:
```java
ClientAnchor anchor = helper.createClientAnchor();
anchor.setCol1(cell.getColumnIndex());
anchor.setRow1(cell.getRowIndex());
```
精确定位批注显示位置,避免覆盖其他单元格
4. **动态批注内容**:
```java
"公式计算结果: " + cell.getNumericCellValue()
```
直接获取公式计算结果展示在批注中
#### 补充建议
1. **版本兼容性**:使用 EasyExcel ≥ 3.3.2 版本(修复了公式相关渲染问题)
2. **批注样式优化**:如需美化批注,可扩展处理器:
```java
comment.setAuthor("系统计算");
// 设置背景色(仅XSSF支持)
if(comment instanceof XSSFComment) {
((XSSFComment)comment).setFillColor(204, 255, 204);
}
```
3. **大批量处理**:当处理10万+单元格时,建议增加批注过滤条件,避免性能问题
> **特殊场景**:若公式引用其他未计算单元格,需先调用 `evaluator.evaluateAll()` 全量计算工作簿[^3]
阅读全文
相关推荐


















