JAVA—咖啡馆

——欢迎访问rogerfan的博客,常来《JAVA——咖啡馆》坐坐,喝杯浓香的咖啡,彼此探讨一下JAVA技术,交流工作经验,分享JAVA带来的快乐!本网站部分转载文章,如果有版权问题请与我联系。

BlogJava 首页 新随笔 联系 聚合 管理
  447 Posts :: 145 Stories :: 368 Comments :: 0 Trackbacks

一、iReportJasperReport简介

1.1   简介

JasperReport是报表的引擎部分,界面设计是用iReport。为什么选择这两个软件呢?因为这两个软件都是开源的,即免费的(虽然某些文档收费,但是磨灭不了我们使用它的理由)

JasperReport是一个报表制作程序,用户按照它制定的规则编写一个XML文件,然后得到用户需要输出的格式文件。它支持输出的文件格式包括PDFHTMLXMLXLSCSV等等。

JasperReport做报表,可以分为两种方式:

1、 直接利用程序定义一个报表

2、 写一个xml文件来描述这个报表,通常需要借助于iReport

JasperReport做报表时用的都是底层的东西,如线条、静态文本等,底层的好处就是强大、容易控制报表,但缺点就是编写很麻烦。庆幸的是,我们有iReport这个软件,这个软件完全是弥补JasperReport的不足,将底层编写推向可视化,大大的方便了我们。

1.2   工作流程简介

JasperReport的工作流程:首先有个xml文件,从xml文件编译出.jasper类型的文件,而以后我们真正要使用的就是编译后的jasper文件。

上图为JasperReportAPI示意图,可作为学习的参考。由iReport输出的文件中,对WEB应用可能有用的是jasper文件,当JAVA程序进行调用的时候,将把jasper文件转换成print类型,从而进一步到达其他格式的输出(如上图)

二、安装与配置

2.1下载相关软件

对于较古老的版本,这里不做过多介绍,请参考本目录下其他文献。这里提一下,古老版本需要额外下载ANT(JAVAbuild工具)以及iTextAsian.jar(用以支持中文的jar)等。

iReport http://www.jasperforge.org/jaspersoft/opensource/business_intelligence/ireport/ 下载

JasperReport http://www.jasperforge.org/jaspersoft/opensource/business_intelligence/jasperreports/ 下载

JDK http://java.sun.com/ 下载

本人用的产品版本:iReport 2.0.5-windows版本;JasperReport 2.0.5版本;JDK 1.5.0_4版本。

注:在新的iReport版本中,已经包含了JasperReportjar包,不需要下载JasperReport包了,但是,要更多的了解JasperReport,还是推荐下载完整的软件包,以学习它的源代码和例子。还有,新版本的iReport带有字体包,不需另下。

2.2 安装软件

首先安装JDK,一路NEXT即可。

其次,安装iReport,由于新版本对windows支持较好,所以和JDK一样,一路NEXT即可。

注:旧版本需要对环境变量进行配置,此篇文章介绍的版本以及更新的版本均不需要手动配置环境变量。

2.3 其他相关

您还需要准备一个数据源(如果需要做动态报表的话),大部分情况是一个数据库。笔者使用的是Oracle 9i的数据库。


三、iReport的基本使用

先看使用界面:

下面开始初步尝试。

1.      新建报表

弹出对话框如下:

 

还有许多其他的选项,中文可以理解。有个XML编码,可以手动输入,也可保持默认,点击”OK”创建新表完成。

注:iReport不会自动保存,需要手动保存,建议多保存。

2.      报表的结构

新建的报表如下:

一个报表的结构大致是几个部分:titlepageHeadercolumnHeaderdetailcolumnFooterpageFooterlastPageFootersummary以及groupHeadergroupfooter

·Title:每个报表一般会有一个名字,比如×××销售报表,title就是搁置这个名称的最好地方了,当然你也可以根据需要搁置在合适的地方。Title只在第一页出现。

·pageHeader:报表的一些公共要素,比如页码、创建时间、创建人等信息放置在这里是比较好的选择。

·columnHeader:无可非议的这里是放置列的名称,记住不是列数据。

·detial:放置需要循环输出的数据,比如销售记录数据。

·columnFooter:放置列级别的统计计算值或是列的说明。

·pageFooter:放置页级别的统计值或是页的说明。

