利用POI生成各种复杂的统计图

利用POI生成各种复杂的统计图

前言

这是一种比较通用并且相对比较稳妥的方法,有利于复杂统计图的拓展,缺点是效率低下,如果只是想简单快捷的同学请移步CV大法,本文仅做参考。
需要的maven依赖如下:

<dependency>
    <groupId>org.apache.poi</groupId>
     <artifactId>poi</artifactId>
     <version>5.1.0</version>
 </dependency>
 <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-ooxml</artifactId>
     <version>5.1.0</version>
 </dependency>
 <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-scratchpad</artifactId>
     <version>5.1.0</version>
 </dependency>
步骤
  1. 新建一个.xlsx(其他的我没有试过,并且我最终输出的文件也是.xlsx格式的)在xml上用假数据画一个自己想要的图(例如下面这样的柱状图)
    在这里插入图片描述

  2. 在idea中以CTChart的方式打开这个图,利用如下代码

XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("这里填上假的xml路径"));
//如果测试的xlsx文件有多个图,get函数得到的图按照当时创建图的时候的顺顺序依次获取
XSSFChart xssfChart = workbook.getSheetAt(0).createDrawingPatriarch().getCharts().get(0);
CTChart ctChart = xssfChart.getCTChart();
System.out.println(ctChart.toString());

3.最终控制台上显示了xml格式字符串,这个xml格式中的各个节点是“模仿”这个图的关键。

//真的xml文件太长了,所以这里就贴一部分,具体的在后面会一步一步说。
<c:title>
    <c:overlay val="0"/>
    <c:spPr>
      <a:noFill/>
      <a:ln>
        <a:noFill/>
      </a:ln>
      <a:effectLst/>
    </c:spPr>
    <c:txPr>
      <a:bodyPr rot="0" spcFirstLastPara="1" vertOverflow="ellipsis" vert="horz" wrap="square" anchor="ctr" anchorCtr="1"/>
      <a:lstStyle/>
      <a:p>
        <a:pPr>
          <a:defRPr sz="1400" b="0" i="0" u="none" strike="noStrike" kern="1200" spc="0" baseline="0">
            <a:solidFill>
              <a:schemeClr val="tx1">
                <a:lumMod val="65000"/>
                <a:lumOff val="35000"/>
              </a:schemeClr>
            </a:solidFill>
            <a:latin typeface="+mn-lt"/>
            <a:ea typeface="+mn-ea"/>
            <a:cs typeface="+mn-cs"/>
          </a:defRPr>
        </a:pPr>
        <a:endParaRPr lang="zh-CN"/>
      </a:p>
    </c:txPr>
  </c:title>
  1. 在上面的节点中我们可以看到大量的一层包一层的节点,这些节点在poi中会有对应的函数,我们只要一步一步照着这个节点依样画葫芦即可。
  2. 在“依样画葫芦”之前,我们还需要做一点点的准备:
XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream("因为我直接就在原来的表格上画,所以路径和上面是一样的,如果是实际应用,要按照具体的表格存放位置确定"));
        XSSFChart chart = workbook.getSheetAt(0).createDrawingPatriarch().createChart(new XSSFClientAnchor(0, 0, 0, 0, 0, 27, 0 + 5, 27 + 15));
        CTChart ctChart = chart.getCTChart();

6.虽然poi中封装了几乎所有的xml节点,但是在实际情况中我们并不需要用到所有的节点,例如上面的title,表示的是图表的标题,如果不想要,可以直接忽略。
7.在xml文件上,紧接着title的是plotarea节点,这个决定了画图的区域以及区域中的实际统计图,是不能忽略的。plotarea节点具体如下:

