程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JBuilder 2005開發Applet游戲全接觸

JBuilder 2005開發Applet游戲全接觸

編輯:關於JAVA

引言

一張湘繡匯集了湘女累月的心血,我們稱之為勞動密集型,一塊芯片集聚著眾多高新的科技,我們稱之為技術密集型,一個實例承載了豐富的知識點,是否可以稱為知識密集型呢:)?用一張網撈到更多的魚是漁夫的追求,通過一個實例學到更多的知識點則是我們這些開發人員的企盼。

本文擬通過一個耳熟能詳的指法練習游戲講解如何在JBuilder 2005下開發Applet應用程序,通過本文,你將可以學習到圖形用戶界面開發、動畫處理、聲音播放、事件處理、多線程、I/O讀寫、Applet打包、Applet安全模型、數字簽名、JRE插件制作、JDK5.0等方面的知識,並適時介紹筆者一些開發經驗。

指法練習Applet游戲介紹

1、界面及功能

指法練習的Applet游戲的界面如下圖所示:

圖 1 指法練習的用戶界面

如上圖所示,這個Applet共由11個組件組成,左邊的主界面是畫布Canvas組件,被分隔為10個欄。程序會隨機在這些欄中產生下落的字母,用戶按下匹配的字母鍵盤按鍵後,即為擊中,相應的字母將消失,正確數遞增1;字母落到畫布底端後,還沒有被擊中,失敗數遞增1;每產生一個下落的字母,總數遞增1。

游戲提供了3個JButton的按鈕,分別用於控制游戲的開始/暫停、結束以及保存游戲成績。在未啟動游戲前第一個按鈕顯示為三角箭頭的圖標,點擊後啟動游戲,隨後按鈕圖標切換為暫停的圖標。而第二個為停止按鈕,其上顯示結束的圖標,當游戲處於運行或暫停的狀態時,點擊該按鈕將停止游戲以便重新開始。而第三個按鈕保存游戲的成績到客戶端的D:\result.txt文件中。

整個界面采用BorderLayout布局管理器,畫布位於BorderLayout.CENTER區,而右邊的控制台JPanel位於BorderLayout.EAST區。控制台的JPanel采用GridLayout布局管理器。

2、程序組成

每個下落的字母對應一個線程實例,稱為DropCharThread線程,它由一個產生器定時產生出來,這個產生器也是一個線程稱為GenerateDropThread線程,下面是這個Applet的類圖:

圖 2 Applet類圖

TypeTrainApplet類繼承了JApplet,是游戲的主類,DropCharThread和GenerateDropThread都是其內部類,後兩者都繼承Thread,以線程的方式運行,下面對這3個類重要的成員變量和成員方法進行說明。

1).TypeTrainApplet

繼承JApplet的Applet主類,負責構造用戶界面、響應用戶操作事件、更新游戲統計數據等。

· 重要成員變量

統計數據變量

volatile int totalCount = 0;//生產下落字母的總數。
volatile int rightCount = 0;//正確擊中的字母數。
volatile int errorCount = 0;//未被擊中且到達畫布底部的字母數。

這3個變量用volatile作了修飾,這是因為這3個變量會被每個字母下落線程更改,為防止各個線程通過各自的緩存更改變量值造成線程間值的不同步,需要將這3個變量設置為volatile的類型,這樣這些變量的更改值對於其他線程馬上可見。

字母下落速率控制變量

private static int stepLen = 2; //每次下落的步長,即字母每移一步的象素。
private static int stepInterval = 50; //每兩步之間的時間間隔,以毫秒為單位。
private static int columnCount = 10; //畫布被分隔為多個欄
private static int generateInterval = 500; //創建一個新的下落字母線程的時間間隔,以毫秒為單位

Applet通過通過這4個變量達到控制產生字母的快慢和字母下落的速度及欄數,可以進一步規劃這些值,以形成游戲的難度級別。有鑒於此,我們特地將這些參數的值通過HTML的<Applet>參數傳入。這樣,只需要更改HTML的<applet>參數值就可以達到控制游戲難度級別的目標,而不需更改Applet程序。

其他

int colWidth; //下落字母每欄的寬度,在運行期才獲取這個變量值,它由畫布的寬度和欄數決定。
volatile char pressKeyChar; //記錄當前按鍵對應的字母。
int statusCode = 0; //記錄游戲所處的狀態,其中1:運行態、,2:暫停態 0:停止態。

· 重要成員方法

private void drawResult()//將統計結果寫到界面的對應JLabel中。
private void resetGame()//重置游戲現場

2) DropCharThread

是一個線程,將一個隨機的字母在畫布的特定欄中往下落下,並實時檢測是否被擊中,如果擊中馬上消失,否則一直落到畫布的底部。

·重要成員變量

char c; //對應的字母
int colIndex; //對應畫布的欄序號,第一欄為1,第二欄為2,以此類推
int x, y; //當前字母在畫布中的坐標

·動作類型常量

private static final int ACTION_DRAW_FONT = 1; //表示畫字符
private static final int ACTION_CLEAR_FONT = 2; //表示清除字符

不應當直接用1或2表示動作的類型,而應該定義一個更有意義的常量,這樣不但於理解,也便於以後的維護。

·重要成員方法

public DropCharThread(char c, int colIndex)//構造函數,傳入特定的字母和欄序號
private void draw(int actionType)//在畫布中特寫的位置上畫字母

3) GenerateDropThread

·重要成員變量

Random random = new Random(); //負責產生隨機數

·重要成員方法

private char getRandomChar()//獲取一個隨機的字母

負責定時產生一個DropCharThread線程實例,通過generateInterval成員變量控制產生DropCharThread線程實例的頻率。

當游戲玩家點擊Applet的開始按鈕後,Applet將啟動游戲,這3個類之間的交互關系可以通過以下的順序圖來描述,如下圖所示:

圖 3 開始游戲的順序圖

1)當用戶按下Applet的開始按鈕後激發一個事件。

2) Applet響應這個事件,調用事件響應方法,在方法中實例化一個GenerateDropThread線程,並啟動這個線程。

3) GenerateDropThread線程定時產生一個DropCharThread線程,並讓賦予一個隨機的字母和欄序號。

4)DropCharThread線程啟動,將字母在特定的欄中從上至下落下。

程序框架

1、利用向導生成Applet

首先創建一個工程(File->New...->Project->雙擊Project頁中的Project圖標),我們將工程名取為game,然後利用下面的步驟,調用Applet向導生成TypeTrainApplet。

1) 啟動Applet向導

File->New...->Web->雙擊Web頁中Applet的圖標啟動共4步的Applet向導。

2) 向導第1步,填寫Applet的詳細信息。

圖 4 Applet向導第1步

·ClassName:Applet的類名,填入TypeTrainApplet

·Package:包名,接受默認值

·Base Class:父類,有兩個選項,一個是java.applet.Applet,另一個是javax.swing.JApplet。前者以AWT為基礎,而後者以Swing為基礎。如果客戶端浏覽器的JRE版本很低,且你不希望客戶下載額外的插件,則需要考慮用java.applet.Applet,且不能應用高版本JDK中的特性,這裡我們用javax.swing.JApplet。

·Generate header comments:在產生Applet代碼時,產生類標題頭的注釋說明,你大可不必生成這些注釋。

·Can run standalone:是否將Applet設置為可獨立運行,如果勾選,JBuilder為其生成了一個main函數,這樣就可以在脫離浏覽器或AppletViewer的情況下,像一般可運行類一樣運行這個Applet中的功能,我們不勾選它。

·Generate standard methods:是否生成Applet的標准函數,大家都知道Applet通過4個函數管理著Applet的生命周期,它們分別是init()、start()、stop()、destroy()。如果不勾選這個選項,向導只會生成init()方法,而其他3個方法不會生成。在我們的例子中,需要用到其他3個方法,所以需要勾選。

按Next到下一步。

3) 定義Applet的參數

Applet的參數是指通過網頁中<applet>標簽的<param>指定的參數值,這些值可以在Applet類中引用到。這樣就允許在不改變Applet程序的情況下,僅通過網頁中<applet>屬性值的更改控制Applet的表現。我們在這一步中為Applet設置4個控制變量參數,如下圖所示:

圖 5 為Applet設置參數

這一步的設置,不但為網頁生成了參數聲明,還為Applet程序生成了從網頁獲取參數值的方法,在Applet初始化時,即將網頁中的參數值賦給Applet的成員變量。

點擊Add Parameter新增一行,聲明一個新的參數,其中Name*為網頁中參數的名字,而Variable*為Applet類成員變量名,通過Type*欄設置成員變量的數據類型。你還可以為參數在Default欄中指定一個默認的值,在Desc中給出描述說明信息,其中帶*的欄是必填的欄。

點擊Next到下一步。

4) 設置包含這個Applet的網頁

在這一步裡,我們指定包含這個Applet網頁的<applet>標簽的一些屬性,如下圖所示:

圖 6 設置引用Applet的網頁

JBuilder會生成一個引用Applet的HTML網頁,網頁名字和Applet的類名相同,網頁通過<applet>標簽引用Applet,網頁的標題及<applet>屬性值在這一步中設置。

我們除將Height從默認的300調整為400,其他的都保持不變。按Next到最後一步。

5) 創建運行配置項

在這一步裡JBuilder允許你決定是否為Applet生成一個運行配置項,運行配置項是允許你配置運行時的有關屬性,如運行的入口類,在運行時是否重新編譯等,你也可以通過Project->Project Properties...->Run來維護運行配置項。

圖 7 設置Applet運行配置信息

點擊Finish完成Applet的創建向導。此時JBuilder為這個Applet生成了兩個文件,一個是TypeTrainApplet.java程序文件,而另一個是引用這個Applet的TypeTrainApplet.html網頁。我們來看這兩個文件的主要結構。

代碼清單 1 TypeTrainApplet.html 引用Applet的網頁表

1. <html>
2. <head>
3.  <meta http-equiv="Content-Type" content="text/html; charset=GBK">
4.  <title>
5.   HTML Test Page
6.  </title>
7. </head>
8. <body>
9. game.TypeTrainApplet will appear below in a Java enabled browser.<br>
10. <applet
11.   codebase = "."
12.   code = "game.TypeTrainApplet.class"
13.   name = "TestApplet"
14.   width = "400"
15.   height = "400"
16.   hspace = "0"
17.   vspace = "0"
18.   align = "middle"
19. >
20.  <param name = "stepLen" value = "2">
21.  <param name = "stepInterval" value = "50">
22.  <param name = "columnCount" value = "10">
23.  <param name = "generateInterval" value = "500">
24. </applet>
25. </body>
26. </html>

在向導第2步所設置的Applet參數悉數在網頁中定義(第20~23行),在向導第3步中設置的Applet屬性反映在第11~18行中。

Applet類的TypeTrainApplet.java文件代碼如下所示:

代碼清單 2 TypeTrainApplet.java