·lastPageFooter:最后一页特殊的格式,可以放置总结等。

·Summary:可能需要对几页(你的报表可能有几个页组成)的统计值。比如50个销售记录共占用了3页,那么放置这些统计记录的统计值最好的地方就是summarySummary只在最后一页出现。

·groupHeader:每个表的内容可能需要根据某个属性进行划分显示内容和计算内容,比如希望以月份为单位每组分开显示销售记录,那么就可以定义一个组(组的定义参考后文),groupHeader就是放置组说明或是组标志最好的地方。

·Groupfooter:放置组的统计或是说明

3.      添加对象

可以通过工具栏的工具添加静态对象与动态对象。点击 可以创建静态对象,点击 可以创建动态对象。之后在报表的空白处单击,如此即可把对象添加到报表,然后拖动对象的边框,使它的大小合适,双击即可对对象中的文本进行快速编辑。

仔细观察,会发现对象的边框有两种颜色,一为蓝色,二为红色,蓝色为符合布局要求,红色反之。

注:一个对象不能横跨两个区域,即不能既在columnHeader里又在detail里。

鼠标右击对象,弹出如下菜单:

点击属性,编辑对象的属性。

这里面有很多属性,可以依据要求来更改其中的部分或所有。其中,重要的可能是关于PDF的,如下图。

红框框起来的部分要注意,当要输出中文的时候,需要类似设置。新版本对边框的更新,使我们操作起来更为方便。注意下面红框里的部分,这可以对每一个边进行分开的设置,人性化的为我们解决了大量问题。

注:JasperReport没有现成的表格,最好的方法就是用一个一个对象的边框堆积起来,组成一个表格。但是,对象的边框不能覆盖,否则HTMLEXCEL等不能正常输出。

对于动态对象,属性如下。

主要属性如图所示。在Pattern里,可以定义输出的格式,在输出时间和日期或者货币时尤为有用。上图中的”stretch with overflow”表示当填充值超出定义的大小时,自动换行;”blank when null”表示当数据为null时不显示数据,这两个在某些时候很有用处。

而在表达式一栏中,如果字体为蓝色,则表示不正确,当输入正确的时候,会呈现绿色,如图。

4.      设置数据来源

首先,需要添加一个数据源,此处选用Oracle数据库,步骤如下。

如下图,点击连结/资料来源

弹出下面对话框,点击”New”(图中已有数据源乃是笔者添加)

选择一个数据来源,这里选择第一个JDBC连接数据库的方式,点击下一步

按照图中的介绍填写相关信息,并测试信息是否正确,然后保存。iReport可以添加保存多个数据源连接信息。

注:如提示找不到相关驱动,则需要将驱动的JAR包复制到iReportlib文件夹下。

5.      变量、参数及字段

在使用iReport 的过程中会碰到很多与变量(Variables)、参数(Parameters)、字段(Fields)这些有关的内容,我们要介绍这些对象的使用和意义:

·字段(Fields):是数据源抽取出来的,希望在报表中出现的数据库内容。比如一个ID的所有值,$F{ filedsName }

·参数(Parameters):这是你的应用需要提供给报表的入口,比如你希望在报表被解释的时候提供Where语句的条件值,那么就可以使用参数(Parameters),$P{ parameterName }

·变量(Variables):这是报表中一些逻辑运算的表现,比如统计值,$V{ variablesName }

            变量

可以通过以下方式添加、修改和删除。

弹出如下对话框:

红框里的是iReport自带的供用户使用的变量,用于计算数量和总值等。点击新增,可以增加用户自己的变量,如图。

各个部分功能如标注所示。

            参数

定义参数的按钮就在定义变量的旁边,如上节,不赘述。

上图中,红框里的是系统内置的参数,供用户调用,其中包括一些常用的参数,如连接、最大值等等。点击新增按钮后,出现如下页面。

在新增参数对话框中输入参数的名称、数据类型、缺省值以及参数的描述信息等。需要注意的是,根据不同的参数类型,在设置参数的缺省值时要使值能与参数类型匹配,即字符型的参数在设置缺省值时要用””把值括起来,如果是数据值型的则不能加””

