首页  编辑  

POI导出Excel表格的时候自适应宽度和高度

Tags: /Java/   Date Created:
Apache POI 组件,导出Excel表格的时候,默认情况下是不会自动调整列宽、行高的,如果需要根据内容自动调整宽度和高度,可以用下面的代码:
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.util.CellRangeAddress;
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;
public class POIExcelUtil {
   private POIExcelUtil() {
       throw new IllegalStateException("Utility class");
     }
   //1POINT= 20TWIPS
   private static final Float POINT_TO_TWIPS = 20.0F;
   //Excel default height
   private static final Float DEFAULT_EXCEL_ROW_HEIGHT =  (float) 24;
   //Excel String: Upper and lower white space
   private static final int UPPER_AND_LOWER_WHITE_SPACE = 8;
  
   /**
    * The width of Sheet column in POI is determined by the number of characters, and the unit of column width is 1/256 of the width of one character.
    * According to the font and pixel parameters are set 256 after many tests.
    */
   private static final int MAX_CHARACTER_FOR_COLUMN_WIDTH =  256;
  
   private static final String LINE_BREAKS_STR =  "\\r?\\n";
  
   /**
    * Adjust Row Heights: Adjust the row height based on the cell and cell contents
    * @param sourceRow
    */
   public static void adjustRowHeights(XSSFRow sourceRow) {
       if(sourceRow == null) return ;
       double maxCellHeight = 0;
       for (int cellIndex = sourceRow.getFirstCellNum(); cellIndex <= sourceRow.getPhysicalNumberOfCells(); cellIndex++) {
           if (cellIndex < 0 || sourceRow.getCell(cellIndex) == null || StringUtils.isBlank(getCellContentAsString(sourceRow.getCell(cellIndex)))){
               continue;
           }
           XSSFCell sourceCell = sourceRow.getCell(cellIndex);
           String cellContent = getCellContentAsString(sourceCell);
           //Cell width and cell info
           Map<String, Object> cellInfoMap = getCellInfo(sourceCell);
           Integer cellWidth = (Integer) cellInfoMap.get("width");
           Integer cellHeight = (Integer) cellInfoMap.get("height");
           XSSFCellStyle cellStyle = sourceCell.getCellStyle();
           XSSFFont font = cellStyle.getFont();
           //Cell font height
           short fontHeight = font.getFontHeight();
           //Old set cell style
           if(cellIndex > 0){
               cellStyle.setFillPattern(FillPatternType.NO_FILL);
               sourceCell.setCellStyle(cellStyle);
           }
           double stringNeedsLines = getStringNeedsLines(cellContent,cellWidth);
           double stringNeedsHeight  = setStringNeedsHeight(stringNeedsLines,fontHeight);
           //Use the highest cell row height for the Region
           if(maxCellHeight > stringNeedsHeight && Boolean.TRUE.equals(getPartOfRowsRegion(cellInfoMap))){
               stringNeedsHeight = maxCellHeight;
           }
           //Reset row height
           setRowHeight(stringNeedsHeight,cellHeight,cellInfoMap,sourceCell,sourceRow);
           maxCellHeight = stringNeedsHeight;
       }
       setDefaultRowHeight(sourceRow,sourceRow.getHeight());
   }
  
   /**
    * Set cell content needs height
    * @param stringNeedsLines
    * @param fontHeight
    * @param sourceRow
    * @return
    */
   private static double setStringNeedsHeight(double stringNeedsLines,short fontHeight){
       double totalFontHeight = (fontHeight * stringNeedsLines );
       double stringNeedsHeight = totalFontHeight ;
       if(stringNeedsLines > 1.0 ){
           stringNeedsHeight =  totalFontHeight + (POINT_TO_TWIPS * 2 * UPPER_AND_LOWER_WHITE_SPACE );
       }
       return stringNeedsHeight;
   }
  
   /**
    * Get String Needs Rows
    * @param cellContent
    * @param cellWidth
    */
   private static double getStringNeedsLines(String cellContent, Integer cellWidth) {
       // According to the font and pixel parameters are set after many tests, there is no official website reference.
       double cellContentWidth = getCellContentWidth(cellContent);
       // String needs Rows
       double stringNeedsLines = getStringLineCount(cellContent,cellWidth) > 0 ? getStringLineCount(cellContent,cellWidth) : Math.round(cellContentWidth / cellWidth);
       if (stringNeedsLines < 1.0) {
           stringNeedsLines = 1.0;
       }
       return stringNeedsLines;
   }
  
   /**
    * Set row height
    * @param stringNeedsHeight
    * @param cellHeight
    * @param cellInfoMap
    * @param sourceCell
    * @param sourceRow
    */
   private static void setRowHeight(double stringNeedsHeight,Integer cellHeight,Map<String, Object> cellInfoMap,XSSFCell sourceCell, XSSFRow sourceRow) {
           //The merged cell
           Boolean isPartOfRowsRegion = getPartOfRowsRegion(cellInfoMap);
           if (Boolean.TRUE.equals(isPartOfRowsRegion)) {
               int firstRow = (Integer)cellInfoMap.get("firstRow");
               int lastRow = (Integer)cellInfoMap.get("lastRow");
               //Set each row height
               double addHeight = (stringNeedsHeight - cellHeight) / (lastRow - firstRow + 1);
               for (int i = firstRow; i <= lastRow; i++) {
                    double rowsRegionHeight = sourceRow.getSheet().getRow(i).getHeight() + addHeight;
                    setDefaultRowHeight(sourceRow.getSheet().getRow(i),rowsRegionHeight);
               }
           }else{
               if(stringNeedsHeight > sourceCell.getRow().getHeight()){
                   sourceRow.setHeight((short)stringNeedsHeight);
               }
           }
   }
  
   /**
    * Get String Row Count
    * @param cellValue
    * @param cellWidth
    * @return
    */
   private static int getStringLineCount(String cellValue,Integer cellWidth) {
       int stringLineCount= 0;
       if(StringUtils.isNotBlank(cellValue)){
            String[] linesStr = cellValue.split(LINE_BREAKS_STR);
            for (String cellContent : linesStr) {
               if(!(LINE_BREAKS_STR).equals(cellContent) && StringUtils.isNotBlank(cellContent)){
                   double cellContentWidth = getCellContentWidth(cellContent);
                   double stringNeedsLines = Math.round(cellContentWidth / cellWidth) < 1.0 ? 1.0 : Math.round(cellContentWidth / cellWidth);
                   stringLineCount = (int) (stringLineCount + stringNeedsLines);
               }
             }
             stringLineCount = stringLineCount + getLineBreaksCount(cellValue);
       }
       return stringLineCount;
   }
  
   /**
    * Get Line breaks Count
    * @param cellValue
    * @return
    */
   private static int getLineBreaksCount(String cellValue) {
       int stringLineCount= 0;
       if(StringUtils.isNotBlank(cellValue)){
             for (int i = 0; i < cellValue.length(); i++) {
               if(cellValue.charAt(i) == '\n' || cellValue.charAt(i) == '\r')  {
                   stringLineCount ++;
                }
             }
       }
       return stringLineCount;
   }
  
  /**
    * Get Cell Content Width
    * @param cellContent
    * @return
    */
   private static double getCellContentWidth(String cellContent){
       return  StringUtils.isNotBlank(cellContent) ? cellContent.getBytes().length * MAX_CHARACTER_FOR_COLUMN_WIDTH  : 0;
   }
  
   /**
    * Get cell value with string
    * @param cell
    * @return
    */
   private static String getCellContentAsString(XSSFCell cell) {
       if (cell == null ) {
           return StringUtils.EMPTY;
       }
       String result = StringUtils.EMPTY;
       switch (cell.getCellType()) {
           case NUMERIC:
               result = String.valueOf(cell.getNumericCellValue());
               break;
           case STRING:
               result = String.valueOf(cell.getRichStringCellValue());
               break;
           case BLANK:
               break;
           case BOOLEAN:
               result = String.valueOf(cell.getBooleanCellValue());
               break;
           case ERROR:
               break;
           default:
               break;
       }
       return result;
   }
  
   /**
    * Get cell info
    * @param cell
    * @return  cell info
    */
   private static Map<String, Object> getCellInfo(XSSFCell cell) {
       Map<String, Object> map = new HashMap<>();
       XSSFSheet sheet = cell.getSheet();
       int rowIndex = cell.getRowIndex();
       int columnIndex = cell.getColumnIndex();
       boolean isPartOfRegion = false;
       int firstColumn = 0;
       int lastColumn = 0;
       int firstRow = 0;
       int lastRow = 0;
       int sheetMergeCount = sheet.getNumMergedRegions();
       for (int i = 0; i < sheetMergeCount; i++) {
           CellRangeAddress c = sheet.getMergedRegion(i);
           firstColumn = c.getFirstColumn();
           lastColumn = c.getLastColumn();
           firstRow = c.getFirstRow();
           lastRow = c.getLastRow();
           if (rowIndex >= firstRow && rowIndex <= lastRow && columnIndex >= firstColumn && columnIndex <= lastColumn) {
               isPartOfRegion = true;
               break;
           }
       }
       Integer width = 0;
       Integer height = 0;
       boolean isPartOfRowsRegion = false;
       if (isPartOfRegion) {
           for (int i = firstColumn; i <= lastColumn; i++) {
               width = width + sheet.getColumnWidth(i);
           }
           for (int i = firstRow; i <= lastRow; i++) {
               if (sheet.getRow(i) == null) {
                   height += sheet.createRow(i).getHeight();
               } else {
                   height += sheet.getRow(i).getHeight();
               }
           }
           isPartOfRowsRegion = isPartOfRowsRegion(lastRow, firstRow,isPartOfRowsRegion);
       } else {
           width = sheet.getColumnWidth(columnIndex);
           height = height + cell.getRow().getHeight();
       }
       map.put("isPartOfRowsRegion", isPartOfRowsRegion);
       map.put("firstRow", firstRow);
       map.put("lastRow", lastRow);
       map.put("width", width);
       map.put("height", height);
       return map;
   }
  
   private static boolean getPartOfRowsRegion(Map<String, Object> cellInfoMap){
       return (Boolean) cellInfoMap.get("isPartOfRowsRegion");
   }
  
   /**
    * is Part of row region
    * @param lastRow
    * @param firstRow
    * @return
    */
   private static boolean isPartOfRowsRegion(int lastRow, int firstRow,boolean isPartOfRowsRegion) {
       if (lastRow > firstRow) {
           isPartOfRowsRegion = true;
       }
       return isPartOfRowsRegion;
   }
  
  /**
    * Set default row height : if the height is lower than the default value, use the default value
    * @param sourceRow
    * @param rowHeight
    */
   private static void setDefaultRowHeight(XSSFRow sourceRow,double rowHeight) {
       sourceRow.setHeight((short) (Float.compare((float) rowHeight, (DEFAULT_EXCEL_ROW_HEIGHT * POINT_TO_TWIPS)) == -1 ? (DEFAULT_EXCEL_ROW_HEIGHT * POINT_TO_TWIPS) : rowHeight));
    }
​
}