<c:plotArea>
    <c:layout/>
    <c:barChart>
      <c:barDir val="col"/>
      <c:grouping val="clustered"/>
      <c:varyColors val="0"/>
      <c:ser>
        <c:idx val="0"/>
        <c:order val="0"/>
        <c:tx>
          <c:strRef>
            <c:f>Sheet1!$B$1</c:f>
            <c:strCache>
              <c:ptCount val="1"/>
              <c:pt idx="0">
                <c:v>title2</c:v>
              </c:pt>
            </c:strCache>
          </c:strRef>
        </c:tx>
        <c:spPr>
          <a:solidFill>
            <a:schemeClr val="accent1"/>
          </a:solidFill>
          <a:ln>
            <a:noFill/>
          </a:ln>
          <a:effectLst/>
        </c:spPr>
        <c:invertIfNegative val="0"/>
        <c:cat>
          <c:strRef>
            <c:f>Sheet1!$A$2:$A$6</c:f>
            <c:strCache>
              <c:ptCount val="5"/>
              <c:pt idx="0">
                <c:v>C1</c:v>
              </c:pt>
              <c:pt idx="1">
                <c:v>C2</c:v>
              </c:pt>
              <c:pt idx="2">
                <c:v>C3</c:v>
              </c:pt>
              <c:pt idx="3">
                <c:v>C4</c:v>
              </c:pt>
              <c:pt idx="4">
                <c:v>C5</c:v>
              </c:pt>
            </c:strCache>
          </c:strRef>
        </c:cat>
        <c:val>
          <c:numRef>
            <c:f>Sheet1!$B$2:$B$6</c:f>
            <c:numCache>
              <c:formatCode>General</c:formatCode>
              <c:ptCount val="5"/>
              <c:pt idx="0">
                <c:v>1</c:v>
              </c:pt>
              <c:pt idx="1">
                <c:v>2</c:v>
              </c:pt>
              <c:pt idx="2">
                <c:v>3</c:v>
              </c:pt>
              <c:pt idx="3">
                <c:v>4</c:v>
              </c:pt>
              <c:pt idx="4">
                <c:v>5</c:v>
              </c:pt>
            </c:numCache>
          </c:numRef>
        </c:val>
        <c:extLst>
          <c:ext uri="{C3380CC4-5D6E-409C-BE32-E72D297353CC}" xmlns:c16="http://schemas.microsoft.com/office/drawing/2014/chart">
            <c16:uniqueId val="{00000000-1C8F-4380-A5A1-F07389DC9975}"/>
          </c:ext>
        </c:extLst>
      </c:ser>
      <c:dLbls>
        <c:showLegendKey val="0"/>
        <c:showVal val="0"/>
        <c:showCatName val="0"/>
        <c:showSerName val="0"/>
        <c:showPercent val="0"/>
        <c:showBubbleSize val="0"/>
      </c:dLbls>
      <c:gapWidth val="219"/>
      <c:overlap val="-27"/>
      <c:axId val="1761604960"/>
      <c:axId val="1761633424"/>
    </c:barChart>
    <c:catAx>
      <c:axId val="1761604960"/>
      <c:scaling>
        <c:orientation val="minMax"/>
      </c:scaling>
      <c:delete val="0"/>
      <c:axPos val="b"/>
      <c:numFmt formatCode="General" sourceLinked="1"/>
      <c:majorTickMark val="none"/>
      <c:minorTickMark val="none"/>
      <c:tickLblPos val="nextTo"/>
      <c:spPr>
        <a:noFill/>
        <a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
          <a:solidFill>
            <a:schemeClr val="tx1">
              <a:lumMod val="15000"/>
              <a:lumOff val="85000"/>
            </a:schemeClr>
          </a:solidFill>
          <a:round/>
        </a:ln>
        <a:effectLst/>
      </c:spPr>
      <c:txPr>
        <a:bodyPr rot="-60000000" spcFirstLastPara="1" vertOverflow="ellipsis" vert="horz" wrap="square" anchor="ctr" anchorCtr="1"/>
        <a:lstStyle/>
        <a:p>
          <a:pPr>
            <a:defRPr sz="900" b="0" i="0" u="none" strike="noStrike" kern="1200" baseline="0">
              <a:solidFill>
                <a:schemeClr val="tx1">
                  <a:lumMod val="65000"/>
                  <a:lumOff val="35000"/>
                </a:schemeClr>
              </a:solidFill>
              <a:latin typeface="+mn-lt"/>
              <a:ea typeface="+mn-ea"/>
              <a:cs typeface="+mn-cs"/>
            </a:defRPr>
          </a:pPr>
          <a:endParaRPr lang="zh-CN"/>
        </a:p>
      </c:txPr>
      <c:crossAx val="1761633424"/>
      <c:crosses val="autoZero"/>
      <c:auto val="1"/>
      <c:lblAlgn val="ctr"/>
      <c:lblOffset val="100"/>
      <c:noMultiLvlLbl val="0"/>
    </c:catAx>
    <c:valAx>
      <c:axId val="1761633424"/>
      <c:scaling>
        <c:orientation val="minMax"/>
      </c:scaling>
      <c:delete val="0"/>
      <c:axPos val="l"/>
      <c:majorGridlines>
        <c:spPr>
          <a:ln w="9525" cap="flat" cmpd="sng" algn="ctr">
            <a:solidFill>
              <a:schemeClr val="tx1">
                <a:lumMod val="15000"/>
                <a:lumOff val="85000"/>
              </a:schemeClr>
            </a:solidFill>
            <a:round/>
          </a:ln>
          <a:effectLst/>
        </c:spPr>
      </c:majorGridlines>
      <c:numFmt formatCode="General" sourceLinked="1"/>
      <c:majorTickMark val="none"/>
      <c:minorTickMark val="none"/>
      <c:tickLblPos val="nextTo"/>
      <c:spPr>
        <a:noFill/>
        <a:ln>
          <a:noFill/>
        </a:ln>
        <a:effectLst/>
      </c:spPr>
      <c:txPr>
        <a:bodyPr rot="-60000000" spcFirstLastPara="1" vertOverflow="ellipsis" vert="horz" wrap="square" anchor="ctr" anchorCtr="1"/>
        <a:lstStyle/>
        <a:p>
          <a:pPr>
            <a:defRPr sz="900" b="0" i="0" u="none" strike="noStrike" kern="1200" baseline="0">
              <a:solidFill>
                <a:schemeClr val="tx1">
                  <a:lumMod val="65000"/>
                  <a:lumOff val="35000"/>
                </a:schemeClr>
              </a:solidFill>
              <a:latin typeface="+mn-lt"/>
              <a:ea typeface="+mn-ea"/>
              <a:cs typeface="+mn-cs"/>
            </a:defRPr>
          </a:pPr>
          <a:endParaRPr lang="zh-CN"/>
        </a:p>
      </c:txPr>
      <c:crossAx val="1761604960"/>
      <c:crosses val="autoZero"/>
      <c:crossBetween val="between"/>
    </c:valAx>
    <c:spPr>
      <a:noFill/>
      <a:ln>
        <a:noFill/>
      </a:ln>
      <a:effectLst/>
    </c:spPr>
  </c:plotArea>