参数的引用方式有两种,,一种是$P{},另一种是$P!{},前者可以出现在任意的表达式位置,而后者则只能出现在SQL脚本里,用来替换查询语句,用以按照不同的需求替换查询语句。同时,在脚本里,也有些许区别:前者只能替换语句的部分字段,如SELECT * FROM bugs where name=$P{Name} order by proname, modulename,而对于$P!{},则可以直接在SQL脚本框里输入$P!{SQL}

            字段

编辑字段的方式与变量、参数类似,打开字段界面。

在点击新增后,出现如上页面,根据情况填写字段名称、字段类型(一般与数据来源类型相匹配)和字段的描述等。这种是手动添加字段,另一种更为方便的方法是从数据源直接读出,将在后面介绍。

字段用于动态对象里,即 。它的主要功用在于动态连接数据源,完成数据源的读出显示,所以在有动态对象的报表里,执行的时候一般采用执行报表(使用动态连接)”,如图。

6.      动态数据源

从上图中,选择默认的动态连接。

弹出如下界面,各部分功能介绍如图。

这便是上面所说的另一种更为方便的添加字段的方法,从数据源直接复制到字段里,方便、快捷。

如上图所示,还有三种连接数据源的方式:JavaBeanDataSourceCSV。多种不同的方式给我们添加了很多便利。

7.     

组是一个很重要的概念,一个报表可以多个组,每个组以一个关键字为标记,比如希望统计是根据项目(或是产品)进行统计的。那么可以设立一个项目标记的组。如图。

组的参数设定可以看界面即可理解部分,其中最主要的是“Group expression”,这里必须输入格式正确的并且存在的字段名称,本文的“Name”是【字段】中的一个元素。依此类推,建立其他的组对象。每建立一个组,在报表的界面上都会出现该组对应的段,组的HeaderFooter是对应出现的。如图。

8.      风格化

如图。

点击”Styles”,弹出如下界面。

在这里,我们可以定义许多个常用的格式,按照个人喜好进行定义,定义后,在对象属性的页面上可以选择使用哪种风格,所设置的属性会自动继承。如图:

9.      界面介绍

iReport的界面上,处于人性化的考虑,设置了很多快捷按钮供用户使用,如图。

上图包括文档的基本操作,线条与图形的绘画,静态与动态对象的添加,图表图像报表的插入,报表、栏等的属性,也包括编译、执行等按钮。

上图包括字体字号,一些对象内部的基本排列操作和数据源设置等。

上图包括对象的对齐、排列、规格化等操作。

所有这些快捷按钮给用户提供了很大的便利,熟悉之后,用起来会更加顺手。

10. 预览

iReport支持多种输出格式,其中包括PDF,HTML,XML,XLS,CSV等等。介绍一下如何进行预览和输出。

iReport默认是JRViewer输出预览,用户可以在上述菜单中进行调整和选择。

在这里,但是只在此设置输出格式了,还没有完成设置,还有一个必需的设置就是,为每一种设置选择执行环境,例如,如果你选择以PDF 输出,那么你需要为其指明Adobe Reader 的路径,如图选择菜单Options/选项,则会弹出如下属性页:

选择之后,保存即可,iReport会自动调用相关程序来完成输出和预览。

四、报表的输出

正如前面所说,报表有很多种输出方式,这里挑选一些常用的进行介绍。

iReport中,我们可以对报表进行预览,而此时,是iReport帮助我们完成了绝大部分的工作,我们并不需要输入代码来完成输出,而这里我们所说的输出是输出到文件或者输出到WEB页面等。

我们将以输出html格式为例子讲述。有人会问:这html不是静态页面吗?我说,的确是这样的。不过,html有普遍适用性,我们可以把它插入到动态页面里面去,比如说JSP或者ASP等。

动态页面的技术细节不在这里赘述,这里我们只介绍jasperReport相关,下面我们以JSP相关技术为基底来叙述。

1)        读取.jasper文件

通过iReport软件,我们可以把已经定义好的XML编译成.jasper文件,供以后使用。本人把.jasper文件放置在站点根目录下的reports文件夹内,读取文件的代码如下:

ServletContext servletContext = this.getServletContext();

        String realPath = servletContext.getRealPath("/report/done.jasper");

2)        转换成JasperReport类型

查找到.jasper文件的绝对路径后,可以对其进行读取,有很多种方式可以读取,不过笔者推荐如下方式(这也是官方例子中推荐并使用的)