1. package game;
2.
3. import java.awt.*;
4. import java.awt.event.*;
5. import java.applet.*;
6. import javax.swing.*;
7.
8. public class TypeTrainApplet1 extends JApplet {
9.  boolean isStandalone = false;
10.  BorderLayout borderLayout1 = new BorderLayout();
11.  int stepLen;
12.  int stepInterval;
13.  int columnCount;
14.  int generateInterval;
15.
16.  //Get a parameter value
17.  public String getParameter(String key, String def) {
18.   return isStandalone ? System.getProperty(key, def) :
19.   (getParameter(key) != null ? getParameter(key) : def);
20. }
21.
22. //Construct the applet
23. public TypeTrainApplet1() {
24. }
25.
26. //Initialize the applet
27. public void init() {
28.  try {
29.   stepLen = Integer.parseInt(this.getParameter("stepLen", "2"));
30.  } catch (Exception e) {
31.   e.printStackTrace();
32.  }
33.  try {
34.    stepInterval = Integer.parseInt(this.getParameter("stepInterval",
35.            "50"));
36.  } catch (Exception e) {
37.   e.printStackTrace();
38.  }
39.  try {
40.   columnCount = Integer.parseInt(this.getParameter("columnCount",
41.          "10"));
42.  } catch (Exception e) {
43.   e.printStackTrace();
44.  }
45.  try {
46.   generateInterval = Integer.parseInt(this.getParameter(
47.             "generateInterval", "500"));
48.  } catch (Exception e) {
49.   e.printStackTrace();
50.  }
51.  try {
52.   jbInit();
53.  } catch (Exception e) {
54.   e.printStackTrace();
55.  }
56. }
57.
58. //Component initialization
59. private void jbInit() throws Exception {
60.  this.setSize(new Dimension(400, 300));
61.  this.getContentPane().setLayout(borderLayout1);
62. }
63.
64. //Get Applet information
65. public String getAppletInfo() {
66.  return "Applet Information";
67. }
68.
69. //Get parameter info
70. public String[][] getParameterInfo() {
71.  java.lang.String[][] pinfo = { {
72.   "stepLen", "int", "每次下落的步長"}, {
73.   "stepInterval", "int", "每移動一個像素的間隔時間,以毫秒為單位"}, {
74.   "columnCount", "int", 分成多少列"}, {
75.   "generateInterval", "int", 分成多少列"},
76.  };
77.  return pinfo;
78. }
79.
80. //static initializer for setting look & feel
81. static {
82.  try {
83.   //UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
84.  } catch (Exception e) {
85.  }
86. }
87. }

其中第11~14行定義了對應向導第2步所定義的參數變量,第70~78行獲取參數的注釋信息。在Applet通過init()初始化,在init()中調用方法將網頁中參數的值賦給Applet類的成員變量,以初始化變量的值。在第59~62行設定了Applet的大小,其值應該和網頁中<applet>的width和height屬性值一致。

2、設計Applet界面

打開TypeTrainApplet.java,切換到Design視圖頁面中,設計如下的Applet界面

圖 8 Applet界面設計

承繼JApplet的Applet其默認的布局管理器是BorderLayout,首先在其東區(BorderLayout.EAST)放置一個infoPnl的JPanel組件,將infoPnl的布局管理器設置為GridLayout,9行1列,即在jbInit()方法中通過infoPnl.setLayout(new GridLayout(9, 1));設置。再在infoPnl上依次放置9個組件,這些組件的類型和用途分別說明如表所示:

表 2 組件說明

組件名 組件類型 用途 jButton1 JButton 開始/暫停按鈕 jButton2 JButton 結束按鈕 jButton3 JButton 保存按鈕 totalLbl_1 JLabel 總數標簽 totalLbl_2 JLabel 總數值顯示標簽 rightLbl_1 JLabel 正確數標簽 rightLbl_2 JLabel 正確數值顯示標簽 errorLbl_1 JLabel 錯誤數標簽 errorLbl_2 JLabel 錯誤數值顯示標簽

你只要從設計器窗口左邊的組件庫中用鼠標將組件拖到設計窗口的相應位置,並放開鼠標就可以了相應生成用戶界面的代碼了。

由於畫布組件沒有java.awt.Canvas並沒有列在JBuilder的組件面板中,你可以直接通過編碼的方式把畫布組件放到Applet的中區(BorderLayout.CENTER),了可以點擊JBuilder可視化設計器的Bean選擇器圖標 (位於組件庫的上方),在彈出的Bean Chooser對話框中選擇java.awt.Canvas,如下圖所示:

圖 9 通過Bean選擇器選擇Canvas組件

在Bean Chooser對話框中有一棵以包組織的類樹,選擇Canvas類,再點擊OK按鈕,在可視化設計器的Bean選擇器的下拉菜單中將出現java.awt.Canvas的類,如下圖所示:

圖 10 Bean選擇器中的Canvas組件類

點擊下拉菜單中的java.awt.Canvas,鼠標移到Applet設計界面的中心點擊一下,一個畫布組件就被添加到Applet的中區去了。在組件樹中選中這個Applet中,將其命名為canvas,並確認其Constaints屬性是CENTER(即位於中區)。

3、游戲的統計數據

游戲包括3個統計數據,即已產生字母的總數,被正確擊中的字母數及未被擊中的字母數。需要有3個變量來保存這些統計數據,同時還需要一個方法,將數據寫到Applet界面的統計標簽組件中去。當用戶點擊開始按鈕時調用resetGame()方法將這些統計數據歸0。

我們在TypeTrainApplet中添加以下粗體的代碼。

代碼清單 3 統計數據

1. …
2. public class TypeTrainApplet extends JApplet
3. {
4.  …
5.  volatile int totalCount = 0;//總數計數器
6.  volatile int rightCount = 0;//正確數計數器
7.  volatile int errorCount = 0;//錯誤數計數器
8.  public TypeTrainApplet()
9.  {}
10.  …
11.  //將統計結果畫到界面上
12.  private void drawResult()
13.  {
14.   totalLbl_2.setText("" + totalCount);
15.   rightLbl_2.setText("" + rightCount);
16.   errorLbl_2.setText("" + errorCount);
17.  }
18.  //重置現場
19.  private void resetGame()
20.  {
21.   totalCount = 0;
22.   rightCount = 0;
23.   errorCount = 0;
24.   drawResult();
25.  }
26.  …
27. }

drawResult()方法以下兩種情況下都應被調用:

·擊中一個字母。

·一個字母下落到底端。

而resetGame()方法在點擊開始按鈕後調用,將3個統計變量歸零,以便重新開始統計。

主體程序

1、字母下落線程

游戲界面中每一個下落的字母對應一個字母下落線程DropCharThread的實例,這個線程負責將一個隨機的字母在指定的畫布欄中從上至下落下。在TypeTrainApplet內部定義這個線程類,之所以要將其作為成員內部類來定義,是因為這樣可以減少類和類之間的通信,降低調用接口的復雜度。DropCharThread需要訪問到TypeTrainApplet的眾多成員,作為內部類就可以直接訪問TypeTrainApplet類的成員變量了。其代碼如下所示:

代碼清單 4 DropCharThread字母下落線程

1. …
2. public class TypeTrainApplet extends JApplet {
3. …
4. private class DropCharThread extends Thread {
5.  char c; //對應的字符
6.  int colIndex; //在哪列下落
7.  int x, y; //行列的坐標
8.  private static final int ACTION_DRAW_FONT = 1; //畫字符
9.  private static final int ACTION_CLEAR_FONT = 2; //清字符
10.  public DropCharThread(char c, int colIndex) {
11.   this.c = c;
12.   this.colIndex = colIndex;
13.   this.x = (colIndex - 1) * colWidth + colWidth / 2; //所在的橫坐標
14.  }
15. //線程方法
16.  public void run() {
17.   draw(ACTION_DRAW_FONT);
18.   try {
19.    while (c != pressKeyChar && y < canvas.getHeight() && statusCode != 0) {
20.     synchronized (canvas) {
21.      while (statusCode == 2) {
22.       canvas.wait();
23.      }
24.     }
25.     draw(ACTION_CLEAR_FONT);
26.     y += stepLen;
27.     draw(ACTION_DRAW_FONT);
28.     Thread.sleep(stepInterval);
29.    }
30.   } catch (InterruptedException ex) {
31.   }
32.
33.   pressKeyChar = ' ';
34.   draw(ACTION_CLEAR_FONT);
35.   if (statusCode != 0) {//游戲沒有停止
36.    totalCount++; //統計總數
37.    if (y < canvas.getHeight()) {
38.     rightCount++; //擊中
39.    } else {
40.     errorCount++; //打不中
41.    }
42.    drawResult();
43.   }
44.  }
45.
46. /**
47. * 畫字母
48. * @param actionType 1:畫字母 2: 清除字母
49. */
50. private void draw(int actionType) {
51.  synchronized (canvas) { //必須對資源canvas進行同步,否則會產生線程不安全
52.   Graphics g = canvas.getGraphics();
53.   if (actionType == ACTION_CLEAR_FONT) {
54.    g.setXORMode(canvas.getBackground()); //清除
55.   }
56.   g.setFont(new Font("Times New Roman", Font.PLAIN, 12));
57.   g.drawString("" + c, x, y);
58.  }
59.  }
60. }
61. …
62. }

由於這個類比較關鍵,邏輯也比較復雜,為了方便說明,我們將其流程通過一個流程圖來描述,如下圖所示:

圖 11 字母下落線程流程圖

1) 首先在欄序號為colIndex的欄的第一個位置畫出保存在變量c中的字母(第17行)。

2) 當這個字母未被擊中,未到達畫布底部且用戶未結束游戲進行循環,這步判斷對應程序的19行。如果這個判斷條件通過進入第3步,即進入循環體,否則轉到第5步。

3) 如果被暫停,這個線程進入等待態,直接被通知後才繼續運行。需要指明一點的是,所有字母下落線程都用畫布對象canvas進行同步,即用canvas進行通訊。線程間要進行通訊時,一定需要通訊線程都可以訪問到的對象充當媒介將這些線程"串"起來,通過這個對象的notify()/notifyAll()/wait()在線程間通訊。這個對象好比一個"月下老人",在線程的情人間傳遞音訊。

