文章摘要
JFreeReport用于生成报表。JFreeReport的数据继承自Swing组件的TableModel接口,使用基于XML的报表格式定义文件对报表进行格式化。JFreeReport生成的报表可以分页预览、打印或者保存为多种格式的文件如pdf、Excel、html等。作者在本文中详细的介绍了如何定义报表格式定义文件、如何使用JFreeReport生成/预览报表和将报表转为其它各种格式。
关键词
JFreeReport 报表生成 预览
报表生成一直是企业信息化过程中重要的一环,也是目前比较难于实现的一环,今天作者给大家介绍一种新的报表生成组件――JFreeReport。JFreeReport是JFreeReport.Org基于LGPL授权协议提供的一组java包,用于生成各类报表,JFreeReport的数据继承自Swing组件的TableModel接口,使用基于XML的报表格式定义文件对报表进行格式化。JFreeReport生成的报表可以分页预览、打印,而且支持导出为多种格式的文件如pdf、Excel、CSV、html等。更重要的是,JFreeReport不仅支持基于C/S结构的系统,而且支持基于B/S结构的系统中的在线报表显示。更详细的关于JFreeReport的介绍请大家访问JFreeReport的官方网站JFree.org。
1 环境准备
1.1 JFreeReport组件
请大家到http://search.download.csdn.net/source/1582346下载JFreeReport组件,下载的是一个ZIP文件,然后将ZIP文件解压缩到c:\jfreereport(后面的章节中将使用%jfreereport_home%表示这个目录)目录下。
1.2 JFreeReport扩展组件
请大家到http://www.jfree.org/jfreereport/jfreereport-ext-0.8.4_7.zip下载JFreeReport扩展组件,他用于支持JFreeReport组件生成的报表的在线显示。请大载后解压缩到c:\jfreereport-ext目录下(后面的章节中将使用%jfreereport_ext_home%表示这个目录)
1.3 Ant工具
Apache公司提供的一个基于JAVA的自动化脚本引擎,请大家到http://ant.apache.org/下载ant的可执行文件,关于如何使用ant请大家查看ant的帮助文档或者http://ant.apache.org/网站上的在线帮助文档。示例中主要是用ant来负责编译java代码。
1.4 作者提供的代码
为了运行本文中作者提到的例子和相关资源文件,请大家下载作者提供的vivianjDemo.zip文件和中文转换工具gb2unicode.jar。然后解压缩到%jfreereport_home%\vivianjDemo(后面的章节中将使用%demo _home%表示这个目录)目录下。
2 JFreeReport生成报表的基本步骤
我们首先演示一个简单的例子,说明使用JFreeReport生成报表的一些必要的步骤。
2.1 实例说明
在这个例子中,我们将循环生成100条数据放入TableModel中,然后使用JFreeReport组件提供的预览功能在屏幕上显示生成的报表。
[注] 为了简化,这里仅仅是逐条显示数据,不作任何修饰和统计工作,所以也不使用报表格式定义文件。
2.2 代码编制
整个演示实例(HelloWorld.java)的代码和相关注释如下,如果你执行了1.3中规定的步骤,你可以在%demo _home%/src/org/vivianj/jfreereport/看到这个文件。
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Point2D;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import org.jfree.report.Boot;
import org.jfree.report.ElementAlignment;
import org.jfree.report.JFreeReport;
import org.jfree.report.ReportProcessingException;
import org.jfree.report.elementfactory.TextFieldElementFactory;
import org.jfree.report.modules.gui.base.PreviewDialog;
import org.jfree.ui.FloatDimension;
/** */ /**
* 使用JFreeReport生成报表的简单例子,用于演示使用JFreeReport生成报表的一些基本步骤
*
* 本例子中,为了简化操作,报表定义是使用java直接编码
*
* @ 作者 : bookman
*/
public class HelloWorld {
/** *//**
* 处理窗口关闭事件
*/
protected static class CloseHandler extends WindowAdapter {
public void windowClosing(final WindowEvent event) {
System.exit(0);
}
}
/** *//**
* 创建和显示简单的报表
*/
public HelloWorld() {
// 获得创建报表需要用到的数据
final TableModel data = createData();
//获得报表要用到的报表定义内容
final JFreeReport report = createReportDefinition();
//将报表定义和数据结合
report.setData(data);
try {
//将生成的报表放到预览窗口中
final PreviewDialog preview = new PreviewDialog(report);
preview.addWindowListener(new CloseHandler());
preview.pack();
//显示报表预览窗口
preview.setVisible(true);
} catch (ReportProcessingException e) {
System.out.println(e);
}
}
/** *//**
* 创建生成报表需要用到的数据
*
* @返回一个TableModel实例
*/
private TableModel createData() {
final Object[] columnNames = new String[] { "Column1", "Column2" };
final DefaultTableModel result = new DefaultTableModel(columnNames, 100);
int rownum = 0;
for (; rownum < 100; rownum++) {
result.setValueAt("say Hello " + rownum + "次", rownum, 0);
result.setValueAt("say World " + rownum + "次", rownum, 1);
}
return result;
}
/** *//**
* 创建一个报表定义
*
* @返回一个报表定义实例
*/
private JFreeReport createReportDefinition() {
final JFreeReport report = new JFreeReport();
report.setName("A Very Simple Report");
/** *//**
* 定义要显示报表第一列的样式
*/
TextFieldElementFactory factory = new TextFieldElementFactory();
factory.setName("T1");
factory.setAbsolutePosition(new Point2D.Float(0, 0));
factory.setMinimumSize(new FloatDimension(150, 20));
factory.setColor(Color.black);
factory.setHorizontalAlignment(ElementAlignment.LEFT);
factory.setVerticalAlignment(ElementAlignment.MIDDLE);
factory.setNullString("-");
factory.setFieldname("Column1");
report.getItemBand().addElement(factory.createElement());
/** *//**
* 定义要显示报表第二列的样式
*/
factory = new TextFieldElementFactory();
factory.setName("T2");
factory.setAbsolutePosition(new Point2D.Float(200, 0));
factory.setMinimumSize(new FloatDimension(150, 20));
factory.setColor(Color.black);
factory.setHorizontalAlignment(ElementAlignment.LEFT);
factory.setVerticalAlignment(ElementAlignment.MIDDLE);
factory.setNullString("-");
factory.setFieldname("Column2");
report.getItemBand().addElement(factory.createElement());
/** *//**
* 返回一个报表定义的实例
*/
return report;
}
public static void main(final String[] args) {
// 初始化JFreeReport
Boot.start();
//调用演示实例
new HelloWorld();
}
}
2.3 运行例子
如果你执行了1.3中规定的步骤,你可以进入命令行界面,然后进入%demo_home%目录下,修改setenv.cmd中的相关设置,执行serenv.cmd设置环境变量。执行java org.vivianj.jfreereport.HelloWorld查看运行结果。下面这个图片是作者执行后结果的屏幕截图:
大家可以看到,JFreeReport已经自动帮我们实现了分页。上面这个图片显示的是第一页的数据,你可以通过工具栏中的查看其它页面中的内容。
2.4 基本步骤解释
使用JFreeReport生成报表通常需要以下三个基本步骤:
- 生成可通过TableModel接口访问的数据,如本例中的createData方法完成的功能
- 生成一个JFreeReport实例,他定义了我们如何格式化显示数据,如本例中的createReportDefinition方法完成的功能
- 将数据和JFreeReport实例连接起来,并且将该JFreeReport实例传给PreviewDialog的一个实例显示给用户
3 使用JFreeReport生成复杂报表
3.1 报表定义文件
报表定义文件是JFreeReport生成复杂报表的重要文件,他就是一个XML文档,主要描述如何使用指定的格式生成复杂的报表,同时使用报表定义文件也可以在报表格式需要修改时只需要更新该报表定义文件,而不需要修改应用代码。
3.1.1 报表定义文件分类
JFreeReport中使用了两种基于XML的报表定义文件来保存报表定义信息:简单格式和扩展格式.很明显,简单格式不能够完全的描述JFreeReport支持的全部报表定义信息,但是他更易于上手使用。而扩展格式则能够对JFreeReport的报表定义提供完整的支持,但是扩展格式太详细了,不太容易使用。
关于这两种报表定义格式文件所支持的标签内容以及如何编写这两种格式的报表定义文件请大家参考%jfreereport_home%下的jfreereport-0.8.3-A4.pdf中的相关部分,该文件附录中还包括了这两种格式的报表定义文件的DTD文档供大家参考.当然大家也JFreeReport的例子中提供了多种形式的报表定义文件,基本上涵盖了常用的报表格式定义,大家可以参考这些例子编写自己的报表定义文件,。
3.2 代码编制
这个例子和2.2中的代码基本一致,只是报表定义内容不再由java编码实现,而是由报表定义文件提供,所以调用方面稍微有点不同,详细的代码如下,请大家注意其中加粗显示的部分:
import javax.swing.table.TableModel;
import javax.swing.JFrame;
import org.jfree.ui.RefineryUtilities;
import org.jfree.report.Boot;
import org.jfree.report.modules.gui.base.PreviewFrame;
import org.jfree.report.modules.parser.base.ReportGenerator;
import org.jfree.report.JFreeReport;
/** */ /**
* 使用JFreeReport生成复杂报表的例子, 用于演示使用JFreeReport生成复杂报表的一些基本步骤
*
* 本例子中,报表定义使用了报表定义文件,该文件是保存在c:\下的report3.xml文件 本例中使用的报表定义使用了简单报表定义格式
* @ 作者 : bookman
*/
public class JFreeTest {
public JFreeTest(final String urlname, final TableModel data) {
// 创建对报表定义文件的引用
final File in = new File(urlname);
;
if (in == null) {
System.out.print(" in is null");
return;
}
System.out.print("Processing Report: " + in);
final ReportGenerator gen = ReportGenerator.getInstance();
try {
// 从报表定义文件中获得要用到的报表定义内容
final JFreeReport report1 = gen.parseReport(in);
if (report1 == null) {
System.out.print(" report1 is null");
return;
}
// 将报表定义和数据结合
report1.setData(data);
// 将生成的报表放到预览窗口中
final PreviewFrame frame1 = new PreviewFrame(report1);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.pack();
RefineryUtilities.positionFrameRandomly(frame1);
frame1.setVisible(true);
frame1.requestFocus();
} catch (Exception e) {
System.out
.print("report.definitionfailure-----------------------\r\n"
+ e);
}
}
public static void main(String[] args) {
Boot.start();
final TableModel data3 = new SampleData3();
@SuppressWarnings("unused")
JFreeTest jft = new JFreeTest("c:\\report3.xml", data3);
}
}
public class SampleData3 extends AbstractTableModel {
/** *//**
*
*/
private static final long serialVersionUID = 1L;
/** *//** Storage for the data. */
private final Object[][] data;
/** *//**
* Default constructor - builds a sample data source.
*
*/
public SampleData3() {
data = new Object[][] {
{ "Mr. Black", "1666 Pennsylvania Ave.", "012345 Washington",
"01212", "Robert A. Heinlein - Starship Trooper",
new Integer(1), new Double(12.49) },
{ "Mr. Black", "1666 Pennsylvania Ave.", "012345 Washington",
"01231", "Robert A. Heinlein - Glory Road",
new Integer(1), new Double(12.99) },
{ "Mr. Black", "1666 Pennsylvania Ave.", "012345 Washington",
"12121", "Frank Herbert - Dune", new Integer(1),
new Double(10.99) },
{ "Mr. Black", "1666 Pennsylvania Ave.", "012345 Washington",
"A1232", "Bierce Ambrose - The Devils Dictionary",
new Integer(2), new Double(19.99) },
{ "John F. Google", "12a Nowaday Road", "99999 Boston",
"12333", "Samuel Adams - How to sell tea ",
new Integer(100), new Double(10.99) },
{ "John F. Google", "12a Nowaday Road", "99999 Boston",
"88812", "Adam Smith - The wealth of nations",
new Integer(1), new Double(49.95) },
{ "John F. Google", "12a Nowaday Road", "99999 Boston",
"33123", "D. Khan - How to conquer friends",
new Integer(1), new Double(15.99) },
{ "John F. Google", "12a Nowaday Road", "99999 Boston",
"33123", "D. Khan - How to conquer friends",
new Integer(1), new Double(19.49) },
{ "Cleeve Johnson", "87 Oakham Drive", "99999 Boston", "33123",
"D. Khan - How to conquer friends", new Integer(1),
new Double(15.99) },
{ "Cleeve Johnson", "87 Oakham Drive", "99999 Boston", "33123",
"J. Ceaser - Choosing the right friends",
new Integer(1), new Double(25.99) },
{ "Cleeve Johnson", "87 Oakham Drive", "99999 Boston", "33123",
"Galileo - When to tell the truth", new Integer(1),
new Double(29.59) } };
}
/** *//**
* Returns the number of rows in the table model.
*
* @return the row count.
*/
public int getRowCount() {
return data.length;
}
/** *//**
* Returns the number of columns in the table model.
*
* @return the column count.
*/
public int getColumnCount() {
return 8;
}
/** *//**
* Returns the name of the specified column.
*
* @param column
* the column (zero-based index).
*
* @return the column name.
*/
public String getColumnName(final int column) {
if (column == 0) {
return "name";
} else if (column == 1) {
return "street";
} else if (column == 2) {
return "town";
} else if (column == 3) {
return "productcode";
} else if (column == 4) {
return "productname";
} else if (column == 5) {
return "count";
} else if (column == 6) {
return "price";
} else if (column == 7) {
return "total";
} else {
return null;
}
}
/** *//**
* Returns the data value at the specified row and column.
*
* @param row
* the row index (zero based).
* @param column
* the column index (zero based).
*
* @return the value.
*/
public Object getValueAt(final int row, final int column) {
if (column == 7) {
final Integer i = (Integer) data[row][5];
final Double d = (Double) data[row][6];
return new Double(i.intValue() * d.doubleValue());
} else {
return data[row][column];
}
}
}
4 中文乱码问题
大家在参看报表定义文件的时候会发现,里面的报表头定义中有一些显示内容,如果你直接将他修改成中文,显示在报表上就会乱码,这是因为这些报表定义文件都是XML文档,他的encoding默认设置是ISO-8859-1,所以出现了中文乱码的问题,解决办法有两个:
1.最简单的方法就是将encoding的设置修改为GB2312
2.还有一个方法就是不修改encoding的设置,而是将这些中文内容使用他们的unicode码来代替。
[注] 作者提供了一个GUI的界面提供这种转化功能,你只需要输入中文就可以获得对应的unicode码,请大家下载作者提供的gb2unicode.jar,设置好java运行环境,然后执行java -jar gb2unicode.jar就可以了。
5 总结
报表问题是企业信息化中比较关注的一个焦点,也比较难于实现客户化,作者在本文中给大家介绍了一个新的报表生成包-JfreeReport,他可以根据XML文档中定义的报表格式,生成符合客户要求的报表。文章中给出了2个简单的例子,演示了如何使用JFreeReport生成报表的详细步骤,希望能够帮助大家熟悉JFreeReport的工作方式。同时文章中给出了使用JFreeReport过程中可能遇到的中文问题的解决办法。
其实JFreeReport还支持在JSP、Servlet中显示报表,只是还需要用到他的扩展组件,作者这里没有给出例子,大家可以参考下载的JFreeReport的扩展组件中的例子。
参考资料:
JFreeReport 在线帮助文档 http://www.jfree.org/jfreereport/index.html
JFreeReport 的JavaDoc http://www.jfree.org/jfreereport/javadoc/index.html