为了能够动态修改报表,了解最初由jrxml格式文件转换为图像、PDF等格式文件的整个过程非常必要。下面几个步骤简要概述了整个转换过程。
1、.jrxml文件借助JRXmlLoader类的loadXML()方法加载进入内存。loadXML()方法接收jrxml文件路径作为输入参数,返回一个JasperDesign对象,JasperDesign对象是jrxml文件在内存中的表示。对报表的所有动态修改操作都是针对JasperDesign对象进行的。
2、完成必要的修改后,JasperDesign对象经过类JasperCompileManager的compile方法的验证、编译。取决于compile方法接收的参数,compile方法或返回一个JasperReport对象,或者将生成的.jasper文件保存到compile方法所接收的第二个参数指定的位置。注意:当不需要对报表进行动态修改时,通过使用合适的compile方法,可直接将jrxml文件编译为一个JasperReport对象或.jasper文件。
3、完成第2步后,需要为生成报表准备必要的数据,数据用JRDataSource子类对象(数据源对象)的形式表示。然后,调用JasperFillManager类合适的方法,将数据源对象和之前编译生成的JasperReport对象(或.jasper文件)传递给JasperFillManager类的方法。方法返回一个JasperPrint对象,JasperPrint对象可以方便地导出为各种用户友好的其他格式文件(如:PDF,Html)或序列化到硬盘。
正如前面所说的,所有的报表的动态修改都发生在第1个步骤对JasperDesign对象的修改。为了对报表进行动态修改,程序员可以直接使用JasperReports API,API提供了各种对象用于表示jrxml的每个元素。比如:参数用JRDesignParameter对象表示,变量用JRDesignVariable对象表示,等等。各种设计元素的所有可用对象表示的完整列表,请查看jasperReports文档,可以从这儿找到。直接从代码中生成动态报表的一个例子在每个jasperReports Library发行版的NoXmlDesignApp类中,参看"demo/samples/noxmldesign/src"文件夹。
请注意jasperReports库的设计者强烈反对动态修改报表,除非项目绝对需要。因为动态修改报表有很多缺点:不能使用像iReports这样的图像化设计工具;更严重的代码耦合等等。如果你不满足于几行java代码,并乐于尝试新库,尤其当你正在直接编码而不是使用jrxml文件创建报表时,请查看一下DynamicJasper库,DynamicJasper库做了几个默认假设,抽离了JasperReports API的复杂度,DynamicJasper库可以按需覆盖。
话虽这样说,现实中程序员可能会有很多理由需要对报表进行动态修改。一种典型的情况是,报表的结构需要随着用户输入的变化而变化,比如在crosstab报表中限制列的数目。许多修改表格报表的例子已经存在。这篇文章的目的在于怎样对含有图形元素(如图表)的报表进行动态修改。
下面的例子使用了包含一个线形图表的简单报表,展示了如何在程序运行过程中修改图表的标题、副标题、高度、宽度、图表定制器类和风格。
首先来看报表的jrxml文件:
<?xml version="1.0" encoding="UTF-8"?>
<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="GenericLineChart">
<property name="author" value="Jeshurun Daniel" />
<subDataset name="sampleDataset">
<field name="category-titles" class="java.lang.String" />
<field name="series-titles" class="java.lang.String" />
<field name="data" class="java.lang.Double" />
</subDataset>
<parameter name="datasource"
class="net.sf.jasperreports.engine.data.JRMapCollectionDataSource" />
<summary>
<band height="750">
<lineChart>
<chart evaluationTime="Report" customizerClass="">
<reportElement x="0" y="0" width="555" height="350" />
<chartTitle>
<font size="16" isBold="true" />
<titleExpression><![CDATA["Title"]]></titleExpression>
</chartTitle>
<chartSubtitle>
<font size="14" isBold="true" />
<subtitleExpression><![CDATA["Sub Title"]]></subtitleExpression>
</chartSubtitle>
<chartLegend />
</chart>
<categoryDataset>
<dataset>
<datasetRun subDataset="sampleDataset">
<dataSourceExpression><![CDATA[$P{datasource}]]></dataSourceExpression>
</datasetRun>
</dataset>
<categorySeries>
<seriesExpression><![CDATA[$F{series-titles}]]></seriesExpression>
<categoryExpression><![CDATA[$F{category-titles}]]></categoryExpression>
<valueExpression><![CDATA[$F{data}]]></valueExpression>
</categorySeries>
</categoryDataset>
<linePlot isShowLines="true" isShowShapes="true">
<plot />
<categoryAxisLabelExpression><![CDATA["Category(X) - axis label"]]></categoryAxisLabelExpression>
<valueAxisLabelExpression><![CDATA["Value(Y) - axis label"]]></valueAxisLabelExpression>
</linePlot>
</lineChart>
</band>
</summary>
</jasperReport>
jrxml文件非常规范,一个线形图表嵌在summary区域内。图表有两个内嵌风格和一个子数据集。
下面我们来看看怎样动态修改报表。
第一步是加载jrxml文件到内存,返回一JasperDesign对象。请更改相应的文件路径,以反映jrxml文件在你计算机中的存放位置。
JasperDesign design = JRXmlLoader.load("report/templates/GenericLine.jrxml");
既然我们在内存中有了报表的Java对象表示,我们进而可以对其进行修改。
我们开始修改报表的页尺寸和页边空白。
design.setPageHeight(250); design.setColumnWidth(390); design.setLeftMargin(0); design.setRightMargin(0); design.setTopMargin(0); design.setBottomMargin(0); design.setPageWidth(400);
现在我们加入一些报表级风格,这些风格将被图表的标题、副标题元素使用。
// create a style element and add it to the report JRDesignStyle chartStyle = new JRDesignStyle(); chartStyle.setName("Title"); chartStyle.setDefault(false); chartStyle.setFontName("DejaVu Sans"); chartStyle.setFontSize(10); chartStyle.setPdfFontName("Helvetica"); chartStyle.setPdfEncoding("Cp1252"); chartStyle.setPdfEmbedded(false); chartStyle.setBold(true); design.addStyle(chartStyle);
下面,我们改变报表的标题和副标题。为改变标题和副标题,首先我们需要从JasperDesign对象中提取出summary区域,然后我们遍历summary区域包含的所有子元素,寻找一个JRDesignChart类型的元素。一旦我们得到图表元素,我们可以改变它的定制器类、标题和副标题,并改变它的大小。
// Extract the summary band JRDesignBand summary = (JRDesignBand) design.getSummary(); JRElement[] elements = summary.getElements(); for (JRElement element : elements) { // loop through all its elements if (element instanceof JRDesignChart) { JRDesignChart chart = (JRDesignChart) element; // get the line chart // we first remove the existing chart element from the // summary band summary.removeElement(chart); // change the customizer class chart.setCustomizerClass("ca.jeshurun.blog.reports.customizers.DynamicLineCustomizer"); // get the chart's existing title and subtitle expression and modify its text JRDesignExpression titleExpression1 = (JRDesignExpression) chart .getTitleExpression(); titleExpression1.setText("\"Dynamic title\""); chart.setTitleExpression(titleExpression1); JRFont titleFont = chart.getTitleFont(); titleFont.setBold(true); titleFont.setFontSize(12); titleExpression1 = (JRDesignExpression) chart.getSubtitleExpression(); titleExpression1.setText("\"Dynamic sub title\""); chart.setSubtitleExpression(titleExpression1); titleFont = chart.getSubtitleFont(); titleFont.setBold(true); titleFont.setFontSize(11); // change the chart's dimensions chart.setHeight(200); chart.setWidth(350); // add the chart style chart.setStyle(chartStyle); //add the chart back to the summary band summary.addElement(chart); //change the summary band's size summary.setHeight(240); //add the summary band back to the chart design.setSummary(summary); } }
图表数据作为接口JRDataSource实现类的对象实例,被传送到报表。JasperReports提供了接口JRDataSource的几种默认实现。
一种实现是JRMapCollectionDataSource,它传送maps集合格式的数据到报表。每个集合表示图表中的一个数据序列。每个map
保存了单一的值集合用于表示类别(x坐标)标签,序列(y坐标)标签和数据本身。一个这样的模拟数据源创建如下:
String[] categoryTitles = {"Category 1", "Category 2","Category 3","Category 4","Category 5"}; Double[] dataSeries1 = {2.09,1.79,1.73,2.23,2.09}; Double[] dataSeries2 = {2.48,2.16,2.21,2.01,2.66}; HashMap<String, Object> dataMap = null; Collection<Map<String, ?>> lineChartData = new ArrayList<Map<String, ?>>(); for(int i=0; i<5; i++){ dataMap = new HashMap(); dataMap.put("series-titles", "Year 1"); dataMap.put("category-titles", categoryTitles[i]); dataMap.put("data", dataSeries1[i]); lineChartData.add(dataMap); } for(int i=0; i<5; i++){ dataMap = new HashMap(); dataMap.put("series-titles", "Year 2"); dataMap.put("category-titles", categoryTitles[i]); dataMap.put("data", dataSeries2[i]); lineChartData.add(dataMap); } JRMapCollectionDataSource datasource = new JRMapCollectionDataSource(lineChartData); paramsMap.put("datasource", datasource);
定制器类是JasperReports允许程序员调整底层图标系统提供的不常用选项的方式。这个例子使用的图标系统是jFreeChart,
jFreeChar是默认的图表系统,虽然现在JasperReports也可以使用其他的图表系统。定制器类必须实现JRChartCustomizer接口,
它强制要求实现带有如下签名的方法:
public void customize(JFreeChart jfreeChart, JRChart jrChart);
当产生报表时,JasperReports库会自动调用这个方法,并传送两个对象作为customize的输入。一个对象是JasperReports的图表表示,另一个
是JFreeChart的对象表示。对于这个例子,我们旋转类别坐标(x坐标)标签90度,这样类别标签在图表中就不会过于拥挤。
package ca.jeshurun.blog.reports.customizers; import java.io.Serializable; import net.sf.jasperreports.engine.JRChart; import net.sf.jasperreports.engine.JRChartCustomizer; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.plot.CategoryPlot; /** * @author Jeshurun Daniel * */ public class DynamicLineCustomizer implements JRChartCustomizer, Serializable { private static final long serialVersionUID = -8493880774698206000L; @Override public void customize(JFreeChart jfreeChart, JRChart jrChart) { CategoryPlot plot = jfreeChart.getCategoryPlot(); CategoryAxis domainAxis = plot.getDomainAxis(); domainAxis.setCategoryLabelPositions(CategoryLabelPositions.createUpRotationLabelPositions(Math.PI / 2.0)); } }
这儿值得注意的一点,因为数据已经作为报表参数传送给报表,所以填充报表时有必要以JREmptyDataSource实例的形式传入一个
空的数据源。如果这点被忽略,JasperReports将显示一条信息提示报表没有包含数据,停止进一步处理。
本文回答了我的”如何做“的问题,但正如你看到的,报表运行时定制的可能性没有尽头,多做点工作,整个报表可以直接用代码创建,
对于那些决定远离XML的精明Java程序员,这是一个可行的选项。
译者注:原文代码下载链接不可用,译者本人使用JasperReports Library 4.7.1创建了一个Eclipse项目,修改了源代码中存在的错误,优化了代码结构,同时修改了过期的函数,以适应项目中使用的新版本JasperReports库。运行该项目会生成两个报表,一个是修改前的,另一个是修改后的。
项目下载地址:http://download.csdn.net/detail/haibinzhang/4667406
原文地址:http://blog.jeshurun.ca/technology/how-to-dynamically-modify-a-jasperreports-report