4) 當線程被喚醒後,或原來就沒有等待,則進入下一個循環的處理過程,在這個過程中,程序將原來位置的字母清除,下移縱坐標,並在新的位置畫字母,以達到字母下落的動畫效果,然後下落線程睡眠指定的毫秒數,毫秒數值為TypeTrainApplet成員變量stepInterval的值,而這個值可以在網頁的<param name = "stepInterval" value = "50">標簽中定義,達到控制下落速度的效果。

因為在畫布上畫字母後,這個字母並不會自動消失,如果直接移動縱坐標並在新位置畫字母,原位置的字母依就存在。所以在新位置畫字母之前,必須先將舊位置的字母清除。我們用了一個小技巧,即使用Graphics對象的setXORMode()方法,該方法兩圖像重疊部分的顏色。我們調用這個方法將圖像重疊部分的顏色設置為畫布的背景色,這樣在原來的位置上再次畫字母時,因為前後兩次畫個字母剛好重疊,就達到了清除原位置字母的效果。

畫字母和清除字母的程序相似,我們把它抽出到一個方法中draw(int actionType),如第50~59行代碼所示,通過入參決定是清除還是畫新字母。為增強程序的可讀性,我們在第8~9行中定義了兩個用於表示清字母和畫字母的動作常量。

5) 當程序出了循環體後,進行善後的處理:將用於保存用戶按鍵字母的pressKeyChar變量置為空字符,在畫布上清除移到底部的字母。如果游戲沒有結束統計數據,並將數據寫到界面的JLabel組件中。

2、添加擊中音效

擊中字母後播放一個短促的聲音,將能大大提高游戲的聽覺體驗,這在節裡,我們對字母下落線程稍作更改,以使其支持音效。

首先准備一個聲音文件hit.wav,放在TypeTrainApplet.java相同的文件夾下。Applet類中定義了一個getAudioClip(URL url)方法,通過這個方法可以獲取AudioClip的聲音文件的對象。通過AudioClip的play()即可播放這個音效。

代碼清單 5

1. …
2. import java.applet.AudioClip;
3. public class TypeTrainApplet extends JApplet {
4.  …
5.  AudioClip hitSound;//聲明音效對象
6.  …
7.  public void init() {
8.   …
9.   hitSound = getAudioClip( (TypeTrainApplet.class).getResource(
10.             "hit.wav"));//初始化音效對象
11.  }
12.  …
13.  private class DropCharThread extends Thread {
14.   …
15.   public void run() {
16.    …
17.    draw(ACTION_CLEAR_FONT);
18.    if (statusCode != 0) { //游戲沒有停止
19.     totalCount++; //統計總數
20.     if (y < canvas.getHeight()) {
21.      hitSound.play();//擊中時播放音效
22.      rightCount++; //擊中
23.     } else {
24.      errorCount++; //打不中
25.     }
26.     drawResult();
27.    }
28.   }
29.  }
30.  …
31. }

在第5行定義一個音效的對象,在Applet初始化時獲取音效對象,如第9行所示。更改字母下落線程,當擊中下落的字母時播放音效,如第21行所示。

3、字母下落線程的產生器線程

指法練習需要"子子孫孫,無窮匮也"地不斷產生字母下落線程,以使游戲持續進行,這個工作由產生器線程GenerateDropThread負責。GenerateDropThread線程出於和DropCharThread同樣的原因,也作為TypeTrainApplet成員內部類來定義,其代碼如下所示:

代碼清單 6 GenerateDropThread 產生器線程

1. …
2. public class TypeTrainApplet extends JApplet {
3.  …
4.  private class GenerateDropThread extends Thread {
5.   Random random = new Random(); //隨機數
6.   public void run() {
7.    try {
8.     while (statusCode != 0) { //產生下落線程
9.      synchronized (canvas) {
10.       while (statusCode == 2) {
11.        canvas.wait();
12.       }
13.      }
14.      DropCharThread dropCharThread = new DropCharThread(
15.       getRandomChar(),
16.       random.nextInt(columnCount) + 1);
17.       dropCharThread.start();
18.       Thread.sleep(generateInterval);
19.      }
20.     } catch (InterruptedException ex) {
21.     }
22.    }
23.
24. /**
25. * <b>功能說明:</b><br>
26. * 返回一個隨機字符
27. * @return 隨機字符
28. */
29.    private char getRandomChar() {
30.     int temp = 97 + random.nextInt(26);
31.     return (char) temp;
32.    }
33.   } …
34.  }

這個線程很簡單:定期創建並啟動一個DropCharThread字母下落線程。需要特別說明的是如何為字母下落線程提供一個隨機字母和一個隨機欄序號。我們通過一個隨機對象java.util.Random的nextInt(int range)方法產生一個0~range-1的整數作為隨機欄序號,在第29~32行定義了一個隨機產生字母的getRandomChar()方法,因為小寫字母a~z的ASCII代碼是97~112,第30行即得到一個小寫字母所對應的ASCII代碼,通過第31行強制類型轉換就可獲取一個隨機的小寫字母字符。

在每次循環時,都判斷游戲是否被暫停,如果暫停,則線程進入睡眠,暫停產生字母下落線程,如第8~13行所示。為了統一游戲總體的控制,所以這個線程也通過canvas對象進行同步,在其他地方調用canvas.notifyAll()方法後,暫停的線程就蘇醒出來,繼續工作。 

在第18行,線程睡眠一小段時間,通過TypeTrainApplet的generateInterval成員變量就可以控制字母下落線程下落的速度,這個參數可以直接通過網頁<param name = "generateInterval" value = "500">指定其值。

4、響應用戶按鍵事件

所謂擊中下落的字母,即是用戶按下鍵盤中的一個鍵所對應的字母和某個字母下落線程的字母是一致的,對應的字母下落線程結束並將擊中數遞增1。

要讓游戲自動監測到用戶所按的按鍵,就需要Applet響應鍵盤按鍵事件,下面我們來為Applet生成按鍵事件的處理方法。

打開TypeTrainApplet.java,切換到Design視圖頁中,在結構窗格的組件樹中選擇this(BorderLayout)節點,切換屬性查看器到Event標簽頁中,雙擊keyPressed項,如下圖所示:

圖 12 為Applet生成響應按鍵的事件處理方法

此時,JBuilder為Applet生成了一個按鍵事件監聽器,並切換到Source視圖頁並將光標定位到事件處理方法中,在方法中鍵入如下粗體的代碼。

1. …
2. public class TypeTrainApplet extends JApplet {
3.  …
4.  /**獲取用戶點擊按鍵所對應的字符*/
5.  public void this_keyPressed(KeyEvent e) {
6.   if (!e.isActionKey()) {
7.    pressKeyChar = e.getKeyChar();
8.   }
9.  }
10.  …
11. }

第6行判斷按鍵是否字符的按鍵,如果是在第7行中獲取按鍵所對應的字符。

控制游戲

至此,我們已經完成了Applet主要功能的開發,剩下的工作是如何通過按鈕控制游戲。在編寫控制代碼之前,先為開始/暫停按鈕(jButton1)和停止按鈕(jButton2) 裝飾一下,再編寫控制代碼。

1、為按鈕添加圖標

