theunforgiven
第六章 画柱状统计图
在编码学习的过程中,我发现的问题越来越多了,有java方面的,sql方面的,html方面的,javascript方面的等等,对这些看似细小的问题的研究使我积累了实战的经验,起码不只是纸上谈兵了。
这个时候我的领导让我做一个东西,实现局域网内部网上计算机故障报修。这其实就是一个留言板的功能,我正好之前做过练习,所以很轻松的就做好了。之后我想我也许应该做一个统计——统计一年内每个月完成的报修任务量,如果用表格显示的话太简单了,不如做一个动态生成的柱状图吧,我突然有了这个想法。
马上开始动手,先是查资料,知道了java里和画图有关的是java.awt包,由于我构想的图只是由矩形组成,那么用到的方法也就这么几个:fillrect,drawrect,setcolor,setfont,drawstring。我很快发现一个问题:如何在页面显示这个图,这是个大问题,于是找例子。
在一个学过研究生java课程的同事的帮助下知道可以这样:写一个类(picture.class),这个类只负责画图,没有任何关于如何显示的语句,然后在一个页面文件(.htm文件就行)里<body>里写上这段代码:<applet code="picture" height="400" width="400"></applet>,运行这个文件就可以了。但是这个方法有这两个弊端:1、它是直接从服务器端下载picture.class,在客户端生成图片,所以客户端必须装有java环境,比如j2re等;2、现在大部分浏览器都或者迫于无奈或者被强行绑架(这里我严重鄙视一下3721和一个叫“天下搜索”的)安装了阻止小窗口、activex控件的插件——就连xp的sp2也集成了这个功能——而这个功能同样对<applet>有效。
放弃第一种方法后我在网上找到了第二个例子,第二个例子让我很奇怪,代码直接写在一个.jsp文件里,打开文件显示图片,一看这个图片的属性竟然就是这个.jsp文件的名。看了一阵子代码发现不是很理解,我开始看第三个例子。
第三个例子符合我的思维:写一个bean(或者说是一个类),把一个代表路径的字符串和一些数据传给它,它根据数据画图但是不返回(从这一点来说它不能叫做bean),而是生成一个如.jpg文件并按照传进来的路径名进行保存。然后显页面通过<img src="……">显示图片。我通过这种方式实现了工作,下面是这个类的代码:
----------------------------------picture.java------------------------------------
//该bean用于画柱状统计图
package ringz.javabeans;
import java.io.*;
import java.util.*;
import com.sun.image.codec.jpeg.*;
import java.awt.image.*;
import java.awt.*;
public class picturebean
{
bufferedimage image;
private string filelocation;
public void setfilelocation(string filelocation)//filelocation是图片的路径,如:“d:\\a\\b\\c.jpg”
{
this.filelocation=filelocation;
}
public void createimage(string filelocation)
{
try
{
fileoutputstream fos = new fileoutputstream(filelocation);
bufferedoutputstream bos = new bufferedoutputstream(fos);
jpegimageencoder encoder = jpegcodec.createjpegencoder(bos);
encoder.encode(image);
bos.close();
}
catch(exception e)
{
e.printstacktrace();
}
}
public void outgraphic(string titles,string sstr,string str[],int datas[])
{
string title=titles;
string sstr=sstr;
int imagewidth = 400;//图片的宽度 line
int imageheight;//不定长
int framefirstwidth=imagewidth-10;
int framefirstheight=25;
int framesecondwidth=imagewidth-10;
int framesecondheight;//不定长
int framespace=10;//两框间隔
int columnheight=18;//柱的粗
int columnmaxwidth=framesecondwidth-20;//柱的最大长度,也是代表数值最大的那个柱的长度
int sp=30;//柱的间隔
int num=datas.length;//数组的长度
int datas[]=new int[num];//得到数组的数值
string name[]=new string[num];
for (int i=0;i<num;i++)
{
datas[i]=datas[i];
name[i]=str[i];
}
//得此数组中的最大值
int max=datas[0];
for (int j=0;j<num;j++)
{
if(datas[j]>max)
max=datas[j];
}
//得到代表数值的柱的各自高度,实际数值*columnmaxheight/max
int columnwidth[]=new int[num];//不定长,柱的长度
for (int k=0;k<num;k++)
columnwidth[k]=(datas[k]*columnmaxwidth)/max;//取整
framesecondheight=(sp+columnheight)*num+10;//+10为了留出一块底边
imageheight=framesecondheight+framefirstheight+framespace+10;//多加10为了画阴影
picturebean chartgraphics = new picturebean();
chartgraphics.image = new bufferedimage(imagewidth, imageheight, bufferedimage.type_int_rgb);
graphics g = chartgraphics.image.getgraphics();
g.setcolor(color.white);
g.fillrect(0,0,imagewidth,imageheight);//用白色涂整个图
color framefirstcolor = new color(20,50,100);
color columncolor = new color(153,19,19);
color shadowcolor = new color(200,200,200);
g.setcolor(shadowcolor);
g.fillrect(0+7,0+7,framefirstwidth,framefirstheight);//阴影在原框基础上移7
g.setcolor(color.white);
g.drawrect(0,0,framefirstwidth,framefirstheight);//画第一个框
g.setcolor(framefirstcolor);
g.fillrect(0+1,0+1,framefirstwidth-1,framefirstheight-1);
g.setfont(new font("仿体", 0 , 14));
g.setcolor(color.white);
g.drawstring(title,10,18);//写字
g.drawstring(sstr,300,18);
int framesecondy=1+framefirstheight+framespace;
g.setcolor(shadowcolor);
g.fillrect(0+7,framesecondy+7,framesecondwidth,framesecondheight);//阴影在原框基础上移7
g.setcolor(color.black);
g.drawrect(0,framesecondy,framesecondwidth,framesecondheight);//画第二个框
g.setcolor(color.yellow);
g.fillrect(0+1,framesecondy+1,framesecondwidth-1,framesecondheight-1);//填充第二个框
for(int l=0;l<num;l++)
{
g.setcolor(color.black);
int texty=framesecondy+20+(sp+columnheight)*l;
g.drawstring(name[l]+"("+datas[l]+")",0+10,texty);//写文字
if (columnwidth[l]!=0)
{
g.setcolor(columncolor);
g.drawrect(10,texty+5,columnwidth[l],columnheight);//画柱的外框//框的上边离文字的底边为5
g.fillrect(10+2,texty+5+2,columnwidth[l]-3,columnheight-3);//画柱
}
}
chartgraphics.createimage(filelocation);
}
}
--------------------------------------------------------------------------------
但是接下来出现了一个让我难以忍受的事:自做聪明的浏览器缓存使得页面无法在短时间内更新图片——输入2004,显示了2004的图片,马上再输入2005,可是显示的仍然是2004的图片,但这时硬盘目录下的图片已经是2005的图片了,一般来说两次操作时间间隔大约少于3秒,则总是显示缓存里的那张图。这个问题困扰我很长时间,问了很多人试了很多方法都没有解决了。
很显然上面提到的第二个方法不存在此问题,我决定采用这种方法,所以我不得不回头研究它的代码,之后我发现这几句代码是显示图片的关键,而最下面的三句是和显示图有关的:
---------------------------------------------------------
response.setcontenttype("image/jpeg");
bufferedimage bi = new bufferedimage(width, height, bufferedimage.type_int_rgb);
graphics2d bicontext = bi.creategraphics();
……
outputstream output = response.getoutputstream();
jpegimageencoder encoder = jpegcodec.createjpegencoder(output);
encoder.encode(bi);
---------------------------------------------------------
我手头仅有一本电子版的《java2参考大全》,而令我苦恼的是在里边我竟然找不到bufferedimage、graphics2d、jpegimageencoder这些字样;另外,上一个例子里是graphics,它和graphics2d有什么差别呢?这也让我很困惑。但是我终于决定要试一试,把两个例子综合一下,最终得到了下面这个worklord.jsp文件:
-----------------------------------worklord.jsp----------------------------------
<%@ include file="include.inc"%>
<%@ page contenttype="text/html;charset=gb2312"%>
<%@ page import="java.io.outputstream" %>
<%@ page import="java.util.*"%>
<%@ page import="java.awt.image.bufferedimage" %>
<%@ page import="java.awt.*" %>
<%@ page import="com.sun.image.codec.jpeg.*" %>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en" "http://www.w3.org/tr/html4/loose.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=gb2312">
<title>工作量统计</title>
<style type="text/css">
<!--
body {
margin-left: 10%;
margin-right: 10%;
}
.style2 {font-size: 24px}
-->
</style></head>
<body>
<%
//得到当前的年
java.text.simpledateformat formatter = new java.text.simpledateformat("yyyy");
java.util.date currenttime_1 = new java.util.date();//得到当前系统时间
string yearnow = formatter.format(currenttime_1);
string year=null;
try
{
year=request.getparameter("select");
}
catch(exception e){}
if (year==null)
year=yearnow;
//string y=integer.tostring(year);
int sum=0;
string mon[]=new string[12];
mon[0]=year+"-01";
mon[1]=year+"-02";
mon[2]=year+"-03";
mon[3]=year+"-04";
mon[4]=year+"-05";
mon[5]=year+"-06";
mon[6]=year+"-07";
mon[7]=year+"-08";
mon[8]=year+"-09";
mon[9]=year+"-10";
mon[10]=year+"-11";
mon[11]=year+"-12";
int datas[]=new int[12];
connection con = null;
statement stmt = null;
resultset rs = null;
try
{
class.forname(classforname);//载入驱动程式类别
con=drivermanager.getconnection(servanddb);//建立数据库连接
stmt=con.createstatement();
string sql="select count(*) from record where com_time like "+year+"%"+"";
rs=stmt.executequery(sql);
if (rs.next())
sum=rs.getint("count(*)");
for (int i=0;i<12;i++)
{
sql="select count(*) from record where com_time like "+mon[i]+"%"+"";
rs=stmt.executequery(sql);
if (rs.next())
datas[i]=rs.getint("count(*)");
else
datas[i]=0;
}
rs.close();
stmt.close();
con.close();
}
catch(exception e)
{
out.print(e);
}
if (sum!=0)
{
string title=year+"年度工作量统计图";
string sstr="总和:"+sum;
string name[]={"一月份","二月份","三月份","四月份","五月份","六月份","七月份","八月份","九月份","十月份","十一月份","十二月份"};
int num=datas.length;//数组的长度
//得此数组中的最大值
int max=datas[0];
for (int j=0;j<num;j++)
{
if(datas[j]>max)
max=datas[j];
}
int imagewidth = 400;//图片的宽度 line
int imageheight;//不定长
int framefirstwidth=imagewidth-10;
int framefirstheight=25;
int framesecondwidth=imagewidth-10;
int framesecondheight;//不定长
int framespace=10;//两框间隔
int columnheight=18;//柱的粗
int columnmaxwidth=framesecondwidth-20;//柱的最大长度,也是代表数值最大的那个柱的长度
int sp=30;//柱的间隔
//得到代表数值的柱的各自高度,实际数值*columnmaxheight/max
int columnwidth[]=new int[num];//不定长,柱的长度
for (int k=0;k<num;k++)
columnwidth[k]=(datas[k]*columnmaxwidth)/max;//取整
framesecondheight=(sp+columnheight)*num+10;//+10为了留出一块底边
imageheight=framesecondheight+framefirstheight+framespace+10;//多加10为了画阴影
//开始画图
response.setcontenttype("image/jpeg");
bufferedimage image = new bufferedimage(imagewidth,imageheight,bufferedimage.type_int_rgb);
graphics g = image.creategraphics();
g.setcolor(color.white);
g.fillrect(0,0,imagewidth,imageheight);//用白色涂整个图
color framefirstcolor = new color(20,50,100);
color columncolor = new color(153,19,19);
color shadowcolor = new color(200,200,200);
g.setcolor(shadowcolor);
g.fillrect(0+7,0+7,framefirstwidth,framefirstheight);//阴影在原框基础上移7
g.setcolor(color.white);
g.drawrect(0,0,framefirstwidth,framefirstheight);//画第一个框
g.setcolor(framefirstcolor);
g.fillrect(0+1,0+1,framefirstwidth-1,framefirstheight-1);
g.setfont(new font("仿体", 0 , 14));
g.setcolor(color.white);
g.drawstring(title,10,18);//写字
g.drawstring(sstr,300,18);
int framesecondy=1+framefirstheight+framespace;
g.setcolor(shadowcolor);
g.fillrect(0+7,framesecondy+7,framesecondwidth,framesecondheight);//阴影在原框基础上移7
g.setcolor(color.black);
g.drawrect(0,framesecondy,framesecondwidth,framesecondheight);//画第二个框
g.setcolor(color.yellow);
g.fillrect(0+1,framesecondy+1,framesecondwidth-1,framesecondheight-1);//填充第二个框
for(int l=0;l<num;l++)
{
g.setcolor(color.black);
int texty=framesecondy+20+(sp+columnheight)*l;
g.drawstring(name[l]+"("+datas[l]+")",0+10,texty);//写文字
if (columnwidth[l]!=0)
{
g.setcolor(columncolor);
g.drawrect(10,texty+5,columnwidth[l],columnheight);//画柱的外框//框的上边离文字的底边为5
g.fillrect(10+2,texty+5+2,columnwidth[l]-3,columnheight-3);//画柱
}
}
try
{
//输出图
outputstream output = response.getoutputstream();
jpegimageencoder encoder = jpegcodec.createjpegencoder(output);
encoder.encode(image);
output.close();
}
catch(exception e)
{
e.printstacktrace();
}
}//if
else
{%>
<table width="100%">
<tr>
<td width="407">
<span class="style2"><font color="#ff0000">没有<%=year%>年的记录!</font></span>
</td>
</tr>
</table>
<%
}
%>
</body>
</html>
----------------------------------------------------------------------------------
现在任务是完成了,其中的最关键的部分代码是实现什么功能的也大概知道了,可是还是没有掌握其中的知识,不能不说是遗憾。
另外,worklord.jsp这样的页面很特殊,就是因为这部分代码引起的:
outputstream output = response.getoutputstream();
jpegimageencoder encoder = jpegcodec.createjpegencoder(output);
encoder.encode(image);
这导致了这个页面没法再干别的了,也就是说,假如你要在<body>里干点别的,像写几个字,放一个<form>什么的,页面显示不出来。
本章最后,感谢那两个例子的作者,而且图的风格都是抄袭第二个例子那位仁兄的:),但是没有记住你们的名字很遗憾。
文章整理:站长天空 网址:http://www.z6688.com/
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




