Apache POI中文列宽自动调整不准确的解决方案咨询
解决Apache POI中文列宽自动调整不准确的问题
一、问题根源
Apache POI默认的autoSizeColumn方法是基于西文字符宽度计算的,而中文(及其他东亚字符)的宽度通常是西文字符的2倍左右,直接调用会导致中文列宽偏窄,显示不全。
二、精准调整中文列宽的方法
1. 自定义列宽计算逻辑
手动遍历单元格内容,根据实际使用的字体计算文本宽度,再转换为POI的列宽单位(1/256个默认字符宽度),这种方法适配性最强。
示例代码(以XSSF为例):
import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFFont; import java.awt.*; import java.util.Map; import java.util.HashMap; public class ColumnWidthUtil { // 缓存字体宽度,避免重复计算 private static final Map<String, Float> FONT_WIDTH_CACHE = new HashMap<>(); public static void autoSizeColumnWithChinese(Sheet sheet, int columnIndex, Workbook workbook) { DataFormatter formatter = new DataFormatter(); // 定义默认字体(替换为你表格实际使用的字体和字号) XSSFFont defaultFont = (XSSFFont) workbook.createFont(); defaultFont.setFontName("微软雅黑"); defaultFont.setFontHeightInPoints((short) 11); int maxWidth = 0; for (Row row : sheet) { Cell cell = row.getCell(columnIndex); if (cell == null) continue; // 获取单元格文本内容 String cellText = formatter.formatCellValue(cell); // 获取单元格使用的字体,无自定义字体则用默认 Font font = getAwtFontFromCell(cell, defaultFont); // 计算文本像素宽度,转换为POI列宽单位并加边距 float textPixelWidth = Toolkit.getDefaultToolkit().getFontMetrics(font).stringWidth(cellText); float baseCharWidth = getBaseCharWidth(font); int columnWidth = (int) (textPixelWidth / baseCharWidth * 256 + 100); if (columnWidth > maxWidth) { maxWidth = columnWidth; } } // 限制最大列宽(POI列宽上限为255*256) if (maxWidth > 255 * 256) { maxWidth = 255 * 256; } sheet.setColumnWidth(columnIndex, maxWidth); } private static Font getAwtFontFromCell(Cell cell, XSSFFont defaultFont) { CellStyle style = cell.getCellStyle(); XSSFFont poiFont = (XSSFFont) style.getFont(); // 转换为AWT字体用于计算宽度 int styleFlag = Font.PLAIN; if (poiFont.getBold()) styleFlag |= Font.BOLD; if (poiFont.getItalic()) styleFlag |= Font.ITALIC; return new Font(poiFont.getFontName(), styleFlag, poiFont.getFontHeightInPoints()); } private static float getBaseCharWidth(Font font) { String cacheKey = font.getName() + "-" + font.getSize() + "-" + font.getStyle(); if (!FONT_WIDTH_CACHE.containsKey(cacheKey)) { FontMetrics metrics = Toolkit.getDefaultToolkit().getFontMetrics(font); FONT_WIDTH_CACHE.put(cacheKey, (float) metrics.charWidth('A')); } return FONT_WIDTH_CACHE.get(cacheKey); } }
使用方式:
// 对中文列调用自定义方法 ColumnWidthUtil.autoSizeColumnWithChinese(sheet, 0, workbook); // 对纯英文/数字列用POI默认方法 sheet.autoSizeColumn(1);
2. 分批次适配不同类型列
针对你部分列含中文、部分不含的场景,可先判断列内容类型,再分别处理:
- 判断列是否含中文的工具方法:
private static boolean columnHasChinese(Sheet sheet, int columnIndex) { DataFormatter formatter = new DataFormatter(); for (Row row : sheet) { Cell cell = row.getCell(columnIndex); if (cell == null) continue; String text = formatter.formatCellValue(cell); if (text.matches(".*[\u4e00-\u9fa5].*")) { return true; } } return false; }
- 批量处理所有列:
int totalColumns = sheet.getRow(0).getLastCellNum(); for (int i = 0; i < totalColumns; i++) { if (columnHasChinese(sheet, i)) { ColumnWidthUtil.autoSizeColumnWithChinese(sheet, i, workbook); } else { sheet.autoSizeColumn(i); } }
内容的提问来源于stack exchange,提问作者user23494860
相关产品推荐
相关产品推荐

