JAVA 使用 POI 和 jxls 通过 Excel 模板方式导出图表 修改图表的取值范围


需求:导出各个班次的平均参评率以及评估平均分信息,并做成一个图表进行展示

 

 

由于这里的班次的所有数据是一个动态长度的一个集合

使用jxls进行横向遍历生成数据后会发现图表的数据选择区域并不在我们想象中的模样。
(如下图) 我们会发现在后面的两个单元格并没有归到图表中进行展示

我使用的模板会在后面给出连接【模板1】

 

 

 

 

 

 

 

我们现在需要在导出后的Excel文件中修改图表的数据选择区域 解决方案: 1、将模板文件后缀xlsx修改为zip,用软件打开找到里面的图表文件 xl/charts/chart1.xml 
  (如果Excel中存在多个图表会有多个类似的文件,不要找错了)
2、将文件复制到idea中进行格式化找到描述图表数据范围的位置,首先找到节点 plotArea 。
  在这个节点下面就是描述了所有数据范围的配置。如下图
3、因为我们这里有两种数据在图表上面,所以对应的节点 plotArea 里面也
  包含了2个 xxxChart 的标签,分别是 barChart 以及 lineChart

4、接下来就开始写代码了来控制这个区域范围了,代码如下,这里的getXX()方法
  和setXX(val)方法都是和这个xml中的标签一一对应的,根据这个思路就可以修改数据表的数据取值范围了

 

 

 

 

代码:测试类

import com.teas.kit.jxls.JxlsUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFChart;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTBarSer;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineSer;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Consumer;


public class Test {


    public static void main(String[] args) throws Exception {
        FileInputStream       is    = new FileInputStream("D:\\Users\\zyc\\Desktop\\fsdownload\\博客模板.xlsx");
        ByteArrayOutputStream os    = new ByteArrayOutputStream();
        Map<String, Object>   model = new HashMap<>();
        // 用len来模拟我们数据的可变性
        int len = 10;
        model.put("classNameList", listTitle(len));
        model.put("avgParticipationRateList", list(len));
        model.put("avgEvaluateList", list(len));
        JxlsUtils.exportExcel(is, os, model);// 利用xlsx


        // 修改图表的取值范围
        XSSFWorkbook workbook = new XSSFWorkbook(new ByteArrayInputStream(os.toByteArray()));
        XSSFSheet    sheet    = workbook.getSheetAt(0);
        // 修改图表的区域
        updateChartArea(sheet, 0, (plotArea) -> {
            String   maxCellIndexStr = calculationCell(len, 'C');
            CTBarSer barChart0       = plotArea.getBarChartArray(0).getSerArray(0);
            barChart0.getTx().getStrRef().setF("班次分析表!$B$3");//  平均参评率 的单元格位置
            // 冒号前面的 $C$2 表示title的第一个单元格,也就是班次的第一个单元格
            // 冒号后面的 $" + maxCellIndexStr + "$2" 也就是对应的最后一个单元格
            barChart0.getCat().getStrRef().setF("班次分析表!$C$2:$" + maxCellIndexStr + "$2");
            // 以上面的类推。这个里是确定 平均参评率 的开始单元格以及结束的单元格位置索引
            barChart0.getVal().getNumRef().setF("班次分析表!$C$3:$" + maxCellIndexStr + "$3");


            // 同上一致的处理方式,只是因为数据展示的方式不一致所以对象不同而已。
            CTLineSer lineChart0 = plotArea.getLineChartArray(0).getSerArray(0);
            lineChart0.getTx().getStrRef().setF("班次分析表!$B$4");//  平均评估分 的单元格位置
            lineChart0.getCat().getStrRef().setF("班次分析表!$C$2:$" + maxCellIndexStr + "$2");
            lineChart0.getVal().getNumRef().setF("班次分析表!$C$4:$" + maxCellIndexStr + "$4");
        });

        // 这里就是我们需要的最终结果
        FileOutputStream outputStream = new FileOutputStream("D:\\Users\\zyc\\Desktop\\fsdownload\\博客模板-最终结果.xlsx");
        workbook.write(outputStream);
        workbook.close();


    }

    private static List<Object> list(int len) {
        List<Object> objects = new ArrayList<>(len);
        for (int i = 0; i < len; i++) {
            BigDecimal decimal = new BigDecimal(Math.random() * 100).setScale(2, RoundingMode.HALF_UP);
            objects.add(decimal);
        }
        return objects;
    }

    private static List<Object> listTitle(int len) {
        List<Object> objects = new ArrayList<>(len);
        for (int i = 0; i < len; i++) {
            objects.add(String.format("A%d班", i + 1));
        }
        return objects;
    }


    /**
     * 仅支持xlsx格式
     *
     * @param xlsxSheet  sheet 表,只支持 xlsx 格式的sheet对象
     * @param chartIndex 图表的索引
     * @param consumer   修改图表数据取值的范围操作
     */
    public static void updateChartArea(Sheet xlsxSheet, int chartIndex, Consumer<CTPlotArea> consumer) {
        if (!(xlsxSheet instanceof XSSFSheet)) {
            // 仅支持xlsx格式
            throw new IllegalArgumentException("暂不支持的Sheet格式");
        }
        XSSFSheet sheet = (XSSFSheet) xlsxSheet;
        // 获取全局图表操作对象
        XSSFDrawing drawingPatriarch = sheet.getDrawingPatriarch();
        // 获取所有的图表
        List<XSSFChart> charts = drawingPatriarch.getCharts();
        if (CollectionUtils.isEmpty(charts)) {
            throw new IllegalArgumentException("未找到对应的图表");
        }
        if (chartIndex >= charts.size()) {
            throw new IllegalArgumentException("未找到对应索引的图表");
        }
        XSSFChart  xssfChart = charts.get(chartIndex);
        CTChart    ctChart   = xssfChart.getCTChart();
        CTPlotArea plotArea  = ctChart.getPlotArea();
        consumer.accept(plotArea);
    }


    /**
     * 计算图表的单元格范围,
     *
     * @param len   数据长度
     * @param start 开始的第一个单元格的列
     * @return 返回列的字母索引,如:第AQ列
     */
    public static String calculationCell(int len, char start) {
        Stack<Character> stack = new Stack<>();
        stack.push(start);
        for (int i = 1; i < len; i++) {
            add(stack);
        }
        StringBuilder builder = new StringBuilder(stack.size());
        while (!stack.isEmpty()) {
            builder.append(stack.pop());
        }
        return builder.reverse().toString();
    }

    private static void add(Stack<Character> stack) {
        if (stack.isEmpty()) {
            stack.push('A');
        } else {
            Character pop = stack.pop();// 获取最后一个值
            if (pop == 'Z') {
                //如果最后一个值是Z,就需要
                add(stack);
                stack.push('A');
            } else {
                char c = (char) (pop + 1);
                stack.push(c);
            }
        }
    }
}
View Code

 

jxls相关的东西自行搜索

 

 

模板文件

链接:https://pan.baidu.com/s/1lx1_NuEL3dg42kW-EwVoLg 
提取码:97ly 
复制这段内容后打开百度网盘手机App,操作更方便哦

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM