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