需要准備3張按鈕的圖標,圖標為gif格式,尺寸大小為25×24象素。

·:jButton1在結束和暫停狀態的圖標,命名為start.gif。

·:jButton1在游戲處於運行狀態的圖標,命名為pause.gif。

·:jButton2的圖標,命名為stop.gif。當游戲處於暫停或運行狀態時,jButton2才被激活。

將這些文件放置在TypeCharApplet.java源文件的目錄下,即<工程根目錄>/src/game目錄下。

下面的代碼使用java.awt.ImageIcon引用這3個圖標,並在jbInit()中將圖標顯示到按鈕上,如下所示:

代碼清單 7 定義3個圖標變量

1. …
2. public class TypeTrainApplet extends JApplet {
3.  …
4.  ImageIcon startIcon = new ImageIcon(TypeTrainApplet.class.getResource("start.gif"));
5.  ImageIcon pauseIcon = new ImageIcon(TypeTrainApplet.class.getResource("pause.gif"));
6.  ImageIcon stopIcon = new ImageIcon(TypeTrainApplet.class.getResource("stop.gif"));
7.  …
8.  private void jbInit() throws Exception {
9.   …
10.   jButton1.setIcon(startIcon);//設置開始按鈕的圖標
11.   jButton2.setIcon(stopIcon);//設置停止按鈕的圖標
12.   jButton2.setEnabled(false);//將停止按鈕圖標置為非激活態
13.   …
14.  }
15. }

第4~6用前面所述的圖片初始化3個圖標變量,其中TypeTrainApplet.class.getResource()方法以TypeTrainApplet.class所在目錄為相對目錄,查詢資源文件。

第10~11行分別將開始和結束圖標顯示到對應的按鈕上,當用戶點擊開始按鈕後,才將jButton1的圖標切換為暫停的圖標pauseIcon。

2、通過按鈕事件控制游戲

由於字母下落線程通過監測statusCode的值決定結束或暫停,所以我們僅需要通過按鈕事件更改這個控制變量就可以達到控制游戲的目的了。

首先,我們打開TypeTrainApplet.java切換到Design的UI設計界面中,雙擊jButton1按鈕,JBuilder自動為jButton1添加一個按鈕點擊事件監聽器,並切換到Source視圖中,將光標定位到事件處理方法處,我們在方法中添加以下粗體的代碼:

代碼清單 8 開始/暫停按鈕事件處理方法

1. …
2. public class TypeTrainApplet extends JApplet {
3.  …
4.  public void jButton1_actionPerformed(ActionEvent e) {
5.   if (statusCode == 0) { //從結束->開始
6.    resetGame();
7.    statusCode = 1;
8.    colWidth = canvas.getWidth() / columnCount;
9.    //實例化字母下落線程產生器
10.   GenerateDropThread gdThread = new GenerateDropThread();
11.   gdThread.start();//產生器啟動
12.   jButton1.setIcon(pauseIcon);//切換為暫停的圖標
13.   jButton2.setEnabled(true);//停止按鈕激活
14.  } else if (statusCode == 1) { //從運行->暫停
15.   statusCode = 2;
16.   jButton1.setIcon(startIcon);
17.  } else { //從暫停->運行
18.   statusCode = 1;
19.   jButton1.setIcon(pauseIcon);
20.   synchronized (canvas) {//通過canvas通知所有暫停的線程繼續運行
21.   canvas.notifyAll();
22.  }
23. }
24. this.requestFocus();//Applet接受光標,以便其接受按鍵事件
25. }
26. …
27. }

在jButton1的按鈕點擊事件處理方法裡根據statusCode所標識的游戲狀態分別進行處理:

·當statusCode=0時,游戲原處於結束或未開始的狀態,表示用戶執行開始游戲的命令。開始一個新游戲的命令,將統計數據歸0,根據畫布當前的寬度和欄數計算出每欄的寬度,實例化一個產生器線程,並切換按鈕的圖標為暫停圖標,將停止按鈕置為激活態。

·當statusCode=1時,游戲原處於運行態,表示用戶執行暫停的命令。更改狀態並更換按鈕的圖標。

·當statusCode=2時,游戲原處於暫停態,表示用戶執行暫停後繼續游戲的命令。更改狀態並更換按鈕圖標,通過canvas對象通知所有暫停的線程。

其次,給停止按鈕jButton2生成以下的事件響應代碼:

代碼清單 9 停止游戲的事件處理代碼

1. …
2. public class TypeTrainApplet extends JApplet {
3.  …
4.  public void jButton2_actionPerformed(ActionEvent e) {
5.   statusCode = 0;
6.   synchronized (canvas) {
7.    canvas.notifyAll();
8.   }
9.   jButton2.setEnabled(false);
10.   jButton1.setIcon(startIcon);
11.  }
12.  …
13. }

首先更改游戲的狀態,在第6~7行向所有處於等待狀態的線程發出一個通知,防止線程"睡死"的情況。線程在循環體的判斷語句中判斷出statusCode為0後將紛紛退出,所有線程結束。而後,將按鈕置為非激活狀並將開始/暫停按鈕切換為開始的圖標。

3、保存游戲統計數據

為了演示通過數字簽名技術突破Applet安全限制的方法,我們特地設計了一個功能:將游戲的統計數字寫入到客戶端機器的D:\result.txt文件中。在UI設計界面中雙擊jButton3的按鈕,為"保存"按鈕添加如下的事件處理方法:

代碼清單 10 保存按鈕事件處理方法

1. …
2. import java.io.*;
3. public class TypeTrainApplet extends JApplet {
4.  …
5.  public void jButton3_actionPerformed(ActionEvent e) {
6.   FileWriter fw = null;
7.   try {
8.    File file = new File("d:\\result.txt");
9.    fw = new FileWriter(file);
10.    fw.write("總數:" + totalCount + "\n");
11.    fw.write("正確數:" + rightCount + "\n");
12.    fw.write("失敗數:" + errorCount);
13.    fw.flush();
14.    JOptionPane.showMessageDialog(this, "成績成功保存到d:\result.txt中",
15.              "信息",JOptionPane.OK_OPTION);
16.    } catch (IOException ex) {
17.     ex.printStackTrace();
18.    } finally {
19.     try {
20.      if (fw != null) {
21.       fw.close();
22.      }
23.     } catch (IOException ex1) {
24.      ex1.printStackTrace();
25.     }
26.    }
27.   }
28.   …
29.  }

至此,我們就完成了整個游戲的開發過程,Rebuild事件工程,在<工程根目錄>/classes文件夾下雙擊打開Applet向導為我們所生成的TypeTrainApplet.html網頁,假如你機器已經安裝了JRE,我們的指法練習游戲將在網頁中打開,點擊開始按鈕玩伙自己親手制作的游戲,如下圖所示:

圖 13 在網頁中運行指法練習Applet游戲

點擊停止按鈕停止游戲,試著點擊"保存"按鈕,將統計數據保存到D:\result.txt中,你將會在Java控制台中看到一個安全異常信息,如下圖所示:

圖 14 Applet的功能被安全管理器限制