可以看到,即使是默认生成的柱状统计图,plotarea中的节点也非常非常的多,但是正如之前说到的,想要画成一个统计图,实际上并不需要这么多的节点,甚至在节点中,也并不是所有的属性都需要用到。所以实际上的节点配置并不会很多。
一个一个来看,首先是<c:plotArea>,这部分对应poi中的代码表示为

 CTPlotArea plotArea = ctChart.getPlotArea();

接着是<c:barChart>,这部分定义了我们需要的统计图,根据统计图的不同,这个节点的名称也会有所不同,例如<c:LineChart>,但是现在暂时不用管这些,java对应代码如下:

//显然,plotArea显示了节点之间的嵌套关系,这个之后会表现得更加明显。
CTBarChart ctBarChart = plotArea.addNewBarChart();

接下来是

      <c:barDir val="col"/>
      <c:grouping val="clustered"/>
      <c:varyColors val="1"/>

对应设置了柱状图的横纵向表示,形式为簇状,不适用颜色变化
对应poi表示为

ctBarChart.addNewBarDir().setVal(STBarDir.COL);
ctBarChart.addNewGrouping().setVal(STBarGrouping.CLUSTERED);
ctBarChart.addNewVaryColors().setVal(true);

接下来是数据序列节点

<c:ser>

对应

CTBarSer ctBarSer = ctBarChart.addNewSer();