JasperReport jasperReport = (JasperReport)JRLoader.loadObject(realPath);

3)        建立数据源

前面介绍过,jasperReport支持多种数据来源,比如说:数据库连接ConnectionJavaBeanCVS数据源等等。不过jasperReport提供一种通用数据源类型JRDataSource,它很多子类实现,即为多种数据来源类型。

这里,我们介绍两种普遍数据源:数据库ConnectionJavaBean

首先是Connection。通过数据库的URL、用户名、密码和驱动类来进行数据库的连接,返回Connection即可,当然,此种情况需要在iReport中定义SQL查询语句。另一种方法是,直接从Connection中完成数据库的查询,返回结果集ResultSet,继而把ResultSet封装成JRDataSource的子类JRResultSetDataSource。两种方式没有孰优孰劣,凭喜好使用即可。两种方式代码如下:

一、

Class.forName("******Driver");

        conn = DriverManager. getConnection ("***Driver:// localhost:***; DatabaseName =***; user=***; password=***");

二、

ResultSet set = statement.executeQuery();//=new ResultSet();

        JRResultSetDataSource source = new JRResultSetDataSource(set);

对于JavaBean来说。需要定义标准的JavaBean格式,即它的每一个属性都要有对应的getter/setter方法,而且,需要定义一个Factory类,其中包含一个静态的方法,返回Collection类型或者Array类型的JavaBean结果集。相比较Connection来说,JavaBean要麻烦许多,不过也要灵活许多,这才是灵活性的体现(因为有时候,查询语句会很复杂,数据来源一样会很复杂,而JavaBean无视这一切)JavaBean代码如下:

JRBeanCollectionDataSource dataSource;

        Collection rows = NumFactory.generateCollection();

        dataSource = new JRBeanCollectionDataSource(rows);

上面语句的功能应该很明显了,不赘述。

4)        生成JasperPrint对象

当数据来源确定后,下一步是生成JasperPrint对象。简单的说,JasperPrint对象就是jasperReport对象的动态填充,即把相关数据插入到报表当中。

JasperPrint rptPnt = JasperFillManager.fillReport(jasperReport, params, dataSource);

JasperFillManager类有许多的填充方法,基本囊括了各种参数的填充,包括ConnectionDataSource等的填充。

5)        输出

动态填充数据完成后,就可以进行输出了。输出时,有几种方式:输出到流,输出到文件,也可以直接生成视图等。

对于每种输出格式,JasperReport都提供一个特定的Exporter,比如JRXlsExporterJRHtmlExporter等等。这些Exporter中,可以定义相关的输出格式,有一些是必须要定义的,比如:

exporter.setParameter(JRExporterParameter.JASPER_PRINT, rptPnt);

        exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, httpOut);

当然,这是流输出的例子。JRExporterParameter中有所有格式的通用属性,而对于每种格式,有不同的ExporterParameter对象,对应着不同的参数。用户所需要的参数定义完毕后,调用exportReport()方法输出即可。

五、例子

下面是一个格式化的报表:

然后是运行之后的截图:

六、独立性与易集成性

前面可能把整个过程讲述的很复杂,其实,在整个建立与输出报表过程中,是一气呵成的。iReportJasperReport是紧密相关的,而除此之外,它们的独立性非常好。

利用jasperReport+iReport生成并输出报表,可以方便的嵌入“胖客户端”以及WEB工程,以公安项目为例。

此项目是基于Struts结构的,浏览器端的请求是通过ActionServlet来传递的,依照项目要求,我们只要把已经做好的jasperReport实例导入到项目里即可。导入方法可参考如下:

首先,把相关jar包导入到工程的lib文件夹下,搭建必需的环境;

其次,新建Action类,并将其对应的路径等相关信息加入xml配置文件中;

然后,如同输出html文件一样,将输出代码拷贝到Action类中,实现流的输出即可;

最后,将原实例中的相关处理类拷贝到项目中,调整包的路径,使之正常运作即可。

这时,只要修改某些页面的链接即可完成报表的输出。

七、其他相关问题

如何使用图片?

很容,Image控件就可以了. Image Express 里面可以用String来表示图路径, 或者用InputStream, File .不过不File 还是String , 都不得不用路径, 这显然很不灵活.决办法,穿入一个$P 的参数,表示图所在的目,然后用$P 和文件名拼接出完路径. 或者代码来控制, 比如System.getProperty("user.dir")+"\\report.jpg"就表示图路径好的方是用InputStream, his.getClass().getResourceAsStream ("report.jpg") ,这时片放在当前.jasper所在的 就可以了,不必考虑么参数,路径

