一、引言
在熙熙攘攘的Internet上制作極富個性化的主頁、及時發布令人心動的信息
,以便吸引全世界不同膚色、不同職業的人們駐"足"觀看,以致流連忘返,甚者慷慨
解囊,是Internet沖浪者心中的夢想。通過合理運用Html腳本語言提供的表格、列
表、字體和段落的格式化、多媒體支持等,能使網頁內容清楚、層次分明、可讀性
強,這些已有眾多文獻論述,在此不再贅述。
我們知道,在各種信息中,統計信息堪稱是用得較多、具有相當說服力的信息
,而表達統計數據最直觀的方式是根據這些數據繪制曲線。文獻[1]用相當的篇幅
介紹了如何在Windows操作系統中編寫一個具有OLE服務器能力的統計曲線繪制工
具,而本文的主題則是:如何利用Java對繪圖的支持,編寫一個根據統計數據繪制曲
線的小程序(Applet)Plot,通過把Plot編譯生成的字節碼文件Plot.class嵌入到H
TML腳本中,從而實現在網頁上發布統計曲線的目的。
二、Java編程概述
1.Java語言規范
Java繼續了C++的基本語法,摒棄了一些過時的特征,比如預編譯的頭文件、宏
、條件編譯,數據結構的結構體、枚舉體和聯合體,輕易導致安全性問題的指針,以
及晦澀難懂的多重繼續等,借鑒了Objective C的接口和Ada的包(package),以及內
置了Internet網絡所需要的基本特征:結構中立(任何芯片、任何操作系統可以運
行同一版本的Java程序)、安全性(避免感染病毒和防止非法訪問)、多線程和網絡
通信等。事實上,上述被Java拋棄的特征,如指針和類的多重繼續,都是由於太復雜
而遭C++程序員詛咒的;而新發展的特征,如結構中立、安全性、多線程和網絡通信
等,是C++程序員極想實現而又非常難實現的特征。
2.JDK
Windows程序員一定都知道做Windows開發要用Windows SDK(軟件開發工具),
現在,做Ja va開發的程序員也都知道編寫Java小程序要用JDK(Java開發工具)了。
然而,與SDK主要采用結構體和函數等面向過程的方式提供Windows編程接口不同,
JDK采用包、界面和類等面向對象的方式提供Java編程接口。從這種意義上說,JD
K與Microsoft Visual C++的MFC,或者Bor land C++的OWL相似:它是一個類庫,一
個應用框架,一個無縫集成的解決方案。
最先發布的JDK應該算JDK 1.0.3α,它是1995年Sun公司在全球范圍內進行Ja
va程序設計大獎賽時隨HotJava 1.0.3α發放的。在該版本中,通用包實現得比較
少,只有java.lang、j ava.util和java.io,而其他有關用戶界面和網絡通信的包
都在HotJava中提供。爾後發行了1.0版,這是提供給各Java平台開發公司的,其中
,包全部獨立出來,共八個:java.applet、ja va.awt 、java.awt.image 、java.
awt.peer 、java.io、java.lang、java.net 和java. util 以及一個sun.tools
.debug。本文的程序基於1.0版本編寫。由於1.0.3α和1.0兩個版本的包安排差別
很大,因而用前一個版本編寫的程序在後一個版本的編譯工具中編譯有可能通不過
,要作修改。在1996年8月底,Sun公司在Internet上發布了1.1版,1.1版在1.0版基
礎上稍有改進,兩個版本保持完全的兼容。
三、設計目標和程序構思
現在我們回到本文的主題上。首先,考慮編寫一個Java小程序,它的界面和功
能可以這樣來描述:
統計標題和統計數據由HTML的param標注提供,統計數據的個數沒有限制;
根據數據的取值畫出帶刻度的X、Y軸;
在用線連接每個數據點的同時標出該點的坐標值;
提供繪制三組曲線的選擇。
針對上述要求,考慮構造兩個類來完成:Curve類用於實現繪制統計曲線的各個
方面;Plo t類則派生於Applet,它是本程序的小程序類,通過使用Curve類來完成統
計曲線的繪制。鑒於篇幅,下面給出實現要點及其程序。如需要全部源代碼的讀者
,請與筆者聯系。
1.Curve類
Curve類構造主要基於以下幾點考慮。
(1)基類。Curve從Java所有類的基類Object派生,那麼就可以不顯式聲明了。
class Curve {
......
}
(2)繪圖環境。根據Windows等窗口操作系統的編程經驗,向顯示器、打印機等
輸出設備繪圖是通過繪圖環境來實現的。由於Java要面向所有平台,包括Windows
、Unix和Macintosh 等,它們的繪圖環境(如顯示器)概念很不一樣。那麼,Java如
何來實現繪圖呢?原來,Java在java.awt包中通過Graphics類來提供了對各種繪圖
設備的設備環境的抽象類封裝。用Window s編程來作對照,Graphics所代表的概念
就是Windows GDI的設備環境(Device Context),也就是MFC中CDC類或OWL的TDc類
。有了這樣的比較,對Graphics就比較好理解了。也就是,所有繪制直線、矩形、
橢圓、多邊形、設置字體、繪制文本等操作,調用Graphics類中的相應方法即可。
下面代碼是通過Curve構造器將小程序類Plot的設備環境對象g傳遞給Curve,以被
其他各個繪制方法使用:
public Curve(Graphics g)
{
myGC = g;
......
}
(3)繪制算法。繪制算法就是繪制統計曲線的實現方法,包括繪制標題、坐標
軸、數據聯線、標出數據點坐標等,以及其逆過程:清除這些繪制(因為當繪制其他
曲線時,得保證刷新前一次繪制的所有內容)。鑒於篇幅,下面僅給出顯示數據的方
法實現showData.
public synchronized void showData(Vector v)
{
float maxY=0;
myGC.setColor(Color.red);
int XPoint;
int yPoint;
//確定Vector中的最大值。
for(int i = 0; i < v.size(); i++)
{
float temp = ((Float)v.elementAt(i)).floatValue();
if(temp > maxY)
{
maxY = temp;
}
}
//確定第一個數據點的X坐標。
xPoint = xOrigin + (xSpacing/2);
int oldX = 0;
int oldY = 0;
//在數據點處繪制一個小圓圈。
for(int j = 0; j < v.size(); j++)
{
yPoint = yOrigin(int)((axisH/maxY) *
((Float)v.elementAt(j)).floatValue());
myGC.fillRect(xPoint, yPoint, 3, 3);
//在數據點之間聯線。
if((oldX != 0) && (oldY != 0))
{
myGC.drawLine(oldX-xSpacing, oldY, xPoint, yPoint)
;
}
myGC.setColor(Color.blue);
//在數據點邊上寫出數據值。
String coordString =
Float.toString(((Float)v.elementAt(j)).floatValue(
));
myGC.drawString(coordString, xPoint+5, yPoint - 4);
myGC.setColor(Color.red);
xPoint += xSpacing;
oldX = xPoint;
oldY = yPoint;
}
}
2.Plot類
Plot類構造主要基於以下幾點考慮。
(1)派生於Applet
一個Java小程序有且只有一個類派生於Applet,這就似乎在MFC中,必須有且只
有一個類派生於CWinApp一樣。另外,與MFC程序不一樣的是,不管包含派生於Appl
et類的源程序文件( .java)命名為什麼,生成的供網頁使用的字節碼文件(.class
)只與該派生類同名,而與源文件名無關,這一點輕易引起混淆。打個比方,假如包
含Plot類的文件為PlotTest.java,而編譯生成的字節碼文件名為Plot.class,而不
是PlotTest.class。最後,Plot必須聲明為public,這是由於一個編輯單元必須有
且只有一個類聲明為public。
public class Plot extends Applet
{
......
}
(2)用戶界面
目前,Java在java.awt中提供小程序的用戶界面所需要的接口和類(共42個類
和2個接口),其中實現了我們在Windows中見到的各種用戶界面,比如,菜單、對話
框,以及各種控件,如按鈕、列表框、復選框、單選框、編輯框、組合框等。鑒於
本程序要求從3組數據中選擇一組進行繪制,需要選用單選框控件。單選框在java
.awt中用Checkbox和CheckboxGroup兩個類聯合實現:當Checkbox單獨使用時,它是
復選框;而把它添加到CheckboxGroup中則變成了單選框。
public void init()
{
......
cbg = new CheckboxGroup();
//單選按鈕。
cb1 = new Checkbox("數據1", cbg, false);
cb2 = new Checkbox("數據2", cbg, false);
cb3 = new Checkbox("數據3", cbg, false);
}
(3)布局治理器
在Java中,為了使得小程序在各種操作系統中的用戶界面具有一致的外觀,采
用布局治理器(Layout Manager)對用戶界面的相對位置進行治理。Java在java.a
wt包中包含了流布局治理器(FlowLayout)、邊界布局治理器(BorderLayout)、卡
片布局治理器(CardLayout)、網格布局治理器(GridLayout)和網格包布局治理器
(GridBagLayout)5個布局治理器。假如希望3 個單選框按鈕放在繪制圖形的下面
按一行排列,該如何編寫代碼呢?可以這樣來實現:首先設置Plot小程序為邊界布局
,然後為3個單選框按鈕創建一個Panel對象cbPanel,用於組織3個控件,並設置cbP
anel為網格布局,最後將cbPanel添加到Plot類中,並設置其方向為南(South)。
public void init()
{
......
//設置Plot為邊界布局。
setLayout(new BorderLayout());
//為3個控件申請面板對象cbPanel。
cbPanel = new Panel();
//設置網格布局治理器,並按1×3方式排列。
cbPanel.setLayout(new GridLayout(1, 3));
//為各按鈕申請1個面板。
cb1Panel = new Panel();
cb1Panel.add(cb1);
cb2Panel = new Panel();
cb2Panel.add(cb2);
cb3Panel = new Panel();
cb3Panel.add(cb3);
//添加到統一的1個面板cbPanel中。
cbPanel.add(cb1Panel);
cbPanel.add(cb2Panel);
cbPanel.add(cb3Panel);
//添加cbPanel到Plot中,方向為:圖形在北,按鈕在南。
add("South", cbPanel);
}
(4)讀取HTML參數
因為本程序要用到統計數據和統計標題作為小程序的參數存放在HTML文檔中
,所以在開始繪制圖形之前,需要從HTML讀取這些參數,然後才開始顯示標題和繪制
統計曲線。其中,讀取統計標題和統計數據分別實現。
①統計標題,其格式可以是:
<param value="1-DESC" value="每月訪問者統計">
1-DESC表示第一組數據的統計標題,"每月訪問者統計"是標題內容。
public String readStringData(String s)
{
String tempString = null;
Integer param;
boolean datapresent = true;
int i = 0;
try
{
tempString = getParameter(s + "-DESC");
} catch (Exception e)
{
System.out.println(e);
}
return tempString;
}
②統計數據,格式可以是:
<param value="1-2" value="14">
1-2表示第一組數據的第二個數據點,14表示該數據點的值。
public Vector readData(String s)
{
Vector tempVector = new Vector();
Float param;
String tempData = null;
boolean datapresent = true;
int i = 0;
while(datapresent)
{
try {
tempData = getParameter(s + "-" + (i+1));
}
catch(Exception e)
{
System.out.println(e);
}
if(tempData == null)
{
datapresent = false;
} else {
param = Float.valueOf(tempData);
tempVector.addElement(param);
i += 1;
}
}
return tempVector;
}
(5)消息循環
根據Windows編程經驗,有用戶界面就有消息循環,以組織消息處理函數句柄對用戶
界面發出的消息進行響應。遺憾的是,在JDK 1.0中,尚沒有類似MFC中的消息映射
表之類的東西組織消息循環。不過,Java發展勢頭迅猛,不久的將來就會有公司將
它實現!現在,我們需要自己來組織它:Applet類有一個action方法,在其中可以通
過if...then語句來對不同的消息進行響應。
public boolean action(Event e, Object o)
{
Vector actionVector = new Vector();
String actionString = new String();
if(e.target instanceof Checkbox)
{
//"數據1"發出的消息
if(cbg.getCurrent() == cb1)
{
actionVector = readData("1");
actionString = readStringData("1");
System.out.println("1");
}
//數據2發出的消息
if(cbg.getCurrent() == cb2)
{
actionVector = readData("2");
actionString = readStringData("2");
}
//數據3發出的消息
if(cbg.getCurrent() == cb3)
{
actionVector = readData("3");
actionString = readStringData("3");
}
int count = actionVector.size();
c.clearHashMarks();
c.clearScreen();
c.drawAxes(curveWidth, curveHeight);
c.makeHashMarks(count);
c.showData(actionVector);
c.makeTitle(actionString);
repaint();
return true;
}
return false;
}
3.HTML文檔和程序演示
下面列出了一個HTML實例,用於觀察上述編制的小程序,結果參見附圖。
<html><body><center>
<applet code="Plot.class" width = 350 height = 200>
<param name="1-DESC" value="每月訪問者統計">
<param name="1-1" value="10">
<param name="1-2" value="4">
<param name="1-3" value="17">
<param name="1-4" value="24">
<param name="1-5" value="9">
<param name="1-6" value="7">
<param name="1-7" value="30">
<param name="2-DESC" value="每年訪問者統計">
<param name="2-1" value="17">
<param name="2-2" value="13">
<param name="3-DESC" value="一次購買稅統計">
<param name="3-1" value="10000">
<param name="3-2" value="130000">
<param name="3-3" value="16100">
<param name="3-4" value="14000">
</applet></center></body>
<address><IMG SRC="imageLi Zhenwen.jpg">
Li Zhenwen, [email protected]
</address></html>