因为接下来的节点规律和上面的完全相同,所以我就简单写了

 <c:idx val="0"/> -->ctBarSer.addNewIdx().setVal(0);
 <c:order val="0"/> -->可以不用管
  <c:tx>
          <c:strRef>
            <c:f>Sheet1!$B$1</c:f>
            <c:strCache>
              <c:ptCount val="1"/>
              <c:pt idx="0">
                <c:v>title2</c:v>
              </c:pt>
            </c:strCache>
          </c:strRef>
        </c:tx>
        --> 只要管一个节点即可
        ctBarSer.addNewTx().addNewStrRef().setF("Sheet1!$B$1");
        <c:spPr>
          <a:solidFill>
            <a:schemeClr val="accent1"/>
          </a:solidFill>
          <a:ln>
            <a:noFill/>
          </a:ln>
          <a:effectLst/>
        </c:spPr>
        ----> 颜色设定,都可以不用管
        <c:invertIfNegative val="0"/> -->不用管
        <c:cat>
          <c:strRef>
            <c:f>Sheet1!$A$2:$A$6</c:f>
            <c:strCache>
              <c:ptCount val="5"/>
              <c:pt idx="0">
                <c:v>C1</c:v>
              </c:pt>
              <c:pt idx="1">
                <c:v>C2</c:v>
              </c:pt>
              <c:pt idx="2">
                <c:v>C3</c:v>
              </c:pt>
              <c:pt idx="3">
                <c:v>C4</c:v>
              </c:pt>
              <c:pt idx="4">
                <c:v>C5</c:v>
              </c:pt>
            </c:strCache>
          </c:strRef>
        </c:cat> 
        --> 只要管如下节点即可
        ctBarSer.addNewCat().addNewStrRef().setF("Sheet1!$A$2:$A$6");
        <c:val>
          <c:numRef>
            <c:f>Sheet1!$B$2:$B$6</c:f>
            <c:numCache>
              <c:formatCode>General</c:formatCode>
              <c:ptCount val="5"/>
              <c:pt idx="0">
                <c:v>1</c:v>
              </c:pt>
              <c:pt idx="1">
                <c:v>2</c:v>
              </c:pt>
              <c:pt idx="2">
                <c:v>3</c:v>
              </c:pt>
              <c:pt idx="3">
                <c:v>4</c:v>
              </c:pt>
              <c:pt idx="4">
                <c:v>5</c:v>
              </c:pt>
            </c:numCache>
          </c:numRef>
        </c:val>--> 只要管如下节点即可
        ctBarSer.addNewVal().addNewNumRef().setF("Sheet1!$B$2:$B$6");
        <c:extLst> --> 不用管
	  <c:dLbls>
        <c:showLegendKey val="0"/>
        <c:showVal val="0"/>
        <c:showCatName val="0"/>
        <c:showSerName val="0"/>
        <c:showPercent val="0"/>
        <c:showBubbleSize val="0"/>
      </c:dLbls>
      --> 一些数据图例和样式的设定,暂时也可以不用管
      <c:gapWidth val="219"/>间距,暂时可以不用管
      <c:overlap val="-27"/> 重叠
      <c:axId val="1761604960"/> 
      <c:axId val="1761633424"/> -->坐标轴id,最好是自己设
      ctBarChart.addNewAxId().setVal(123456);
      ctBarChart.addNewAxId().setVal(123457);
<c:catAx> --->x轴设定,实在分不清就直接照着写
CTCatAx ctCatAx = plotArea.addNewCatAx();
<c:axId val="1761604960"/> -->  ctCatAx.addNewAxId().setVal(123456); // id of the cat axis
<c:scaling>
 	<c:orientation val="minMax"/> --> ctCatAx.addNewScaling().addNewOrientation().setVal(STOrientation.MIN_MAX);
 </c:scaling>
 <c:delete val="0"/> --> ctCatAx.addNewDelete().setVal(false);
 <c:axPos val="b"/> -->  ctCatAx.addNewAxPos().setVal(STAxPos.B);
 <c:tickLblPos val="nextTo"/> --> ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);
<c:crossAx val="1761633424"/> --> ctCatAx.addNewCrossAx().setVal(123457);
<c:crosses val="autoZero"/> --> ctCatAx.addNewCrosses().setVal(STCrosses.AUTO_ZERO);

<c:valAx>和<c:catAx>的配置方法完全一样,所以就只写java代码了

