jsp提供了很多簡單實用的工具,其中包括從數據庫中讀出數據,發送數據,并能夠把結果顯示在一個餅狀圖形?,F在讓我們看看這一簡單而實用的方法。
你所需要的東西
為了能正確運行這一文章相關的范例,你必須需要jdk 1.2或更高的版本、一個關系數據庫管理系統、一個jsp網絡服務器。我都是在tomcat調試這些例子,同時我也使用了sun java 2 sdk發布的com.sun.image.codec.jpegclasses。
數據庫設計
假設你在一家從事銷售新鮮水果的公司上班,公司出售的水果包括:蘋果、桔子、葡萄?,F在你的老板想用一個餅狀圖形顯示每一種水果的總出售量,餅狀圖形能使每一種產品的銷售情況一目了然,老板可以迅速掌握公司的產品成交情況。
表a使用了本文中的兩種數據庫列表。第一種列表(products)包含所有銷售產品的名稱;第二種列表(sales)包含每一種產品對應的銷售量。
listing a
database design
---------------
p_products table
----------------
productid int (number) not null
productname string (varchar) not null
p_sales table
-------------
saleid int (number) not null
productid int (number) not null
amount float not null
產品(products)列表包含productid和productname兩個域。銷售(sales)列表包含saleid, productid,以及總額。銷售列表中的productid提供了這兩個列表之間的關聯。銷售列表中的總額包含了每一次出售的現金數額,這些數額以浮點型數據出現。
表b中的getproducts()方法連接了兩個數據庫,并把所有的產品名稱保存在數組中:
listing b
////////////////////////////////////////////////////////////
//get products from the database as a string array
////////////////////////////////////////////////////////////
public string[] getproducts()
{
string[] arr = new string[0];
connection con;
statement stmt;
resultset rs;
int count = 0;
string sql = "select * from p_products order by productid";
try
{
//load driver: class.forname(driver);
//connect to the database with the url
con = drivermanager.getconnection(dburl , dbuid , dbpwd);
stmt = con.createstatement();
//get resultset
rs = stmt.executequery(sql);
//count the records
while(rs.next())
{count++;}
//create an array of the correct size
arr = new string[count];
//get resultset (the portable way of using rs a second time)
rs = stmt.executequery(sql);
while(rs.next())
{
arr[rs.getint("productid")] = rs.getstring("productname");
}
stmt.close();
con.close();
}
catch (java.lang.exception ex)
{
arr[0] = ex.tostring();
}
return arr;
}
我設置以下的數據庫規則:
1、productid在產品列表中最獨特,也是最關鍵;
2、productid對于第一個記錄的值為0;
3、所有之后的連續的記錄都是累加的,所以第二個記錄的productid為1,第三個記錄的productid為2,以此類推。
這些數據庫規則允許在product數組中存儲數據,如下所示:
arr[rs.getint("productid")] = rs.getstring("productname");
一些數據庫管理系統在缺省情況下就允許數據的自動累加或者自動排序。當你在設計數據庫時,一定先查明你的數據庫管理系統遵循哪些規則,比如自動累加,自動排序等。
獲取總銷售量
在多數情況下,銷售列表中會有很多個記錄,所以訪問數據庫的快捷性和高效性顯得非常重要?,F在我們只需要訪問數據庫中每一種產品的總額銷售量。
表c中的getsales()方法與數據庫連接并返回一個數組,這個數組包含每一種產品的總額出售量。
listing c
//////////////////////////////////////////////////////////// //get the sales totals from the database //////////////////////////////////////////////////////////// public float[] getsales(int products) { float[] arr = new float[products]; connection con; statement stmt; resultset rs; int count = 0; string sql = "select productid, amount from p_sales"; try { //load driver: class.forname(driver); //connect to the database with the url con = drivermanager.getconnection(dburl , dbuid , dbpwd); stmt = con.createstatement(); //get resultset rs = stmt.executequery(sql); while(rs.next()) { int product = rs.getint("productid"); //check that the productid is valid if (product >= 0 && product < products) { //add to product total arr[product] += rs.getfloat("amount"); count++; } } stmt.close(); con.close(); } catch (java.lang.exception ex) { arr[0] = -1.0f; } return arr; } |
int product = rs.getint("productid"); arr[product] += rs.getfloat("amount"); |
color piecolorarray[] = {new color(210,60,60), new color(60,210,60)…} |
curpiecolor++; if(curpiecolor >= piecolorarray.length) {curpiecolor = 0;} |
renderinghints renderhints = new renderinghints(renderinghints.key_antialiasing, renderinghints.value_antialias_on); g2d.setrenderinghints(renderhints); |
制做可調整的邊界
圖a中的餅狀圖形有一邊界,如何能改變邊界的大小呢?可以先定義int border = 10,然后計算邊界內面積的大小而實現:
ellipse2d.double elb = new ellipse2d.double(x_pie - border/2, y_pie - border/2, piewidth + border, pieheight + border);
x_pie和y_pie的值代表著包圍在餅狀圖形的正方形的左上角。我們通過邊界面積取一半(border/2)而得到餅狀圖形的中心。
圓弧(arc)理論
從java.awt.graphics 類繼承而來的fillarc()方法提供了繪制餅狀圖形各個部分(或圓弧)的簡單方法:
g2d.fillarc(x_position, y_position, width, height, startangle, sweepangle); |
x_position,和y_position整數代表著要填充的圓弧的左上角的x,y的坐標,width和heigh整數代表其具體的尺寸。如果width和height的值相等,餅狀圖形將是一個圓。如果width和height不相等,那么餅狀圖形將是一個橢圓。
fillarc()方法決定基于sweepangle整數值的圓弧的大小。如果sweepangle值是正的,則圓弧是以反時針方向繪制,反之以順時針繪制。
繪制圓弧
第一步,使用piecolor對象的getpiecolor()方法獲取最近餅狀圓弧的顏色,并把它賦予當前的圓弧::
g2d.setcolor(pc.getpiecolor()); |
接著,通過不斷循環sales[]數組并使其累加而獲得總共的銷售量:
salestotal += sales[i]; |
使用總共銷售量,可以計算出每一種產品銷售情況占總共銷售量的百分量:
float perc = (sales[i]/salestotal); |
我們計算sweepangle即可給圓弧的每一部分分配度數:
int sweepangle = (int)(perc * 360); |
每一部分圓弧畫完之后,startangle即可根據當前的sweepangle遞增。這就確保當前的圓弧部分都是以上一圓弧為開始,從而建立一個完整的餅狀圖形。
顯示圖標
圖標提供了顯示餅狀圖形中各個部分最簡潔的方式。一個圖標的大小應該與餅狀圖形中的占有量相對應。
圖b顯示了一個完整餅狀圖形及其對應各個部分的圖標,包括產品名稱、銷售總量、以及各個部分的占有量。
圖b
總結
本文講述了如何利用jsp繪制餅狀圖形的方法及算法,這些方法及算法簡單而實用,開發人員可以充分地利用這些方法。
附:本文全部源代碼
listing e
<%@ page language="java" %> <%@ page import="java.io.outputstream" %> <%@ page import="java.sql.*" %> <%@ page import="java.awt.*" %> <%@ page import="java.awt.geom.*" %> <%@ page import="java.awt.image.bufferedimage" %> <%@ page import="com.sun.image.codec.jpeg.*" %> <%! //////////////////////////////////////////////////////////// // piecolors class manages the colors used in the pie chart //////////////////////////////////////////////////////////// class piecolors { color piecolorarray[] = { new color(210,60,60), new color(60,210,60), new color(60,60,210), new color(120,60,120), new color(60,120,210), new color(210,120,60) }; int curpiecolor = 0; public color getpiecolor() { return piecolorarray[curpiecolor]; } public void setnewcolor() { curpiecolor++; if(curpiecolor >= piecolorarray.length) {curpiecolor = 0;} } } %> <%! string driver = "com.mysql.jdbc.driver"; string dburl = "jdbc:mysql://localhost/articles"; string dbuid = "myuid"; string dbpwd = "mypwd"; //////////////////////////////////////////////////////////// // get the products from the database as a string array //////////////////////////////////////////////////////////// public string[] getproducts() { string[] arr = new string[0]; connection con; statement stmt; resultset rs; int count = 0; string sql = "select * from p_products order by productid"; try { //load driver: class.forname(driver); //connect to the database with the url con = drivermanager.getconnection(dburl , dbuid , dbpwd); stmt = con.createstatement(); //get resultset rs = stmt.executequery(sql); //count the records while(rs.next()){count++; } //create an array of the correct size arr = new string[count]; //get resultset (the most portable way of using rs a second time) rs = stmt.executequery(sql); while(rs.next()) { arr[rs.getint("productid")] = rs.getstring("productname"); } stmt.close(); con.close(); } catch (java.lang.exception ex) {arr[0] = ex.tostring();} return arr; } //////////////////////////////////////////////////////////// //get the sales totals from the database //////////////////////////////////////////////////////////// public float[] getsales(int products) { float[] arr = new float[products]; connection con; statement stmt; resultset rs; string sql = "select productid, amount from p_sales"; try { //load driver: class.forname(driver); //connect to the database with the url con = drivermanager.getconnection(dburl , dbuid , dbpwd); stmt = con.createstatement(); //get resultset rs = stmt.executequery(sql); while (rs.next()) { int product = rs.getint("productid"); //check that the productid is valid if (product >= 0 && product < products) { //add to product total arr[product] += rs.getfloat("amount"); } } stmt.close(); con.close(); } catch (java.lang.exception ex) {arr[0] = -1.0f; } return arr; } %> <% //get an array that contains the product names string products[] = getproducts(); //read the data and store the totals in an array float sales[] = getsales(products.length); //declare piecolors piecolors pc = new piecolors(); //colors color dropshadow = new color(240,240,240); //inner padding to make sure bars never touch the outer border int inneroffset = 20; //set the graph's outer width & height int width = 400; int height = 200; int pieheight = height - (inneroffset * 2); int piewidth = pieheight; //to make a square (circular) pie int halfwidth = width/2; //width of the inner graphable area int innerwidth = width - (inneroffset * 2); //graph dimensions dimension graphdim = new dimension(width,height); rectangle graphrect = new rectangle(graphdim); //border dimensions dimension borderdim = new dimension(halfwidth-2,height-2); rectangle borderrect = new rectangle(borderdim); ///////////////////////////////////////////////////////////// //set up the graph //////////////////////////////////////////////////////////// //set content type response.setcontenttype("image/jpeg"); //create bufferedimage & graphics2d bufferedimage bi = new bufferedimage(width, height, bufferedimage.type_int_rgb); graphics2d g2d = bi.creategraphics(); // set antialiasing renderinghints renderhints = new renderinghints( renderinghints.key_antialiasing,renderinghints.value_antialias_on); g2d.setrenderinghints(renderhints); //set graph background color to white: g2d.setcolor(color.white); g2d.fill(graphrect); //draw black border g2d.setcolor(color.black); borderrect.setlocation(1,1); g2d.draw(borderrect); //now draw border for legend borderrect.setlocation((width/2) + 1,1); g2d.draw(borderrect); //////////////////////////////////////////////////////////////////// //draw data onto the graph: //////////////////////////////////////////////////////////////////// int x_pie = inneroffset; int y_pie = inneroffset; int border = 20; //main chart ellipse //ellipse2d.double el = new ellipse2d.double(x_pie, y_pie, piewidth, pieheight); ellipse2d.double elb = new ellipse2d.double(x_pie - border/2, y_pie - border/2, piewidth + border, pieheight + border); //shadow g2d.setcolor(dropshadow); g2d.fill(elb); //border g2d.setcolor(color.black); g2d.draw(elb); ///////////////////////////////////////////////////////////////// //calculate the total sales ///////////////////////////////////////////////////////////////// float salestotal = 0.0f; int lastelement = 0; for(int i=0; i<products.length; i++) { if(sales[i] > 0.0f) { salestotal += sales[i]; lastelement = i; } } ////////////////////////////////////////////////////////////// //draw the pie chart ///////////////////////////////////////////////////////////// //chart variables int startangle = 0; //legend variables int legendwidth = 20; int x_legendtext = halfwidth + inneroffset/2 + legendwidth + 5; int x_legendbar = halfwidth + inneroffset/2; int textheight = 20; int curelement = 0; int y_legend = 0; //dimensions of the legend bar dimension legenddim = new dimension(legendwidth , textheight/2); rectangle legendrect = new rectangle(legenddim); for(int i=0; i<products.length; i++) { if(sales[i] > 0.0f) { //calculate percentage sales float perc = (sales[i]/salestotal); //calculate new angle int sweepangle = (int)(perc * 360); //check that the last element goes back to 0 position if (i == lastelement) { sweepangle = 360-startangle; } // draw arc g2d.setcolor(pc.getpiecolor()); g2d.fillarc(x_pie, y_pie, piewidth, pieheight, startangle, sweepangle); //increment startangle with the sweepangle startangle += sweepangle; ///////////// //draw legend ///////////// //set y position for bar y_legend = curelement * textheight + inneroffset; //display the current product string display = products[i]; g2d.setcolor(color.black); g2d.drawstring(display, x_legendtext, y_legend); //display the total sales display = "" + (int)sales[i]; g2d.setcolor(color.black); g2d.drawstring(display, x_legendtext + 80, y_legend); //display the sales percentage display = " (" + (int)(perc*100) + "%)"; g2d.setcolor(color.red); g2d.drawstring(display, x_legendtext + 110, y_legend); //draw the bar g2d.setcolor(pc.getpiecolor()); legendrect.setlocation(x_legendbar,y_legend - textheight/2); g2d.fill(legendrect); //set new pie color pc.setnewcolor(); //increment curelement++; } } //////////////////////////////////////////////// // encode the graph ///////////////////////////////////////// outputstream output = response.getoutputstream(); jpegimageencoder encoder = jpegcodec.createjpegencoder(output); encoder.encode(bi); output.close(); %> |
新聞熱點
疑難解答