动态控制Field 是否显示

Static Text, Text Field 甚至整Band 的属性里面都有Print When Expression, 比如设成new boolean(!$P{isDisplay}.equalsIgnoreCase("yes")), 那么有当参数displayyes 的时候显示。

使Sub Report, 如何使用相对路径

使用图片类似,最好使InputStream 或者传入参数。

Query里面如何使用参数

$P!{xxx} 或者 $P{xxx} 后者能用于类似PreparedStatement参数, 而前者可替换Sql 的任意部分. 在需要动态排序的时候, 前者特别有用. 比如select a,b,c from t order by $P!{orderClause} $P 还是$P!, SQL是以PreparedStatement 式执行的, 不必太担心性能问题 注意:参数是不能嵌套,比如$P{a} =''$P{b}'' , $P{b}=''value'', 不要$P{a}被替换''value''

如何使用图表(Graph)

JasperReport 没有图表功能, 有显示Image 的功能,iReport 里有个Graph 向导, 其实jFreeChart 生成Image. 另外, 更直接的做法一个Image控件, Image Express Class 设置成java.awt.Image, Image Expression 过自定义的类返回java.awt.Image.

GraphProvider.getImage($P{REPORT_DATASOURCE},title, subtitle.....);GraphProvider是自己的, public static Image getImage(JRDataSource, ....)

如果显示多个图表

在一报表上显示一个图表和显示多个图表是不同的. Query selectname,price,qty from xxx, 图显示name-price, 第二张图显示name-qty, 如果还是按上面的方, 第二张本显示不出来! 因为传入的是JRDataSource, JRDataSource仅仅是对ResultSet的简单, 处理完后, 游标已经到了eof 置了, 在开始处理第二张图的时候,就必然游标耗尽异常! ?自己JRDataSourceAdapter,JRDataSource里面的预先保存到一个Collection (相当于一个Offline的数据), 然后这个Collection传个getImage. 体是, 建一个Variable mydate, 类型java.util.Map, Calculation Type- System,Initial Value Expression JRDataSourceAdapter.JRDataSource2Map($P{REPORT_DATA_SOURCE}, new String[]{"NAME","PRICE","QTY"},

new Class[]{java.lang.String.class,java.lang.Double.class,java.lang.Double.class}); JRDataSource2Map 是自己的一个Adapter. 然后在Image Expression 里面成如GraphProvider.getImage(mydata,title, other params...), 当然得修改getImage

ExportExcel的问题

如何去掉报表头

不需要的Band 删除(高度设为0). 如果仅仅export Excel 的时候不需要报表, 而输出到PDF 然需要保, 那么使print when expression, 前面

如何让Excel 看起来整齐

不要有空白方,首先所有的Field 设成一样高, ! 所在Band 高度也设成和Field 样高, Field Band. 然后调整Field 宽度, 让每Field 都相,没有空.(如果设置正确,所有的Field 边框在鼠标点中的时候显示色,否则是绿) 最后,得设置参数

exporter.setParameter(JRXlsExporterParameter. IS_REMOVE_EMPTY_SPACE_BETWEEN _ROWS , Boolean.TRUE);

如何保留GridLine

首先, 设置参数exporter.setParameter (JRXlsExporterParameter. IS_WHITE_PAGE_BACKGROUND , Boolean.FALSE);然后,把每个Field 或者Static Text 框的''Transparent''属性都勾上

如何使字段名只显示一次

如果把字段名放在ColumnHead 区域, 那么输出到Excel, 会每个Page 都显示一遍. 在设计Report 时候, 一般会设定Page 大小. 然而对于Excel, 这个Page设定仍然存在,而且往往很讨厌, 因为在Excel , 通常希望得到连续的数据, 然而Jasper 仍然会''自作多情''进行分页. 比如说, 设计JasperReport 的时候, 设定page sizeLetter, Portrait, 那么输出到Excel 的时候每隔大约30 (具体取决于Field 的高度), page header, column header, column foot, page foot 会被重复一次,而且还附带一个高度为0 Excel Row, 表示Page Break 的地方. 把字段名放在title band , 可以解决字段名重复的问题, 当然page header也不要显示了. 如果需要, 可以把title bandprint when expression设成只有输出Excel的时候才显示为什么Excel 里面的数据是从第二行,B列开始显示的。

