最近由于业务需要,在做导出Excel的业务需求,要求导出的Excel含有多级表头,和含有表尾部数据,如下图的表格:

java实现导出excel动态表头 java导出excel多级表头_导出Excel

运用java的POI来实现上述需求:

//表格头数组
        String[][] names = {{"合作费审批单", "", "", "", "", "合作费", " ", "基金", " "},
                {"回款日期", "发货日期", "省份", "代理商名称", "数量", "合作费金额", "基金", "基金金额", "结算日期"}};
        /*表格尾部 动态 数据*/
        //个人汇总数据(根据自己的具体业务需求拿到一个list集合)
        List<Business> summaryData = businessService.summaryDate(business);

        //总计数据 (根据自己的具体业务需求拿到一个对象)
        Business total = businessService.totalDate(business);
        //固定尾部数据
        String[][] tableEnd = {{"", "", "", "总计",total.getSumAmount().toString(), "","", "", ""},
                {"编号:"},
                {"制单人:      运营总监审批:"}};
      //所有表尾部数据存放该二维数组中
        String[][] tableEndDate = new String[summaryData.size() + 3][];

        int j = 0;
        for (int i = 0; i < summaryData.size() + 3; i++) {

            if (i < summaryData.size()) {
                tableEndDate[i] = new String[]{"", "", "",summaryData.get(i).getAgentName() + " 汇总", summaryData.get(i).getSumAmount().toString(), "", "", "", ""};
            } else {
                tableEndDate[i] = tableEnd[j];
                j = j + 1;

            }
        }
        
        /*导出Excel*/
        XSSFWorkbook workbook = ExcelUtil.exportMultilevelHeader("业务合作协议", names, list, Record.class, tableEndDate);

ExcelUtil工具类中封装的处理多级表头和表尾部的方法为:

/**
     * 导出含多级表头和表尾的excel
     * @param sheetName
     * @param head 多级表头
     * @param dataList 表数据
     * @param type  
     * @param tableEndData 表尾
     */
    public static XSSFWorkbook exportMultilevelHeader(String sheetName, String[][] head, List<?> dataList, Class type, String[][] tableEndData) {
         /*变量*/
        String[] properties;
        Object[] rowValue;
        List<Object[]> values;
        Field[] fields;
        XSSFCell cell;
        String vo;


        /*导出Excel*/
        // 第一步,创建一个workBook,对应一个Excel文件
        XSSFWorkbook wb = new XSSFWorkbook();
        // 第二步,在workBook中添加一个sheet,对应Excel文件中的sheet
        XSSFSheet sheet = wb.createSheet(sheetName);
        // 第三步,在sheet中添加表头第0行,注意老版本poi对Excel的行数列数有限制short

        XSSFRow row;

        // 第四步,创建单元格,并设置值表头 设置表头居中
        //生成一个Style
        XSSFCellStyle style = wb.createCellStyle();
        style.setWrapText(true);
        style.setAlignment(XSSFCellStyle.ALIGN_CENTER);
        //循环创建表格头,设置style,名称
        /*for (int i = 0; i < head.length; i++) {
            cell = row.createCell(i);
            cell.setCellStyle(style);
            cell.setCellValue(head[i]);
        }*/
        int mergerNum = 0; //合并数
        //给单元格设置值
        for (int i = 0; i < head.length; i++) {
            row = sheet.createRow(i);
            row.setHeight((short) 700);
            for (int j = 0; j < head[i].length; j++) {
                cell = row.createCell(j);
                cell.setCellStyle(style);
                cell.setCellValue(head[i][j]);
            }
        }
        Map<Integer, List<Integer>> map = new HashMap<Integer, List<Integer>>();   // 合并行时要跳过的行列
        //合并列
        for (int i = 0; i < head[head.length - 1].length; i++) {
            if ("".equals(head[head.length - 1][i])) {
                for (int j = head.length - 2; j >= 0; j--) {
                    if (!"".equals(head[j][i])) {
                        sheet.addMergedRegion(new CellRangeAddress(j, head.length - 1, i, i)); // 合并单元格
                        break;
                    } else {
                        if (map.containsKey(j)) {
                            List<Integer> list = map.get(j);
                            list.add(i);
                            map.put(j, list);
                        } else {
                            List<Integer> list = new ArrayList<Integer>();
                            list.add(i);
                            map.put(j, list);
                        }
                    }
                }
            }
        }
        //合并行
        for (int i = 0; i < head.length - 1; i++) {
            for (int j = 0; j < head[i].length; j++) {
                List<Integer> list = map.get(i);
                if (list == null || (list != null && !list.contains(j))) {
                    if ("".equals(head[i][j])) {
                        mergerNum++;
                        if (mergerNum != 0 && j == (head[i].length - 1)) {
                            sheet.addMergedRegion(new CellRangeAddress(i, i, j - mergerNum, j)); // 合并单元格
                            mergerNum = 0;
                        }
                    } else {
                        if (mergerNum != 0) {
                            sheet.addMergedRegion(new CellRangeAddress(i, i, j - mergerNum - 1, j - 1)); // 合并单元格
                            mergerNum = 0;
                        }
                    }
                }
            }
        }
        //解析导出类型
        Class<Record> recordClass = Record.class;
        if (null == type) {
            //导出失败
            return null;
        } else if (type.equals(recordClass)) {    //导出List<Record>
            //获取Record中包含的properties,用于生成表格头及创建Cell
            properties = getRecordProperties(dataList, null);
            vo = "record";
        } else {    //导出List<Bean>
            //获取Bean的Field
            fields = type.getDeclaredFields();
            properties = getRecordProperties(null, fields);
            vo = "bean";
        }

        if (null == head) {
            int i = 0;
            if (head.length > 0) {
                i = head.length - 1;
            }
            head[i] = properties;
        }

        // 第五步,写入实体数据
        /*表头行数*/
        int m = 1;
        if (head.length > 0) {
            m = head.length;
        }
        values = getRowValue(dataList, properties, vo);
        for (int i = 0; i < dataList.size(); i++) {

            row = sheet.createRow(i + m); //创建行
            rowValue = values.get(i);
            // 第四步,创建单元格,并设置值
            for (int j = 0; j < properties.length; j++) {
                cell = row.createCell(j);
                cell.setCellStyle(style);
                setCellValue(cell, rowValue[j]);
            }
        }
        //第六步,处理表格尾部的数据
        if (tableEndData != null && tableEndData.length > 0) {
            for (int i = 0; i < tableEndData.length; i++) {
                row = sheet.createRow(dataList.size() + m + i);
                for (int j = 0; j < tableEndData[i].length; j++) {
                    cell = row.createCell(j);
                /*cell.setCellStyle(style);*/
                    setCellValue(cell, tableEndData[i][j]);
                }
            }
        }
        return wb;
    }