因為一般的Applet運行在稱為"沙盒"的安全模塊下,Applet雖然在客戶端機器上運行,但至多只是一個"外來客",客戶機沒有將其當作"自家人"來對待。所以Applet不能執行訪問本地文件系統、執行本地程序,保存統計數據到文件也就發生異常了。在本章後面,我們將詳細介紹如何通過數字簽名的技術來繞過Applet的安全限制,讓客戶機將這個"外來客"賓至如歸。

4、關注Applet生命周期

Applet在浏覽器中運行時,第一次加載Applet,將調用init()方法,接著調用start(),當窗口關閉或頁面替換時先調用stop()然後再調用destroy()。

因為我們的游戲是多線程的程序,當關閉浏覽器時,如果Applet的字母下落線程還在運行可能會引發異常。在JBuilder中右擊TypeTrainApplet.html,在彈出的菜單中選擇Run using default,JBuilder使用AppletViewer運行TypeTrainApplet。啟動游戲後直接關閉窗口,在信息窗格中將報告以下的異常信息:

java.lang.NullPointerException

at game.TypeTrainApplet$DropCharThread.draw(TypeTrainApplet.java:290)

at game.TypeTrainApplet$DropCharThread.run(TypeTrainApplet.java:258)

這是由於關閉AppletViewer後,TypeTrainApplet畫布的Graphics對象先被銷毀,而字母下落線程依然調用訪問這個對象,所以拋出空指針異常。

我們可以通過Applet的生命周期解決這個問題:Applet在被關閉前會調用stop()和destroy()方法。我們只要利用stop()方法就可以了,在stop()方法中置一個標識,線程通過判斷這個標識就可以知道當前窗口是否關閉,當發現關閉時就不再運行。

1. public class TypeTrainApplet extends JApplet {
2.  …
3.  boolean isClose = false;//用於標識Applet窗口有沒有關閉
4.  …
5.  public void start() {
6.   isClose = false;
7.  }
8.
9.  public void stop() {
10.   statusCode = 0;//停止游戲
11.   isClose = true;//窗口關閉
12.  }
13.  …
14.  private class DropCharThread extends Thread {
15.   if(isClose) return ;//發現窗口關閉馬上返回
16.   draw(ACTION_DRAW_FONT);
17.   try {
18.    while (c != pressKeyChar && y < canvas.getHeight() &&
19.           statusCode != 0) {
20.     synchronized (canvas) {
21.      while (statusCode == 2) {
22.       canvas.wait();
23.      }
24.     }
25.     draw(ACTION_CLEAR_FONT);
26.     y += stepLen;
27.     draw(ACTION_DRAW_FONT);
28.     Thread.sleep(stepInterval);
29.    }
30.    } catch (InterruptedException ex) {
31.   }
32.   if (!isClose) {//窗口沒有關閉才後續處理
33.    draw(ACTION_CLEAR_FONT);
34.    if (statusCode != 0) { //游戲沒有停止
35.     totalCount++; //統計總數
36.     if (y < canvas.getHeight()) {
37.      hitSound.play();
38.      rightCount++; //擊中
39.     } else {
40.      errorCount++; //打不中
41.     }
42.     drawResult();
43.    }
44.   }
45.  }
46. }

Applet啟動時調用start()方法,這方法裡將窗口關閉標識置為false,如第6行所示,而當窗口關閉時stop()方法被調用,停止游戲並置窗口關閉標識,如第10~11行所示。字母下落線程的程序也要作相應的調整,在進入線程和結束線程都判斷是否關閉了窗口。

打包並進行數字簽名

浏覽器對Applet的數字簽名支持並沒有一個統一的標准,但是一些著名的浏覽器如IE和Navigator對進行數字簽名的Applet都可以開放大部分的權限。要對Applet進行數據簽名必須先將Applet類和資源打成一個JAR包。

JBuilder提供一個Applet的打包向導,在向導的最後一步可以指定一個數據證書對最終生成的JAR包進行簽名。所以在這一節裡,我們先介紹數字簽名的技術,而後再講解如何使用JBuilder的Applet打包向導。

1、數字簽名技術

數字簽名涉及到以下幾個基本的概念:

·消息摘要

消息摘要是對原始數據按照一定算法進行計算得到的結果,它主要檢測原始數據是否被修改過。消息摘要與加密不同,加密是對原始數據進行變換,可以從變換後的數據中獲得原始數據,而消息摘要是從原始數據中獲得一部分信息,它比原始數據少得多,因此消息摘要可以看作是原始數據的指紋。

·消息驗證碼

當甲和乙通信時,甲將數據傳給乙時,同時也將數據的消息摘要傳給乙,乙收到後可以用該消息摘要驗證甲傳的消息是否正確。這時會產生問題,即若傳遞過程中別人修改了數據時,同時也修改了消息摘要。乙就無法確認數據是否正確。消息驗證碼可以解決這一問題。 使用消息驗證碼的前提是 甲和乙雙方有一個共同的密鑰,這樣甲可以將數據計算出來的消息摘要加密後發給乙,以防止消息摘要被改。由於使用了共同的密鑰,所以稱為"驗證碼"。

·數字簽名

使用消息摘要和消息驗證碼兩種技術可以保證數據沒有經過改變,但接收者還無法確定數據是否確實是某個人發來的。盡管消息碼可以確定數據是某個有同樣密鑰的人發來的,但這要求雙方具有共享的密鑰,若有一組用戶共享,我們就無法確定數據的來源了。

數字簽名即是被設計用來解決這個問題的技術。數字簽名利用非對稱加密技術,發送者使用私鑰加密數據產生的消息摘要(簽名),接收者使用發送者的公鑰解密消息摘要以驗證簽名是否是某個人的。由於私鑰只有加密者才有,因此如果接收者用某個公鑰解密了某個消息摘要,就可以確定這段消息摘要必然是對應的私鑰持有者發來的。

·數字證書

使用數字簽名的前提是接收數據者能夠確信驗證簽名時(用發送者的私鑰加密消息摘要)所用的公鑰確實是發送者本人的,這需要通過數字證書來解決這個問題。

數字證書含有兩部分數據:一部分是對應發送者(組織或個人)的信息,另一部分是這個發送者所對應的公鑰。即數字證書保存了發送者和其公鑰一一對應的關系。同樣,數字證書也有可能被假造,有效的數字證書必須經過權威 CA的簽名,即權威CA驗證數字證書的內容的真實性,然後再在數字證書上使用權威CA自己的私鑰簽名,相當於在發送者的證書上蓋章。

其實數字簽名技術是現實生活在計算機領域的反映,我們不妨通過一個小故事將這此技術反向映射到生活中。

《永不消失的電波》是60年代一部著名的電影,講述了1938年我黨地下組織在國統區上海的電台被敵人破壞了,延安解放區我軍電台政委李俠奉命前往的上海,加強秘密電台的工作,為了保證上海地下黨組織能夠正確接洽到真實的李俠而不被敵人蒙騙,我們不妨來設定這樣的一個情節,李俠身上帶一封介紹其身份的介紹信,這相當於消息摘要,還約定了接頭暗號:地下黨接頭中說"山上杜鵑紅艷艷",李俠則要接"山下溪水細潺潺",那麼這個暗號就是"消息驗證碼"。而在李俠的介紹信上有延安電台台長的簽名,這個就是"數字簽名",簽名之上還加蓋了一個延安電台的公章,這個就是數字證書。還有一份證名電台公章的文件那就是CA證書了。當然實際情況可能只需要一個暗號,太多東西反而會在行動中暴露身份,但在計算機領域這些東西都是必要的。