因为第一行和第A列分别是用来表示page top margin page left margin.对于Excel 来说, 纯粹多余. 解决方法是把page margin 设成0. 不过如果这个report 还需要以PDF 等显示, 那么设成0 就不好看了. 最好能动态的改变pagemargin. 当然,这个改变只能在外部(调用eport 的地方) 进行, 在设计Report 的时候是无能为力的. 不幸的是, JasperReport 类居然没有setMargin 的方法,只有getter.折中的方法只能是reflect . 代码示意如下:

//use reflect to set the private field of JRBaseReport

java.lang.reflect.Field margin =

JRBaseReport.class.getDeclaredField("leftMargin");

margin.setAccessible(true);

margin.setInt(jasperReport,0);

margin =

JRBaseReport.class.getDeclaredField("topMargin");

margin.setAccessible(true);

margin.setInt(jasperReport,0);

margin =

JRBaseReport.class.getDeclaredField("bottomMargin");

margin.setAccessible(true);

margin.setInt(jasperReport, 0);

如何去掉Excel 中隐藏的行

如前说述, 由于page break 的关系, Excel 中每隔几十行,就有一个高度为0 row, 即使把page botom margin设为0, page footer去掉都没有办法. 唯一的解决办法是把page height 设为很大. 同上面一样, 不得不使用reflect:

java.lang.reflect.Field pageHeight=

JRBaseReport.class.getDeclaredField("pageHeight");

pageHeight.setAccessible(true);

pageHeight.setInt(myRpt, Integer.MAX_VALUE);

八、HTMLBar3D图表输出心得

图表在ireport中是利用其他开源包生成的图片插入而生成,本人使用的版本是使用jfreechart1.0.0开源包实现。Ireportjfreechart的支持不算完美,只是实现了部分的图表生成,但对于一般项目,也是足够用的。

对于图表的数据来源,和报表一样,也有多种来源,并不局限于数据库,而本人推荐的依然是javabean。用户只要在定义好图表的各项数据(比如:横坐标数据,纵坐标数据,横坐标标签,纵坐标标签,分类标准等等)jasperreport+jfreechart会自动进行数据的分类统计输出,这点是很人性化的。

看过jasperreport的源代码,发现,图表在HTML格式输出的时候,首先是输出一张图片,或者放在具体目录下,或者放在临时的response里,然后进行调用、输出展示。(如果选用后者的输出方式,需要注意:1.x版本后的输出需要对web.xml进行配置,配置一个servlet进行输出)这样就造成了一个问题,就是当多用户同时访问页面的时候,用户看到的数据是正确的,但是图片却可能是别人产生的图片!

这可能是由于HTML浏览器对图片的引用时机不对,解决的方案是这样的:由于HTML格式的输出是字符形式的(PDF是二进制流形式的),所以我们选择首先把整个图表输出到字符缓冲区中,然后进行一次性输出,这样,我们辅助浏览器完成对图片引用时机的修正。当然,也可以这样做:通过修改源代码,把图片输出到不同的临时目录,这样的话,想引用错基本都不可能了J

HTML输出的时候,一定要记得设置编码格式,通过exporterCHARACTER_ENCODING属性来设置。而且,在HTML中输出的时候,可以不对报表进行分页操作,即取消分页。

还有,jfreechart默认输出的图片是进行抗锯齿处理过的。对于图形来说,这样会让图像显示的更圆润,而对于文字来说,可能就会显示变得模糊。解决方案:修改源代码。可以修改jasperreport的源代码,也可以修改jfreechart的源代码,只需按照如下代码进行改进即可:

jfreechart.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);

上面的代码将图片输出的文本抗锯齿关闭,而图像依然有抗锯齿处理,所以图片相对好看。不过,有一点需要注意:字体尽量是宋体,字号最好在1214之间,这样能达到最好的显示效果。

posted on 2013-08-29 10:46 rogerfan 阅读(3819) 评论(0)  编辑  收藏 所属分类: 【Java知识】【开源技术】

只有注册用户登录后才能发表评论。


网站导航: