В JasperReports можно создавать свои темы для диаграмм. Темы дают полный контроль над созданием объектов JFreeChart. Их можно использовать вместо customizer классов.
Для начала необходимо создать класс темы, реализующий интерфейс net.sf.jasperreports.charts.ChartTheme. У этого интерфейса есть всего один метод:
1 2 3 4 |
/** * @return an <code>org.jfree.chart.JFreeChart</code> object representing the chart */ public JFreeChart createChart(ChartContext chartContext) throws JRException; |
Этот метод должен возвращать созданный элемент JFreeChart. Создание темы с нуля довольно большая задача. Лучше сделать свой класс наследником от класса net.sf.jasperreports.engine.fill.DefaultChartTheme. В качестве примера сделаем тему, которая будет добавлять надписи значений в диаграммах вида StackedAreaChart:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package ru.urvanov.javaexamples.jasperreportscharttheme; import net.sf.jasperreports.charts.ChartContext; import net.sf.jasperreports.engine.JRException; import net.sf.jasperreports.engine.fill.DefaultChartTheme; import net.sf.jasperreports.engine.fonts.FontUtil; import org.jfree.chart.JFreeChart; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.renderer.category.CategoryItemRenderer; public class MyChartTheme extends DefaultChartTheme { private ChartContext chartContext; public JFreeChart createChart(ChartContext chartContext) throws JRException { this.chartContext = chartContext; return super.createChart(chartContext); } /** * */ protected JFreeChart createStackedAreaChart() throws JRException{ JFreeChart jfreeChart = super.createStackedAreaChart(); for (int n = 0; n < jfreeChart.getCategoryPlot().getRendererCount(); n++) { System.out.println("n="+n); CategoryItemRenderer renderer = jfreeChart.getCategoryPlot().getRenderer(n); renderer.setBaseItemLabelsVisible(true); renderer.setBaseItemLabelGenerator((CategoryItemLabelGenerator)getLabelGenerator()); renderer.setBaseItemLabelPaint(getChart().getForecolor()); renderer.setBaseItemLabelFont(FontUtil.getInstance(chartContext.getJasperReportsContext()).getAwtFont( getFont(null), getLocale() )); } return jfreeChart; } } |
Тема готова. Чтобы её подключить нужно создать в проекте ещё несколько классов.
Класс MyChartThemeBundle, реализующий интерфейс net.sf.jasperreports.charts.ChartThemeBundle. У него есть два метода. Метод getChartTheme возвращает тему по названию. Метод getChartThemeNames возвращает массив со списком наших тем.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package ru.urvanov.javaexamples.jasperreportscharttheme; import net.sf.jasperreports.charts.ChartTheme; import net.sf.jasperreports.charts.ChartThemeBundle; public class MyChartThemeBundle implements ChartThemeBundle { public MyChartThemeBundle() { System.out.println("test"); } @Override public ChartTheme getChartTheme(String chartThemeName) { if (chartThemeName.equals("myTheme")) return new MyChartTheme(); throw new IllegalArgumentException(); } @Override public String[] getChartThemeNames() { return new String[] { "myTheme" }; } } |
Класс, реализующий интерфейс net.sf.jasperreports.extensions.ExtensionsRegistryFactory должен возвращать наш класс MyChartThemeBundle в своём интерфейсе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package ru.urvanov.javaexamples.jasperreportscharttheme; import java.util.ArrayList; import java.util.List; import net.sf.jasperreports.charts.ChartThemeBundle; import net.sf.jasperreports.engine.JRPropertiesMap; import net.sf.jasperreports.extensions.ExtensionsRegistry; public class MyExtensionRegistryFactory implements net.sf.jasperreports.extensions.ExtensionsRegistryFactory { @Override public ExtensionsRegistry createRegistry(String arg0, JRPropertiesMap arg1) { return new ExtensionsRegistry() { @SuppressWarnings("unchecked") public <T> List<T> getExtensions(Class<T> extensionType) { List<MyChartThemeBundle> result = new ArrayList<MyChartThemeBundle>(); if (extensionType == ChartThemeBundle.class) { result.add(new MyChartThemeBundle()); } return (List<T>) result; } }; } } |
Обратите внимание, что класс сравнивает аргумент extensionType с ChartThemeBundle.class и возвращает наш MyChartThemeBundle только тогда, когда ожидаются темы диаграмм.
Нужно зарегистрировать наш класс MyExtensionRegistryFactory. Для этого нужно поместить файл jasperreports_extension.properties в src/main/resources нашего Maven проекта. В этом файле должна быть строчка вида:
1 |
net.sf.jasperreports.extension.registry.factory.my=ru.urvanov.javaexamples.jasperreportscharttheme.MyExtensionRegistryFactory |
То есть net.sf.jasperreports.extension.registry.factory.<любое окончание>=<класс ExtensionRegistryFactory>
Всё. Тема готова и доступна для использования. На этом этапе тема уже должна быть видна в форме редактирования отчётов в списке тем для диаграмм. Если она не видна, то, возможно, нужно закрыть IDE и открыть снова, чтобы JasperStudio её увидел. Создадим отчёт с диаграммой, использующей нашу тему (theme=»myTheme»), чтобы проверить её:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<?xml version="1.0" encoding="UTF-8"?> <!-- Created with Jaspersoft Studio version 6.1.0.final using JasperReports Library version 6.1.0 --> <!-- 2015-06-30T15:08:55 --> <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="chart1" pageWidth="595" pageHeight="842" whenNoDataType="NoDataSection" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="6f22b859-a70c-4d32-9abe-303a2e7a4c31"> <queryString> <![CDATA[]]> </queryString> <field name="v" class="java.lang.Integer"/> <field name="x" class="java.lang.Integer"/> <field name="name" class="java.lang.String"/> <background> <band splitType="Stretch"/> </background> <title> <band height="184" splitType="Stretch"> <stackedAreaChart> <chart evaluationTime="Report" theme="myTheme"> <reportElement x="77" y="-16" width="400" height="200" uuid="f7ff05eb-162b-4204-883a-b3a05842e717"/> <chartTitle/> <chartSubtitle/> <chartLegend/> </chart> <categoryDataset> <categorySeries> <seriesExpression><![CDATA[$F{name}]]></seriesExpression> <categoryExpression><![CDATA[$F{x}]]></categoryExpression> <valueExpression><![CDATA[$F{v}]]></valueExpression> </categorySeries> </categoryDataset> <areaPlot> <plot/> <categoryAxisFormat> <axisFormat/> </categoryAxisFormat> <valueAxisFormat> <axisFormat/> </valueAxisFormat> </areaPlot> </stackedAreaChart> </band> </title> <pageHeader> <band height="35" splitType="Stretch"/> </pageHeader> <columnHeader> <band height="201" splitType="Stretch"/> </columnHeader> <detail> <band height="253" splitType="Stretch"/> </detail> <columnFooter> <band height="45" splitType="Stretch"/> </columnFooter> <pageFooter> <band height="54" splitType="Stretch"/> </pageFooter> <summary> <band height="42" splitType="Stretch"/> </summary> <noData> <band height="55"> <staticText> <reportElement x="0" y="0" width="545" height="55" uuid="9b6f6b74-60fa-4ef6-ae62-b9e917020fdf"/> <textElement> <font size="30"/> </textElement> <text><![CDATA[Нет данных для отчёта]]></text> </staticText> </band> </noData> </jasperReport> |
Java код генерации отчёта по этому шаблону:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package ru.urvanov.javaexamples.jasperreportscharttheme; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.jasperreports.engine.JasperCompileManager; import net.sf.jasperreports.engine.JasperFillManager; import net.sf.jasperreports.engine.JasperPrint; import net.sf.jasperreports.engine.JasperReport; import net.sf.jasperreports.engine.data.JRMapCollectionDataSource; import net.sf.jasperreports.engine.export.JRXlsExporter; import net.sf.jasperreports.export.SimpleExporterInput; import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput; import net.sf.jasperreports.export.SimpleXlsReportConfiguration; /** * Hello world! * */ public class App { public static void main(String[] args) { try { JasperReport jasperReport = JasperCompileManager .compileReport(App.class .getResourceAsStream("chart1.jrxml")); Map<String, Object> parameters = new HashMap<>(); List<Map<String, ?>> data = new ArrayList<>(); Map<String, Object> map = new HashMap<>(); map.put("x", Integer.valueOf(1)); map.put("v",Integer.valueOf(2)); map.put("name", "name"); data.add(map); map = new HashMap<>(); map.put("x", Integer.valueOf(2)); map.put("v",Integer.valueOf(1)); map.put("name", "name"); data.add(map); map = new HashMap<>(); map.put("x", Integer.valueOf(3)); map.put("v",Integer.valueOf(3)); map.put("name", "name"); data.add(map); JRMapCollectionDataSource dataSource = new JRMapCollectionDataSource(data); JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, parameters, dataSource); try (FileOutputStream baos = new FileOutputStream("chart1.xls")) { JRXlsExporter xlsExporter = new JRXlsExporter(); xlsExporter.setExporterInput(new SimpleExporterInput(jasperPrint)); xlsExporter.setExporterOutput(new SimpleOutputStreamExporterOutput( baos)); SimpleXlsReportConfiguration configuration = new SimpleXlsReportConfiguration(); configuration.setOnePagePerSheet(false); xlsExporter.setConfiguration(configuration); xlsExporter.exportReport(); } } catch (Exception e) { e.printStackTrace(); } } } |
Java класс, генерирующий отчёт, и шаблон отчёта должны быть в одном пакете, в одной папке.
В идеале всё уже должно работать. Но нет. Если мы запустим отчёт на исполнение сейчас, то увидим, что наш код темы выполняется, но надписи не появялются. Это баг. Он связан с использованием старой версии JFreeChart в JasperReports. В версии 6.0.3 используется JFreeChart версии 1.0.12. Я выявил этот баг создав диаграмму StackedAreaChart в чистом JFreeChart, без JasperReports, и надписей не было. Уберём в pom.xml зависимость от старого JFreeChart и добавим зависимость от последнего JFreeChart. Вот так будет выглядеть конечный pom.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>ru.urvanov.javaexamples</groupId> <artifactId>jasper-reports-chart-theme</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>jasper-reports-chart-theme</name> <url>http://maven.apache.org</url> <properties> <java-version>1.8</java-version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- Reports --> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.0.3</version> <exclusions> <exclusion> <artifactId>jfreechart</artifactId> <groupId>jfree</groupId> </exclusion> <exclusion> <artifactId>jcommon</artifactId> <groupId>jfree</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.jfree</groupId> <artifactId>jfreechart</artifactId> <version>1.0.19</version> </dependency> <!-- Generating xls reports by jasper --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.11</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>${java-version}</source> <target>${java-version}</target> <encoding>${project.build.sourceEncoding}</encoding> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>ru.urvanov.javaexamples.jasperreportscharttheme.App</mainClass> <addClasspath>true</addClasspath> <classpathPrefix>data/lib/</classpathPrefix> </manifest> </archive> </configuration> </plugin> <plugin> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <phase>install</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/data/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </project> |
Теперь всё должно работать. Собираем проект и запускаем. Результат:
Ссылки:
https://github.com/urvanov-ru/java-examples/tree/master/jasper-reports-chart-theme — Проект на github. Поставьте звезду, пожалуйста.
jasper-reports-chart-theme — ZIP архив Maven проекта.
https://github.com/urvanov-ru/java-examples/tree/master/jfreechart-stackedarealabels — Пример, показывающей, что в JFreeChart версии 1.0.12 надписи не показываются, хотя настройки setBaseItemLabelsVisible и прочие выставлены. Поставьте звезду, пожалуйста.
http://jasperreports.sourceforge.net/sample.reference/chartthemes/index.html — пример создания тем для диаграмм в JasperReports.