POI条形图生成一个系列有问题

时间:2019-05-09 07:40:57

标签: java charts apache-poi

我使用JDK8和POI-4.1.0在此处使用它们的示例a link

将图表导出到Word。创建两个系列就可以了two series img, 但我只创建一个系列。系列one series img

的图表错误类别 “ lang1”,“ lang2”,“ lang3”是类别名称,但它们成为系列名称。 我没有理想。我也发现使用折线图有同样的问题  我的代码
    public static void main(String[] args) throws Exception {

            List<String> listLanguages = new ArrayList<>(3);
            listLanguages.add("lang1");listLanguages.add("lang2");listLanguages.add("lang3");

            List<Double> listCountries = new ArrayList<>(3);            
            listCountries.add(10d);listCountries.add(20d);listCountries.add(30d);

            List<Double> listSpeakers = new ArrayList<>(3);
            listSpeakers.add(14d);listSpeakers.add(25d);listSpeakers.add(33d);

            String[] categories = listLanguages.toArray(new String[listLanguages.size()]);
            Double[] values1 = listCountries.toArray(new Double[listCountries.size()]);
            Double[] values2 = listSpeakers.toArray(new Double[listSpeakers.size()]);

            try (XWPFDocument doc = new XWPFDocument()) {
                XWPFChart chart = doc.createChart(5000000, 4000000);
                XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);

                XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);

                leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
                leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
                final int numOfPoints = categories.length;
                final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
                final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
                final String valuesDataRange2 = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
                final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
                final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(values1, valuesDataRange, 1);               
                final XDDFNumericalDataSource<? extends Number> valuesData2 = XDDFDataSourcesFactory.fromArray(values2, valuesDataRange2, 2);
                XDDFBarChartData bar = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
                XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData);
                series1.setTitle("a",chart.setSheetTitle("a", 1));

                 //XDDFBarChartData.Series series2 = (XDDFBarChartData.Series) bar.addSeries(categoriesData, valuesData2);
                //series2.setTitle("b",chart.setSheetTitle("b", 2)); 

                bar.setVaryColors(true);
                bar.setBarDirection(BarDirection.COL);
                chart.plot(bar);

                XDDFChartLegend legend = chart.getOrAddLegend();
                legend.setPosition(LegendPosition.LEFT);
               // legend.setOverlay(false);
               try (OutputStream out = new FileOutputStream("C:/Users/lyf/Desktop/barExample.docx")) {
                    doc.write(out);
                }
            }
            catch(Exception e)
            {  
            }   
    }

1 个答案:

答案 0 :(得分:0)

主要问题是将setVaryColors设置为true意味着:

如果仅一个系列,则为该系列的每个数据点改变颜色。然后,图例显示了变化的数据点而不是序列。如果有多个系列,请为每个系列改变颜色。然后,图例显示了变化的序列。

因此,如果仅存在一个系列,则需要将setVaryColors设置为false

但是另外我们需要设置AxisCrossBetween,所以左轴与类别之间的类别轴交叉。其他的第一个和最后一个类别恰好在交叉点上,条形图只有一半可见。

最后,XDDF目前仅准备就绪一半,并且存在许多错误。例如XDDFChart.setSheetTitle是越野车。它创建一个表,但只有一半且不完整。创建该不完整的表后,Excel无法打开工作簿。因此无法在Word中更新图表数据。

以下代码对我有用,并且可以创建只有一个系列以及两个系列的图表。另外,我尝试在单个步骤中使代码更结构化。每个步骤都会对其操作进行评论。

import java.io.*;

import org.apache.poi.xwpf.usermodel.*;

import org.apache.poi.ss.util.*;
import org.apache.poi.util.Units;

import org.apache.poi.xddf.usermodel.*;
import org.apache.poi.xddf.usermodel.chart.*;

import org.apache.poi.xssf.usermodel.*;

public class CreateWordXDDFChart {

 // Methode to set title in the data sheet without creating a Table but using the sheet data only.
 // Creating a Table is not really necessary.
 static CellReference setTitleInDataSheet(XWPFChart chart, String title, int column) throws Exception {
  XSSFWorkbook workbook = chart.getWorkbook();
  XSSFSheet sheet = workbook.getSheetAt(0);
  XSSFRow row = sheet.getRow(0); if (row == null) row = sheet.createRow(0);
  XSSFCell cell = row.getCell(column); if (cell == null) cell = row.createCell(column);
  cell.setCellValue(title);
  return new CellReference(sheet.getSheetName(), 0, column, true, true);
 }

 public static void main(String[] args) throws Exception {
  try (XWPFDocument document = new XWPFDocument()) {

   // create the data
   String[] categories = new String[]{"Lang 1", "Lang 2", "Lang 3"};
   Double[] valuesA = new Double[]{10d, 20d, 30d};
   Double[] valuesB = new Double[]{15d, 25d, 35d};

   // create the chart
   XWPFChart chart = document.createChart(15*Units.EMU_PER_CENTIMETER, 10*Units.EMU_PER_CENTIMETER);

   // create data sources
   int numOfPoints = categories.length;
   String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
   String valuesDataRangeA = chart.formatRange(new CellRangeAddress(1, numOfPoints, 1, 1));
   String valuesDataRangeB = chart.formatRange(new CellRangeAddress(1, numOfPoints, 2, 2));
   XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
   XDDFNumericalDataSource<Double> valuesDataA = XDDFDataSourcesFactory.fromArray(valuesA, valuesDataRangeA, 1);
   XDDFNumericalDataSource<Double> valuesDataB = XDDFDataSourcesFactory.fromArray(valuesB, valuesDataRangeB, 2);

   // create axis
   XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
   XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
   leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
   // Set AxisCrossBetween, so the left axis crosses the category axis between the categories.
   // Else first and last category is exactly on cross points and the bars are only half visible.
   leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);

   // create chart data
   XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
   ((XDDFBarChartData)data).setBarDirection(BarDirection.COL);

   // create series
   // if only one series do not vary colors for each bar
   ((XDDFBarChartData)data).setVaryColors(false);
   XDDFChartData.Series series = data.addSeries(categoriesData, valuesDataA);
   // XDDFChart.setSheetTitle is buggy. It creates a Table but only half way and incomplete. 
   // Excel cannot opening the workbook after creatingg that incomplete Table. 
   // So updating the chart data in Word is not possible.
   //series.setTitle("a", chart.setSheetTitle("a", 1));
   series.setTitle("a", setTitleInDataSheet(chart, "a", 1));

/*
   // if more than one series do vary colors of the series
   ((XDDFBarChartData)data).setVaryColors(true);
   series = data.addSeries(categoriesData, valuesDataB);
   //series.setTitle("b", chart.setSheetTitle("b", 2));
   series.setTitle("b", setTitleInDataSheet(chart, "b", 2));
*/

   // plot chart data
   chart.plot(data);

   // create legend
   XDDFChartLegend legend = chart.getOrAddLegend();
   legend.setPosition(LegendPosition.LEFT);
   legend.setOverlay(false);

   // Write the output to a file
   try (FileOutputStream fileOut = new FileOutputStream("CreateWordXDDFChart.docx")) {
    document.write(fileOut);
   }
  }
 }
}