补充工具类方法:

/**
     * 获取Record包含的所有properties
     *
     * @param list   列表
     * @param fields 属性
     * @return 包含properties
     */
    private static String[] getRecordProperties(List<?> list, Field[] fields) {
        if (null != list && null == fields) {
            Record record = (Record) list.get(0);
            Set<String> keySet = record.keySet();
            List<String> keysList = new ArrayList<>(keySet);
            return keysList.toArray(new String[keysList.size()]);
        } else if (null != fields && null == list) {
            String[] properties = new String[fields.length];
            for (int i = 0; i < fields.length; i++) {
                properties[i] = fields[i].getName();
            }
            return properties;
        }
        return new String[0];
    }

    /**
     * 转换列表数据
     *
     * @param list       数据列表
     * @param properties 属性列表
     * @param vo         类型
     * @return 转换后的数据
     */
    private static List<Object[]> getRowValue(List<?> list, String[] properties, String vo) {
        List<Object[]> resultList = new ArrayList<>();
        Record record;
        if (Strings.isNullOrEmpty(vo)) {
            return resultList;
        } else if ("record".equals(vo)) {
            for (Object object : list) {
                record = (Record) object;
                Object[] values = new Object[properties.length];    //定义在外部数组值会被最后写入的覆盖
                for (int i = 0; i < properties.length; i++) {

                    values[i] = record.get(properties[i]);

                }
                resultList.add(values);
            }
            return resultList;
        } else if ("bean".equals(vo)) {
            for (Object object : list) {
                Class cf = object.getClass();
                Object[] values = new Object[properties.length];    //定义在外部数组值会被最后写入的覆盖
                for (int i = 0; i < properties.length; i++) {
                    char[] name = properties[i].toCharArray();
                    name[0] -= 32;
                    try {
                        Method method = cf.getMethod("get" + String.valueOf(name));
                        values[i] = method.invoke(object);
                    } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
                resultList.add(values);
            }
            return resultList;
        }
        return resultList;
    }

    /**
     * 设置单元格值
     *
     * @param cell  单元格
     * @param value 值
     */
    private static void setCellValue(XSSFCell cell, Object value) {
        if (value instanceof String) {
            cell.setCellValue((String) value);
            cell.setCellType(XSSFCell.CELL_TYPE_STRING);
        } else if (value instanceof Date) {
            cell.setCellValue((Date) value);
            cell.setCellType(XSSFCell.CELL_TYPE_STRING);
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
            cell.setCellType(XSSFCell.CELL_TYPE_BOOLEAN);
        } else if (value instanceof Double) {
            cell.setCellValue((Double) value);
            cell.setCellType(XSSFCell.CELL_TYPE_NUMERIC);
        } else if (value instanceof Calendar) {
            cell.setCellValue((Calendar) value);
            cell.setCellType(XSSFCell.CELL_TYPE_STRING);
        } else if (value instanceof RichTextString) {
            cell.setCellValue((RichTextString) value);
            cell.setCellType(XSSFCell.CELL_TYPE_STRING);
        } else {
            cell.setCellValue(String.valueOf(value));
            cell.setCellType(XSSFCell.CELL_TYPE_STRING);
        }
    }

补充Record类:

import java.math.BigDecimal;
import java.util.Date;
import java.util.LinkedHashMap;

public class Record extends LinkedHashMap<String,Object> {
    public void set(String field,Object value){
        put(field,value);
    }
    public String getString(String field){
        return (String)get(field);
    }
    public Integer getInteger(String field){
        return (Integer)get(field);
    }
    public Long getLong(String field){
        return (Long)get(field);
    }
    public BigDecimal getBigDecimal(String field){
        return (BigDecimal)get(field);
    }
    public Date getDate(String field){
        return (Date)get(field);
    }
    public Boolean getBoolean(String field){
        return (Boolean) get(field);
    }
}

如果没有表尾数据,可在传参数时,直接给它一个null就好了!

好的,处理完成啦!希望能给需要的朋友提供帮助!

另外,如果大家有更好更简洁的办法,欢迎在下方留言!!