CTValAx ctValAx = plotArea.addNewValAx();
ctValAx.addNewAxId().setVal(123457); // id of the val axis
ctValAx.addNewScaling().addNewOrientation().setVal(STOrientation.MIN_MAX);
ctValAx.addNewDelete().setVal(false);
ctValAx.addNewAxPos().setVal(STAxPos.L);
ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO);
ctValAx.addNewCrosses().setVal(STCrosses.AUTO_ZERO);
ctValAx.addNewCrossAx().setVal(123456);

最后是图例显示

// legend
CTLegend ctLegend = ctChart.addNewLegend();
ctLegend.addNewLegendPos().setVal(STLegendPos.B);
ctLegend.addNewOverlay().setVal(false);

好,写(chao)到这一步就基本上差不多了,最后只剩下把我们设定好的统计图实实在在的写到xlsx表格中去了。

//这个是拿来检验自己画的图上有哪些节点,一般也不用细看
//System.out.println(ctChart);

workbook.write(new FileOutputStream("C:\\Users\\dpx5803\\IdeaProjects\\springboot-helloworld\\test2.xlsx"));
 workbook.close();

检验一下效果
在这里插入图片描述
额…最后的效果并不是完全一样的,其原因主要是我在配置节点的时候多配置了一些属性,比如最后的legend部分的代码(显示为下方的图例)。看到这里还有兴趣的话可以跟自己生成的xml字符串比对一下,看看哪里不一样,这里就不说了。

参考链接

POI EXCEL 图表、折线图、条形图,柱状图、饼图、散点图

项目中想实现excel图表但POI不支持所以想到了这个方法。放上来抛砖引玉,感兴趣的欢迎大家交流 注:-------------------------------------------------------------------------- 其实现原理和利用JavaScript实现客户端绘图完全一样。后台代码将输入 写入特定区域,前端脚本在页面载入时执行脚本绘图。大家都是做B/S开发 的,这方面的只是不需要我多说了吧。 如果我们让一个HTML页面在载入时显示一个提示框用后台编程是不现实的 所以我们就动态生成一段JS: window.onload=function(){ ...... } 用Java输出图表的场景和这个一样,POI、JXL对操作Excel图表无能为力 但Excel支持脚本的,也有工作表载入事件,所以也可以用同样的原理实现 只不过这次客户端不再是浏览器而是Excel应用程序。 这样交代的应该比较明白了吧,请不要再留言抱怨没有Java代码了,因为 这根本跟java代码没多大关系,后台的代码所做的工作仅仅是写入必要的 数据,数据的解析和绘图操作是在VBA中完成的 ---2009-12-18 今天查找资料翻出来了这段代码,发现里边的类确实是空的,我说怎么总挨骂 呢,当时肯定打包的时候晕透了把另一个本该删除的类打包进去了。我无法 替换文件只能把java代码贴上来了,请自己修改模板文件的路径。 package com.test.poi; import java.io.FileInputStream; import java.io.FileOutputStream; import org.apache.poi.hssf.usermodel.HSSFRow; import org.apache.poi.hssf.usermodel.HSSFSheet; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.filesystem.POIFSFileSystem; public class PoiChart { public static void main(String[] args){ try{ POIFSFileSystem fs=new POIFSFileSystem(new FileInputStream("E:/王建波_bak/我的练习/Module.xls")); HSSFWorkbook wb=new HSSFWorkbook(fs); HSSFSheet sheet=wb.getSheetAt(0); HSSFRow row; //写入表头 String[] aTblHeaders=new String[]{ "姓名","语文","数学","英语" }; row=sheet.createRow(0); for(int i=0,len=aTblHeaders.length;i<len;i++){ row.createCell((short)i).setCellValue(aTblHeaders[i]); } //向模板中写入数据 String[] aNames=new String[]{ "张飞","刘备","关羽","曹操","孙权","吕布" }; for(int i=0,nRowLen=aNames.length;i<nRowLen;i++){ row=sheet.createRow(i+1); row.createCell((short)0).setCellValue(aNames[i]); for(int j=1,nColLen=aTblHeaders.length;j<nColLen;j++){ row.createCell((short)j).setCellValue(Math.floor(Math.random()*100)); } } //输出文件 FileOutputStream fout=new FileOutputStream("d:/chart.xls"); wb.write(fout); fout.close(); }catch(Exception e){ e.printStackTrace(); } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值