2、數字證書的生成

在JBuilder的Applet打包向導中僅需要一個數字證書,向導會為最終的JAR包生成消息摘要、消息驗證碼並簽名。通過JDK自帶的Keytool工具可以為生成一個數據證書,這個工具位於JDK的bin目錄下。

打開DOS命名窗口,定位到JBuilder 2005下自帶的JDK的bin目錄下,執行下面的Keytool命名生成一張自己的證書:

C:\Borland\JBuilder2005\jdk1.4\bin>keytool -genkey -alias chenxhCA -keyalg RSA -keystore superCALib -validity 3650

命令窗口將要求你輸入一些個人信息如下圖所示:

圖 15 生成數字證書的命令窗口

這裡我們使用keytool工具生成了一個名為chenxhCA的證書,它存放到superCALib證書庫中,有效期為10年,使用的加密算法上RSA。證書庫superCALib的訪問密碼是123456,而chenxhCA證書條目的訪問密碼是123123。在輸入作為發送者身份標識的信息後就會在當前目標,即C:\Borland\JBuilder2005\jdk1.4\bin下生成一個名為superCALib的證書庫文件。

keytool參數較多,使用也比較復雜,詳細使用說明,請參見Sun網站的幫助文檔:

http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html。

一般情況下你還需要將該證書發給權威的CA簽名,這個證書才會被視為合法的證書,當然你也可以模擬創建一個CA證書,用這個CA證書為我們將用於簽發Applet的chenxhCA證書簽名,為了簡單起見我們忽略這一步。

3、打包

現在已經萬事俱備了,我們可以開始利用JBuilder的打包向導將Applet所以文件打包並簽名的過程。

1) File->New...->Archive,在Archive頁中雙擊Applet JAR圖標啟動Applet打包向導。

2) 在向導第1步中指定Applet JAR的名字和保存到目標文件,如下圖所示:

圖 16 指定文件名

為了加速網絡下載速度,我們勾選上Compress the contents of the archive選項,壓縮JAR文件,減小文件的體積。Always create archive when building the project選項使用每次編輯工程時都重新創建Applet JAR包。點擊Next到下一步。

3) 在這一步裡,指定JAR文件中所需包含的資源文件。

由於TypeTrainApplet程序引用了3張圖片,所以JAR文件除包含TypeTrainApplet.class程序文件外,還需要將用於按鈕圖標的文件選擇進來,如下圖所示:

圖 17 指定JAR的內容

按Next到下一步。

注意:

