动态修改JasperReports图表报表

        为了能够动态修改报表,了解最初由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


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值