POI生成Excel文件(合并单元格+计算合并)

2023-04-27写好的,代码已上传,看这篇链接:https://www.jianshu.com/p/4f7df3148a63
基于poi5.x,jxl2.x版本,下载jar包,引入到项目中即可,poi或者jxl想使用哪个就引入哪个依赖。

以下内容为旧内容不用看了

一、先进行效果展示

image.png

二、问题

开发过程中发现了如下问题

  1. 若A3,A4,A5都一致,那么就需要合并,标题4这列的计算为A1C1+A2C2+A3*C3=D3合并列,因为计算后D1为12,D2累加后为:24,D3累加后为:36,所以理论上要保留36,但是合并后,却是D1的值。这时候就需要获取到合并的首行,然后将其设置为最终计算列D3的值了。
    见代码: hssfSheet.getRow((i+j-count) < j ? j : (i+j-count)).getCell(3).setCellValue(bigDecimal.floatValue());

  2. 单元格如果使用默认的,在计算完数字之后,打开excel文件,加入求和公式等函数,计算结果不准确的问题。
    解决:需要对单元格设置为数字类型:cell4.setCellType(CellType.NUMERIC);

三、代码

package com.util.poi;

import com.alibaba.excel.util.DateUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class ExcelMain {

    static class Student {
        private String id;
        private String userName;
        private String passWord;
        private Float score;

        public Student(String id, String userName, String passWord, Float score) {
            this.id = id;
            this.userName = userName;
            this.passWord = passWord;
            this.score = score;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getPassWord() {
            return passWord;
        }

        public void setPassWord(String passWord) {
            this.passWord = passWord;
        }

        public Float getScore() {
            return score;
        }

        public void setScore(Float score) {
            this.score = score;
        }
    }

    public static void main(String[] args) {

        String fileName = String.format("《%s》项目测试文件生成%s", "测试导出", DateUtils.format(new Date(), "yyyy-MM-dd"));
        System.out.println("文件名:" + fileName);
        System.out.println("文件存放系统临时路径:" + System.getProperty("java.io.tmpdir"));

        // 创建一个Excel
        HSSFWorkbook workbook = new HSSFWorkbook();

        // 创建一个shell标签
        HSSFSheet hssfSheet = workbook.createSheet(fileName);
        // 设置单元格默认宽度
        hssfSheet.setDefaultColumnWidth(20);
        // excel设置列数
        String[] titleArr = {"标题1","标题2","标题3","标题4"};

        // 在shell里面增加一个合并单元格(此列合并第一行到第titleArr.length行,第一行行标为0,所以第四行是titleArr.length-1)
        hssfSheet.addMergedRegion(new CellRangeAddress(0, 0, 0, titleArr.length-1));
        // 创建第0行,即第一行
        HSSFRow titleRow = hssfSheet.createRow(0);
        // 创建标题样式
        HSSFCellStyle titleCellStyle = createTitleCellStyle(workbook);

        // 为什么循环titleArr.length呢?因为设置了边框,如果只创建一个单元格,合并后其他三个没边框
        for (int i = 0; i < titleArr.length; i++) {
            HSSFCell titleCell = titleRow.createCell(i);
            titleCell.setCellValue("合并单元格的第一列的标题");
            titleCell.setCellStyle(titleCellStyle);
        }

        HSSFCell hssfCell = null;
        HSSFRow row = hssfSheet.createRow(1);

        for (int i = 0; i < titleArr.length; i++) {
            hssfCell = row.createCell(i);//列索引从0开始
            hssfCell.setCellValue(titleArr[i]);//列名1
            hssfCell.setCellStyle(titleCellStyle);//列居中显示
        }


        // 写入实体数据,下方的list应当是从数据库查询出来的数据,这里模拟
        Student  person1 = new Student("1","赵本山","10086", 11.20f);
        Student  person2 = new Student("1","赵本山","10086", 12.20f);
        Student  person3 = new Student("2","赵本山","10010", 14.00f);
        Student  person4 = new Student("2","赵本山","10010", 16.00f);
        Student  person5 = new Student("3","宋小宝","10086", 18.33f);
        Student  person6 = new Student("4","文松","10086", 9.9f);
        Student  person7 = new Student("5","小损样","10086", 6.00f);
        Student  person8 = new Student("6","谢大脚","10086", 151f);

        List list = new ArrayList<>();
        list.add(person1);
        list.add(person1);
        list.add(person1);
        list.add(person2);
        list.add(person4);
        list.add(person3);
        list.add(person3);
        list.add(person5);
        list.add(person6);
        list.add(person6);
        list.add(person6);
        list.add(person7);
        list.add(person8);

        // 定义数据单元格的样式
        HSSFCellStyle columCellStyle = createColumCellStyle(workbook);

        // 定义一个合并单元格集合
        List cellRangeAddressList = new ArrayList<>();

        String watch = null;
        int count = 0; //计数器用来计算需要合并的列数
        BigDecimal bigDecimal = null; //用来计算数值的

        //为啥j定义为2呢,因为第一行是【合并单元格的第一列的标题】,第二行是titleArr的标题,所以从第三行开始创建数据行
        for (int i = 0, j = 2; i < list.size(); i++) {
            row = hssfSheet.createRow(i+j);
            Student person = list.get(i);
            String id = person.getId();
            String userName = person.getUserName();
            String passWord = person.getPassWord();
            Float score = person.getScore();

            HSSFCell cell = row.createCell(0);
            cell.setCellValue(id);
            cell.setCellStyle(columCellStyle);

            // 第一个循环,watch肯定是空的,要设置值的
            if (StringUtils.isEmpty(watch)){
                watch = userName;
                bigDecimal = new BigDecimal(0);
            }

            // 以name为合并依据,即:名称相同的合并
            if (watch.equals(userName)){
                count++;
                bigDecimal = bigDecimal.add(new BigDecimal(score).multiply(new BigDecimal(id)));
            }else {
                watch = userName;
                if (count > 1){
                    try {
                        cellRangeAddressList.add(new CellRangeAddress(i+j-count, i+j-1, 0, 0));
                        cellRangeAddressList.add(new CellRangeAddress(i+j-count, i+j-1, 3, 3));
                        hssfSheet.getRow((i+j-count) < j ? j : (i+j-count)).getCell(3).setCellValue(bigDecimal.floatValue());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                count = 1;
                bigDecimal = new BigDecimal(score).multiply(new BigDecimal(id));
            }

            // 若是遍历到最后一行,还存在需要合并的需要做判断,否则会跳出循环不合并
            if (i == (list.size()-1) && count > 1){
                try {
                    cellRangeAddressList.add(new CellRangeAddress(i+j-count+1, i+j, 0, 0));
                    cellRangeAddressList.add(new CellRangeAddress(i+j-count+1, i+j, 3, 3));
                    hssfSheet.getRow((i+j-count+1) < j ? j : (i+j-count+1)).getCell(3).setCellValue(bigDecimal.floatValue());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            HSSFCell cell1 = row.createCell(0);
            cell1.setCellType(CellType.STRING);
            cell1.setCellValue(userName);
            cell1.setCellStyle(columCellStyle);

            HSSFCell cell2 = row.createCell(1);
            cell2.setCellType(CellType.STRING);
            cell2.setCellValue(passWord);
            cell2.setCellStyle(columCellStyle);

            HSSFCell cell3 = row.createCell(2);
            cell3.setCellType(CellType.STRING);
            cell3.setCellValue(passWord);
            cell3.setCellStyle(columCellStyle);

            HSSFCell cell4 = row.createCell(3);
            short format = workbook.createDataFormat().getFormat("0.00");
            cell4.setCellType(CellType.NUMERIC);
            cell4.setCellValue(score*3);
            cell4.setCellStyle(columCellStyle);
            cell4.getCellStyle().setDataFormat(format);
        }

        for (CellRangeAddress cellRangeAddress : cellRangeAddressList) {
            try {
                hssfSheet.addMergedRegion(cellRangeAddress);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }

        File file = new File(System.getProperty("java.io.tmpdir") + "/"+fileName+".xls");
        if (!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }

        try {
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            try {
                workbook.write(out);
                workbook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null){
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 创建标题样式
     * @param wb
     * @return
     */
    private static HSSFCellStyle createTitleCellStyle(HSSFWorkbook wb) {
        HSSFCellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.CENTER);//水平居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直对齐
        cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
        cellStyle.setBorderLeft(BorderStyle.THIN); //左边框
        cellStyle.setBorderRight(BorderStyle.THIN); //右边框
        cellStyle.setBorderTop(BorderStyle.THIN); //上边
        HSSFFont headerFont = (HSSFFont) wb.createFont(); // 创建字体样式
        headerFont.setBold(true); //字体加粗
        headerFont.setFontName("仿宋"); // 设置字体类型
        headerFont.setFontHeightInPoints((short) 15); // 设置字体大小
        cellStyle.setFont(headerFont); // 为标题样式设置字体样式
        return cellStyle;
    }


    /**
     * 创建标题样式
     * @param wb
     * @return
     */
    private static HSSFCellStyle createColumCellStyle(HSSFWorkbook wb) {
        HSSFCellStyle cellStyle = wb.createCellStyle();
        cellStyle.setAlignment(HorizontalAlignment.LEFT);//水平居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//垂直对齐
        cellStyle.setBorderBottom(BorderStyle.THIN); //下边框
        cellStyle.setBorderLeft(BorderStyle.THIN); //左边框
        cellStyle.setBorderRight(BorderStyle.THIN); //右边框
        cellStyle.setBorderTop(BorderStyle.THIN); //上边
        HSSFFont headerFont = (HSSFFont) wb.createFont(); // 创建字体样式
        headerFont.setBold(false); //字体加粗
        headerFont.setFontName("仿宋"); // 设置字体类型
        headerFont.setFontHeightInPoints((short) 11); // 设置字体大小
        cellStyle.setFont(headerFont); // 为标题样式设置字体样式
        return cellStyle;
    }

}

你可能感兴趣的:(POI生成Excel文件(合并单元格+计算合并))