當你指定game.TypeTrainApplet.class,start.gif,pause.gif,stop.gif,hit.wav時,打成的Applet JAR包將不能正確運行,那些和TypeTrainApplet類位於同一程序文件的事件監聽器類將被排除在外,所以需要通過game/*.*來打包。

4) 由於向導第3~6步,我們不需要作特別的設置,所以一直按Next到第7步。

在這一步裡,我們用上一小節中生成的數字證書簽名Applet的目標JAR文件,如下圖所示:

圖 18 指定如何對JAR進行簽名

·Digitally sign this archive選項在默認的情況下是未選中的,首先勾選該選項

·點擊Keystore後的…按鈕,選擇我們剛才在C:\Borland\JBuilder2005\jdk1.4\bin目錄下所生成的superCALib證書庫文件。

·在Keystore password中輸入123456,即證書庫的密碼。

·點擊Alias後的…按鈕,由於我們在superCALib證書庫中僅有一個chenxhca證書,所以在彈出的Select Alias對話框的Available Alias列表中僅有一個chenxhca選項,選擇chenxhca證書。

·在Alias password中輸入123123,即chenxhca證書的私鑰密碼。

·在Store type中輸入JKS,由於Keytool工具的默認證書庫類型是JKS,所以superCALib的類型為JKS。

在設置完以後的信息後,按Finish結束向導,在工程窗格的資源樹中將出現一個TypeTrainJAR的節點。右擊這個節點,在彈出的菜單中選擇Rebuild,JBuilder將創建Applet的JAR包,並用chenxhca證書簽名。

Rebuild完成後,工程窗格的TypeTrainJAR節點就可以展開了,展開這個節點,我們發現目標JAR文件中除了資源文件以外,在META-INF文件夾下還有3個文件,如下圖所示:

圖 19 目標JAR中關於簽名的文件

META-INF文件夾下的3個文件是和數字簽名有關的文件,說明如下:

·MANIFEST.MF:這個 manifest 文件定義了與擴展和包相關的數據。

·CHENXHCA.SF:這是 JAR 文件的簽名文件,文件名標識了簽名者。

·CHENXHCA.RSA:與簽名文件相關聯的簽名程序塊文件,它存儲了用於簽名 JAR 文件的公共簽名。

4、在文件中引用Applet包文件

我們現在來更改TypeTrainApplet.html中<applet>的屬性使其通過JAR來引用Applet程序。這個過程很簡要,打開TypeTrainApplet.html文件,切換到Source視圖頁中,將光標定位在<applet>標簽中。窗口右邊出現<applet>標簽的屬性輸入編輯器,在archive中輸入game.JAR,按回車。

圖 20 更改網頁的<applet>標簽屬性

JBuilder為<applet>標簽添加archive的屬性。由於game.JAR文件位於工程根目錄下,而TypeTrainApplet.html文件位於工程目錄的classes子文件夾下,所以需要將TypeTrainApplet.html拷貝到工程根目錄下,這樣archiver="game.JAR"的屬性聲明才是正確的,因為在尋找程序資源時,是以TypeTrainApplet.html所在目錄為相對路徑的。

保存後,到工程目錄下雙擊TypeTrainApplet.html文件,IE檢測到網頁中包含了經過簽名的Applet程序,彈出一個安全警告的對話框,如下圖所示:

圖 21 IE在運行簽名的Applet前的安全警告

由於我們的證書沒有經過權威機構的簽名認證,所以對話框提示"此安全證書是由不可信的公司簽發的"信息。需要指出的是游覽器的JRE版本不同,彈出的警告對話框並不相同,上圖是JRE版本為1.5.0時的警告對話框。

可以通過點擊"更多詳細信息"按鈕查看證書的信息,如下圖所示:

圖 22 簽名證書的信息

點選"簽發人"項,將可以看到證書執有者的個人信息。關閉這個對話框,回到"警告-安全"對話框中,點擊"是"接受這個簽名的Applet。IE就對這個Applet開放了安全權限,不再受沙盒模型的限制了。

試著啟動游戲,玩一伙兒後,點擊"保存"按鈕,Applet將正確地將統計數據保存到D:\result.txt文件中。

使用插件下載JRE

如果客戶端游覽器還未安裝JRE或已安裝的JRE版本低於你Applet的要求,或浏覽器自帶的JRE不是Sun公司標准的Applet,你Applet都可能無法正常運行。可以通過JDK自帶的HtmlConverter.exe工具對帶Applet的HTML文件進行轉換,轉換後的文件可以指定浏覽器在運行Applet時將特定版本的JRE以插件的方式下載並安裝,就象帶Flash插件或SVG插件一樣。

我們先從game工程根目錄的classes目錄下,拷貝TypeTrainApplet.html到game工程根目錄下,以使其和game.jar位於同一個目錄。

HtmlConverter.exe工具位於JDK的bin目錄下,我們使用JDK5.0下的,導航到JDK5.0所安裝的bin目錄中,雙擊HtmlConverter.exe,稍等片刻,將彈出如下的對話框:

圖 23 HTML轉換工具

1.點擊"指定文件或目錄路徑"後的"浏覽…"按鈕,選擇工程目錄下的TypeTrainApplet.html。

2."將文件備份到文件夾"指定了將未轉換前的TypeTrainApplet.html文件備份到的目錄。

3.在"模板文件"中設置轉換模板,根據你客戶端用戶所在的平台和使用的浏覽器選擇相應的選項。這裡我們選擇"只適用Windows和Solaris的標准組件(IE和Navigator)"。

4.點選"使用任何Java1.5,或更高版本",這樣Java plug-in插件將使用JER1.5版本,這樣將使用JRE1.5系統最新的版本,如果選擇"JRE1.5.0"將保持插件版本不變,則不會去更新。

5.點擊"轉換(C)…"開始轉換,原始的TypeTrainApplet.html被備份到備份文件夾下,在原位置的TypeTrainApplet.html已經被轉換的結果覆蓋。

提示:

如果你想使用JRE1.4或JRE1.3作為插件,則需要使用JDK1.4或JDK1.3所帶的轉換器進行轉換。

打開轉換後的TypeTrainApplet.html文件內容如下所示:

代碼清單 11 轉換後的TypeTrainApplet.html

1. <html>
2. <head>
3. <meta http-equiv="Content-Type" content="text/html; charset=GBK">
4. <title>HTML Test Page</title>
5. </head>
6. <body>game.TypeTrainApplet will appear below in a Java enabled browser.
7. <br>
8. <!--"CONVERTED_APPLET"-->
9. <!-- HTML CONVERTER -->
10. <object
11. classid = "clsid:CAFEEFAC-0015-0000-0000-ABCDEFFEDCBA"
12. codebase =
13. "http://java.sun.com/update/1.5.0/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"
14. WIDTH = "400" HEIGHT = "400" NAME = "TestApplet" ALIGN = "middle" VSPACE =
15. "0" HSPACE = "0" >
16. <PARAM NAME = CODE VALUE = "game.TypeTrainApplet.class" >
17. <PARAM NAME = CODEBASE VALUE = "." >
18. <PARAM NAME = ARCHIVE VALUE = "game.JAR" >
19. <PARAM NAME = NAME VALUE = "TestApplet" >
20. <param name = "type" value = "application/x-java-applet;jpi-version=1.5">
21. <param name = "scriptable" value = "false">
22. <PARAM NAME = "stepLen" VALUE="2">
23. <PARAM NAME = "stepInterval" VALUE="50">
24. <PARAM NAME = "columnCount" VALUE="10">
25. <PARAM NAME = "generateInterval" VALUE="500">
26.
27. <comment>
28. <embed
29.  type = "application/x-java-applet;jpi-version=1.5" \
30.  CODE = "game.TypeTrainApplet.class" \
31.  JAVA_CODEBASE = "." \
32.  ARCHIVE = "game.JAR" \
33.  NAME = "TestApplet" \
34.  WIDTH = "400" \
35.  HEIGHT = "400" \
36.  ALIGN = "middle" \
37.  VSPACE = "0" \
38.  HSPACE = "0" \
39.  stepLen ="2" \
40.  stepInterval ="50" \
41.  columnCount ="10" \
42.  generateInterval ="500"
43.  scriptable = false
44. pluginspage = "http://java.sun.com/products/plugin/index.html#download">
45. <noembed>
46.
47. </noembed>
48. </embed>
49. </comment>
50. </object>
51.
52. <!--
53. <APPLET CODE = "game.TypeTrainApplet.class" JAVA_CODEBASE = "." ARCHIVE
54. = "game.JAR" WIDTH = "400" HEIGHT = "400" NAME = "TestApplet" ALIGN =
55. "middle" VSPACE = "0" HSPACE = "0">
56. <PARAM NAME = "stepLen" VALUE="2">
57. <PARAM NAME = "stepInterval" VALUE="50">
58. <PARAM NAME = "columnCount" VALUE="10">
59. <PARAM NAME = "generateInterval" VALUE="500">
60. </APPLET>
61. -->
62. <!--"END_CONVERTED_APPLET"-->
63. </body>
64. </html>

第13行指定了下載JRE插件的地址,如果沒有安裝這樣的版本,將自動下載當前 JRE 1.5 系列的缺省下載版本,如果不能自動安裝,則將用戶引導到下載頁面中,用戶可以手工下載JRE,下載頁面在第44行指定。

如果你的Applet最終部署在一個Web服務器中,且Web服務器位於局域網中,則你事先可以將JRE1.5.0下載下來,放置到Web服務器的上下文中,並更改第13行和第44行的路徑。

提示:

將jinstall-1_5_0-windows-i586.cab下載並放置到自己的Web服務器中,相應更改<object>的codebase屬性值,並不會成功自動安裝JRE1.5.0,因為jinstall-1_5_0-windows-i586.cab並未包含JRE1.5.0的安裝程序,而是通過cab文件中的jinstall-1_5_0.inf文件聲明根據http://java.sun.com/update/1.5.0/1.5.0-b64.xml的配置信息,從Sun網站下載後安裝。如果你Web服務器所在的局域網不能直接訪問Sun網站,安裝過程將無法安成。你需要更改cab文件中的jinstall-1_5_0.inf文件,下載並更改1.5.0-b64.xml配置文件才可以使局域網的客戶端到你自己的Web服務器指定地址下載。

如果Applet是在jsp而非html文件中調用,則可以使用<jsp:plugin>標簽來引用applet,以使applet以插件方式引用JRE。關於<jsp:plugin>的使用方法,請查看jsp相關書籍。

總結

我們講述了如何在JBuilder開發一個簡單的Applet指法練習游戲程序的過程,雖然這個游戲在功能上屬於不敢見公婆型,但它涵蓋了Applet開發的大部分內容和技巧。我們特在Applet中設置了一個不安全的功能:在客戶機器中保存文件,浏覽器事先毫不留情地阻截了它,爾後我們通過數字簽名技術曉之以情,動之以理"說服"了浏覽器取消安全限制。

大千世界,紛繁復雜,客戶端浏覽器的JRE版本和廠家百家爭鳴,百花齊放,為了使我們的Applet能夠在Sun標准的JRE1.5.0的版本上運行,我們動用了JDK自帶的轉換器對原html進行轉換,這樣標准的JER1.5.0將作為插件的形式下載並安裝以支持這個難伺候Applet。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved