posts - 23,comments - 66,trackbacks - 0
          摘要

          這篇文章介紹了Mustang(野馬)——Sun Microsystems將在今年晚些時候正式發布的最新Java平臺。Jeff Friesen探討了Mustang有關控制臺輸入/輸出和分區空間的新方法、用于啟動畫面以及與系統托盤交互的新API。

          sun 將要在今年的晚些時候發布最新的Java平臺(開發代號Mustang)。作為正式的Java平臺,Standard Edition 6這個版本關注了幾個重要的主題,例如兼容性和穩定性。有關完整的主題列表,參閱Java Specification Request 270,JSE 6的版本目錄。

          Mustang預期擁有的新特性包括(除了別的以外):
          ·????????一個編譯器API
          ·????????控制臺輸入/輸出(I/O)
          ·????????一個啟動畫面API
          ·????????眾多的Java 2D性能改進
          ·????????XML數字簽名
          ·????????一個系統托盤API
          ·????????java.io.File類的分區空間方法
          ·????????Java數據庫連接(JDBC)4.0
          ·????????公共注釋(Common annotations)
          ·????????腳本支持(Scripting)
          ·????????一個用于XML的流(streaming)API
          ·????????排序、過濾和加亮javax.swing.JTable內容的能力
          ·????????Javadoc標記的更新
          ·????????可編程操作網絡屬性(例如廣播地址和子網掩碼)
          ·????????方便地打印javax.swing.text.JTextComponent的內容的能力

          Mustang擁有遠遠超出一篇文章探討范圍的新特性,因此,本文只關注新特性的一小部份。確切地說,本文將討論用于控制臺輸入/輸出和分區空間的方法、用于啟動畫面以及與系統托盤交互的API。

          版權聲明:任何獲得Matrix授權的網站,轉載時請務必保留以下作者信息和鏈接
          作者:Jeff Friesen;jlearner(作者的blog:http://blog.matrix.org.cn/page/jlearner)
          原文:http://www.javaworld.com/javaworld/jw-01-2006/jw-0109-mustang.htmltml
          譯文:http://www.matrix.org.cn/resource/article/44/44282_Mustang+Java+SE.html
          關鍵字:Mustang;Java SE

          警告
          由于Mustang目前沒有最終發布,一些特性還可能會被改變或者去掉。所以,當Sun公司最終發布Mustang的時候,本文中的一些代碼可能會需要改動或者變得完全不相干了。

          注意
          我使用Sun公司的Java 2 SDK版本1.6.0-rc (build 62)創建和測試了本文的代碼。底層平臺是Microsoft Windows ME。

          控制臺輸入/輸出

          在1997 年5月,Sun收到了一個改進控制臺輸入/輸出的增強請求(RFE)。申請人特別要求一種可以提示用戶輸入密碼并且允許用戶輸入密碼(任意長度)而不會在控制臺顯示出密碼字符的方法。申請人指出,抽象窗口工具包(AWT)的setEchoChar()方法并不合用,因為它依賴于GUI的可用性,然而很多基于服務器的操作系統根本不使用GUI。

          在2005年后期,Sun回應了RFE #4050435,為Mustang (build 57)添加了java.io.Console類。這個類所提供的方法可以訪問與當前虛擬機相關聯的基于字符的控制臺設備。但是在調用這些方法之前,需要首先調用System的public static Console console()方法來獲取一個Console對象。該方法將返回一個用來與控制臺設備交互的Console對象,但是如果控制臺設備不存在就會返回 null,例如當你重定向標準輸入或標準輸出(或二者皆有)的時候。在調用System.console()來返回Console對象之后,下面的一段代碼將檢查返回的Console實例是否為null來測定控制臺設備是否存在:

          Console console = System.console ();
          if (console == null)
          {
          ????System.err.println ("Console not available");
          ????return;
          }


          假設控制臺設備是存在的,你可以從控制臺輸入流讀取密碼和整行的字符,還可以向控制臺輸出流寫入字符。為了讀取密碼(而不會將密碼字符顯示到控制臺輸出流),你必須調用Console的兩個readPassword()方法之一。這兩個方法不允許換行符作為密碼的一部分,如果達到了控制臺輸入流的字符數目限制,他們將返回null。

          舉例來說,你可以調用public char [] readPassword(String fmt, Object... args)來提示用戶輸入密碼,其中提示是由java.util.Formatter-類型格式化字符串(fmt)和它的變量表(args)來描述的,然后從一個字符數組中返回用戶選擇的密碼。下面的代碼段反復調用readPassword(String fmt, Object... args)來提示用戶輸入密碼,直到用戶輸入了一個字符長度不小于MIN_PWD_LEN的密碼為止。
          char [] pwd;
          do
          {

          ?? pwd = console.readPassword ("Enter password (%d characters min):",
          ????????????????MIN_PWD_LEN);
          }
          while (pwd.length < MIN_PWD_LEN);


          在密碼被存儲到pwd之后,就可以按需使用了。然而,出于安全性考慮,在不需要使用密碼的時候,應該將pwd清零。
          除了readPassword()方法之外,Console提供了兩個readLine()方法以便于從控制臺讀取一整行的字符,并且將這些字符(不包括換行符)存儲在一個String中。如果達到了控制臺輸入流的字符數目限制,這兩個方法都將返回null。
          舉例來說,你可以在不提示用戶的情況下,調用public String readLine()來返回一行字符。下列代碼段演示了這個方法:

          關于兩個readPassword()方法和兩個readLine()方法有一些有趣的事。當這些方法遇到輸入/輸出錯誤時,它們不拋出 java.io.IOException對象(例如會被System.in.read()拋出),而是拋出一個java.io.IOError對象。由于 IOError是Error的子類,你無需像捕捉IOException那樣來捕捉這個對象。

          向控制臺輸出流輸出字符的配套方法是 public Console format(String fmt, Object... args)和作用相同的public Console printf(String fmt, Object... args)方法,后者內部調用了format()。下列代碼段演示了printf():
          console.printf ("%s", input);


          輸出字符之后,format()——和printf()(按擴展名)——自動轉儲清除控制臺輸出流。

          注意
          控制臺對象與唯一的一個java.io.Reader對象和唯一的一個java.io.PrintWriter對象相關聯。調用控制臺的public Reader reader()方法可以返回Reader。例如你可以把Reader傳遞給java.util.Scanner的一個構造函數來對控制臺輸入流進行復雜的解析。調用控制臺的public PrintWriter writer()方法可以獲取PrintWriter。然后你可以調用各種各樣有用的方法來輸出不同類型的數據到控制臺上。為方便起見,控制臺提供了一個 public void flush()方法來調用PrinterWriter的flush()方法。

          讓我們用關于控制臺輸入/輸出的知識來做一個實際應用——數據庫訪問。最終,我建立了一個Microsoft Access sales.mdb銷售數據庫,它隨著本文的代碼一同發布(可以在資源下載)。該數據庫包含一個單獨的territories表,它有兩列:Name代表售貨員的名字,Territory代表售貨員可以合法地出售產品的區域。我將下面的數據輸入到了數據庫中并且為它設置了密碼保護——密碼是 mustang。

          | ------------------------ |
          | Name????????| Territory??|
          | ------------------------ |
          | John Doe????| North??????|
          | Jane Doe????| South??????|
          | Paul Smith??| East?????? |
          | Kathy Smith | West?????? |
          | ------------------------ |

          銷售數據庫由一個Sales應用軟件來訪問。在訪問數據庫之前,程序請求用戶輸入一個用戶名和密碼。然后它使用這些數據通過JDBC-ODBC bridge驅動來連接sales數據源(你可以用Windows控制面板的ODBC數據源小程序來創建它,用于識別sales.mdb的位置)。如果連接成功,程序將輸出territories表的全部行和列的值。列表1顯示了Sales程序的源代碼。

          Listing 1. Sales.java
          // Sales.java

          import java.io.*;
          import java.sql.*;
          import java.util.*;

          class Sales
          {
          ?? public static void main (String [] args)
          ???????????????? throws ClassNotFoundException, SQLException
          ?? {
          ??????// Attempt to obtain a console.
          ??????// 嘗試獲取控制臺

          ??????Console console = System.console ();
          ??????if (console == null)

          ??????{
          ??????????System.err.println ("sales: unable to obtain console");
          ??????????return;
          ??????}

          ??????// Obtain username.
          ??????// 獲取用戶名

          ??????String username = console.readLine ("Enter username: ");

          ??????// Obtain password.
          ??????// 獲取密碼

          ??????String password = new String (console.readPassword ("Enter password: "));

          ??????// Create a Vector datastructure for holding table records.
          ??????// 建立一個向量數據結構來存儲表記錄

          ??????Vector<Object []> v = new Vector<Object []> ();

          ??????Connection con = null;
          ??????Statement stmt = null;
          ??????ResultSet rs;

          ??????try
          ??????{
          ???????? // Attempt to connect to the sales datasource.
          ???????? // 嘗試連接sales數據源

          ???????? con = DriverManager.getConnection ("jdbc:odbc:sales", username,
          ????????????????????????????????????????????password);

          ???????? // Garbage collect the password—not a good idea to keep passwords
          ???????? // hanging around.
          ???????? // 對密碼進行垃圾收集——始終留著密碼可不是個好主意

          ???????? password = null;

          ???????? // Attempt to create a statement.
          ???????? // 嘗試建立一個statement

          ???????? stmt = con.createStatement ();

          ???????? // Establish the maximum number of rows that can be returned.
          ???????? // 設置允許返回的最大行數

          ???????? stmt.setMaxRows (10);

          ???????? // Attempt to fetch all rows from the territories table.
          ???????? // 嘗試讀取territories表的所有行

          ???????? String query = "SELECT * FROM territories";
          ???????? rs = stmt.executeQuery (query);

          ???????? // Get number of columns.
          ???????? // 獲取列數

          ???????? int nCols = rs.getMetaData ().getColumnCount ();

          ???????? // Read all rows of all columns into storage.
          ???????? // 讀取所有列的每一行到向量存儲

          ???????? int i = 0;

          ???????? while (rs.next ())
          ???????? {
          ????????????Object [] buffer = new Object [nCols];

          ????????????for (int j = 0; j < nCols; j++)
          ???????????????? buffer [j] = rs.getObject (j + 1);

          ????????????// NOTE: getObject requires a 1-based column index.
          ???????????? // 注意:getObject要求列索引號從1開始

          ????????????v.add (buffer);
          ???????? }

          ???????? // Extract rows from the array.
          ???????? // 從數組中取出全部行

          ???????? Object [] rows = v.toArray ();
          ???????? for (i = 0; i < rows.length; i++)
          ???????? {
          ??????????????// Extract columns from the row.
          ?????????????? // 取出一行中的每列值

          ??????????????Object [] cols = (Object []) rows [i];

          ??????????????// Print out the values from each column.
          ?????????????? // 顯示輸出每一列的值

          ??????????????for (int j = 0; j < cols.length; j++)
          ?????????????????? console.printf ("%s ", cols [j]);

          ??????????????console.printf ("\n");
          ???????? }

          ???????? console.printf ("Value at Row 1, Col 0 = %s\n", getValue (v, 1, 0));
          ??????}
          ??????catch (SQLException e)
          ??????{
          ???????? console.printf ("sales: %s\n", e.getMessage ());
          ??????}
          ??????finally
          ??????{
          ???????? if (stmt != null)
          ???????????? try
          ???????????? {
          ???????????????? stmt.close ();
          ???????????? }
          ???????????? catch (SQLException e2) {}

          ???????? if (con != null)
          ???????????? try
          ???????????? {
          ???????????????? con.close ();
          ???????????? }
          ???????????? catch (SQLException e2) {}
          ??????}
          ?? }

          ?? static Object getValue (Vector v, int row, int col)
          ?? {
          ??????// The following conversion should really not be done in this method,
          ??????// because it's inefficient. Placing the conversion here is a matter of
          ??????// convenience.
          ??????// 由于效率低下,下列的轉換其實本不該在這個方法中進行,把轉換放在這里只是為了方便。

          ??????Object [] rows = v.toArray ();

          ??????Object [] cols = (Object []) rows [row];

          ??????return cols [col];
          ?? }
          }


          編譯Sales.java然后執行程序,首先呈現在你面的是Enter username:的提示。你可以隨意輸入任何用戶名,但至少要輸入點什么。接下來你將被提示輸入密碼,確定這里要使用mustang。假如連接成功建立了,你將會看到我在上面已經展示的表值。

          列表1使用了Console的printf()方法來輸出表的內容。然而,這種方法有一個不十分明顯的問題,如果territories表有很多行的數據,在不滾動的情況下,并不是所有的數據都適合控制臺窗口的顯示。為了獲取這些行,通常需要重定向標準輸出到一個文件,但是如果你這樣做的話, System.console()將返回null,你也就無法使用控制臺輸入/輸出了。因此,在你的應用中使用Console的printf()和 format()方法之前要考慮清楚,不要使用它們向屏幕輸出大量的數據,否則你可能就會無法看到全部數據了。

          注意
          仔細地分析列表1的代碼,它并沒有通過Class.forName()來嘗試加載JDBC-ODBC bridge驅動。由于Mustang支持JDBC 4.0——它提供了一種內在機制來使DriverManager定位和加載驅動類——你不再需要通過Class.forName()來顯式地加載驅動類了。

          盡管Console解決了最初的RFE問題,但是很多開發者都對這個類存在異議,主要的批評包括:
          ·????????System.console ()方法應該被命名為System.getConsole()。然而一些不贊成的人爭辯說“get”前綴只應用在bean上面,而System并不是 bean。此外,console()是一個靜態方法,這意味著IDE將不會認為這個方法是用來獲取property的getter方法了。
          ·????????Console 類應該提供不使用緩沖來讀取單個字符的能力。換句話說,程序不應該等到用戶按下回車之后才能讀取用戶的輸入。一個類似C語言的kbhit()函數、通過返回一個Boolean值來判斷是否有按鍵被按下的方法將可以回應這種批評,如果返回true,程序就可以調用另一個方法來返回下一個等待在BIOS按鍵緩沖中的按鍵了。
          ·????????Console應該提供打開/關閉控制臺字符顯示的能力。兩個類似C語言中getch()函數(獲得字符而不在控制臺顯示,并且只當沒有字符等待在BIOS按鍵緩沖中時才有效)和getche()函數(獲得字符同時在控制臺顯示,并且只當沒有字符等待在BIOS按鍵緩沖中時才有效)的方法將可以回應這種批評。
          ·????????Console應該提供清除屏幕的能力和其他可以在基于Unix的curses庫中找到的特性。
          ·????????Console應該提供一個detach()方法來將應用程序從控制臺分離出來并且送入后臺。
          除了第一條之外,對Sun來說,在Mustang的正式版本中回應上述批評的一部份(如果不是全部的話),也許還不是太晚。


          分區空間方法
          在收到改進控制臺輸入/輸出的RFE #4050435的一個月之前,Sun收到了另外一個RFE,要求提供一種方法來得到可用的磁盤空間。Sun回應了這個RFE #4057701,為File類加入了3個分區空間方法:
          ·????????public long getFreeSpace():返回以抽象路徑名命名的分區的未分配空間字節數。 如果抽象路徑名不是磁盤分區的名字,返回值為空。
          ·????????public long getTotalSpace():返回以抽象路徑名命名的分區的總磁盤空間。 如果抽象路徑名不是磁盤分區的名字,返回值為空public long getUsableSpace():返回以抽象路徑名命名的虛擬機可用的空間字節數。 如果抽象路徑名不是磁盤分區的名字,返回值為空.

          對于Windows版本的Mustang,這些方法是按照Microsoft的GetDiskFreeSpaceEx函數實現的。讓我們基于這個函數的文檔重新定義一下這些方法:
          ·????????getFreeSpace() 將返回當前根目錄的磁盤的全部空閑字節數,若當前根目錄是一個CD-ROM驅動器則返回0. 如果有不可寫CD在一個CD-RW驅動器中會返回非0值。.
          ·????????getTotalSpace ()將返回與調用線程相關的用戶可用的磁盤字節總數。如果用戶配額被開啟,這個值可能會小于磁盤的空閑字節總數。getUsableSpace()將返回與調用線程相關的用戶可用的磁盤全部空閑字節數。如果用戶配額被開啟,這個值可能會小于磁盤的空閑字節總數。如果當前根目錄是一個CD-ROM驅動器則返回0,如果有不可寫CD在一個CD-RW驅動器中會返回非0值。

          注意
          很多的現代操作系統允許限制每個用戶可用的最大磁盤空間。這個最大空間叫做用戶的配額,為用戶留出的磁盤空間區域叫做該用戶的分區。在這種情況下,getTotalSpace()返回的是與調用線程相關的用戶的配額。如果用戶沒有配額限制(只有一個用戶),該方法返回磁盤字節總數。同樣,getUsableSpace()返回的是與調用線程相關的用戶的配額限制內的可用字節數。如果用戶沒有配額限制,該方法則返回磁盤的全部空閑字節數。最后,基于GetDiskFreeSpaceEx()的文檔, getFreeSpace()磁盤的空閑字節數——而不考慮用戶的配額。

          列表2展示了一個SpaceChecker程序的源代碼,它將顯示文件系統的每一個根目錄的空閑空間,以及分配給當前用戶的可用空間和全部空間。

          Listing 2. SpaceChecker.java

          列表 2. SpaceChecker.java
          // SpaceChecker.java

          import java.io.File;

          public class SpaceChecker
          {
          ?? public static void main (String [] args)
          ?? {
          ??????File [] roots = File.listRoots ();

          ??????for (int i = 0; i < roots.length; i++)
          ??????{
          ?????????? System.out.println (roots [i]);
          ?????????? System.out.println ("Free space = " + roots [i].getFreeSpace ());
          ?????????? System.out.println ("Usable space = " + roots [i].getUsableSpace ());
          ?????????? System.out.println ("Total space = " + roots [i].getTotalSpace ());
          ?????????? System.out.println ();
          ??????}
          ?? }
          }


          在我的Windows ME平臺上運行該程序時,我觀察到了下列信息:
          A:\
          Free space = 0
          Usable space = 0
          Total space = 0

          C:\
          Free space = 27913584640
          Usable space = 27913584640
          Total space = 40965373952

          M:\
          Free space = 0
          Usable space = 0
          Total space = 0

          N:\
          Free space = 0
          Usable space = 0
          Total space = 0


          由于磁盤配額沒有被應用到Windows ME操作系統上,所以C:的空閑空間與可用空間是相同的。

          啟動畫面 API

          啟動畫面是基于GUI的現代應用軟件的一個重要部分。啟動畫面不僅可以在漫長的軟件啟動過程中占據用戶的注意力(也可能用來通報用戶版本信息或者其他細節),還可以使用戶確定軟件正在啟動中。這在Java環境中是尤其重要的,因為JVM需要一段時間來加載和啟動。圖1列舉了一個啟動畫面的例子。


          圖1. 使用啟動畫面來顯示版權和其他重要信息

          Mustang對啟動畫面的實現是使用一個能夠顯示GIF(包括動畫GIF)、PNG或者JPEG圖像的無修飾窗口。Java應用程序加載器創建一個啟動畫面窗口,然后響應命令行參數或者JAR manifest入口,從而在窗口中顯示指定的圖片:
          ·???????? -splash命令行參數創建一個啟動畫面窗口并且顯示一個指定的圖片。例如,java -splash:mylogo.gif MyApp將創建啟動畫面窗口并且在這個窗口中顯示mylogo.gif 確定的圖像(見圖1),然后加載和啟動JVM,最后使JVM加載MyApp.class并且執行該類的public static void main(String [] args)方法。
          ·????????由于你最有可能把重要的應用打包進一個jar文件, Mustang提供了一個SplashScreen-Image manifest項目,用來從jar文件的manifest中訪問啟動畫面的圖像。例如SplashScreen-Image:mylogo.gif把 mylogo.gif識別為在啟動畫面窗口中顯示的圖像。

          假設下列信息被放置在了一個manifest文件中:
          Manifest-Version: 1.0
          Main-Class: MyApp
          SplashScreen-Image:mylogo.gif


          將這個manifest文件、mylogo.gif和全部相關的類文件都打包進myApp.jar,java -jar myApp.jar會在加載和啟動JVM及運行應用程序之前創建啟動畫面并且顯示mylogo.gif。

          如果你調用java -splash:yourlogo.gif -jar myApp.jar將會發生什么呢?在這種情況下,yourlogo.gif將會在啟動畫面窗口中出現,因為命令行參數-splash優先替代了 manifest設置SplashScreen-Image。

          假如你希望在圖像上加入視覺效果來定制啟動畫面,例如為一個有著漫長初始化過程的軟件的啟動畫面圖像加入一個動態的進度條來告知用戶剩余的時間(參考“Mustang新的啟動畫面功能”一文中的SplashTest程序)。 Mustang的java.awt.SplashScreen類提供了對啟動畫面定制的支持。

          在沒有啟動畫面窗口的情況下,一個 SplashScreen對象不具有任何意義,所以你不能從SplashScreen類實例化對象,而啟動畫面窗口只當你指定了-splash命令行選項或者SplashScreen-Image manifest項目時才會存在。當你指定了此命令行選項或者manifest項目并且窗口被創建出來,作為其啟動過程的一部分,Mustang會創建一個SplashScreen對象。調用SplashScreen的public static SplashScreen getSplashScreen()方法可以返回這個對象的一個實例。切記如果沒有啟動畫面窗口的話,將會返回null。在調用了 SplashScreen.getSplashScreen()來返回SplashScreen對象之后,下力代碼段首先通過檢查返回的 SplashScreen實例是否為null來測定啟動畫面窗口是否存在:

          SplashScreen ss = SplashScreen.getSplashScreen ();
          if (ss != null)
          {
          ????// Customize the splash screen.
          }


          假設啟動畫面窗口是存在的,你可以調用下列五種SplashScreen方法來獲取一個圖形環境,以便于在緩沖區繪制圖象、得到啟動畫面圖象、獲取圖象的尺寸、用其他圖象替換現有圖象或者是使用緩沖區中的內容來更新啟動畫面窗口。

          ·????????public Graphics getGraphics():以java.awt.image.BufferedImage的形式創建一個覆蓋圖象,它具有與啟動畫面圖象相同的尺寸,還有一個窗口,類型設置為BufferedImage.TYPE_INT_ARGB(給予圖象一個alpha通道來設置透明度),并且返回一個實例到 BufferedImage的圖形環境。這幅圖象的所有像素都被設為黑色并且是完全透明的。你可以調用圖形環境的方法在BufferedImage中繪制。受到繪圖操作影響的每個像素都會被分配一個不透明的alpha值。如果在啟動畫面窗口關閉之后調用這個方法,它將會拋出 IllegalStateException。
          ·????????public URL getImageURL():以URL返回當前的啟動畫面圖象。如果在啟動畫面窗口關閉之后調用這個方法,它將會拋出IllegalStateException。
          ·????????public Dimension getSize():返回啟動畫面窗口的尺寸,同時也就是顯示的圖象的尺寸。如果在啟動畫面窗口關閉之后調用這個方法,它將會拋出IllegalStateException。
          ·????????public void setImageURL(URL imageURL):將啟動畫面圖象改為imageURL確定的圖象。當圖象被加載、窗口被更新之后,該方法將返回。啟動畫面窗口將調整為圖象的同樣大小同時在屏幕上居中。如果imageURL為null,該方法將拋出NullPointerException;如果在啟動畫面窗口關閉之后調用這個方法,它將會拋出 IllegalStateException。
          ·????????public void update():更新啟動畫面窗口使得覆蓋圖內容與當前的啟動畫面窗口的像素相合成。由于采用了Source-over合成,覆蓋圖象的透明像素可以使啟動畫面圖象的像素顯示出來,而覆蓋圖象的不透明像素則遮擋了它下面啟動畫面圖象的像素。如果沒有調用getGraphics()來創建覆蓋圖象,或者在啟動畫面圖象關閉之后它才被調用,它將拋出IllegalStateException。

          列表3通過一個PhotoAlbum軟件的輪廓演示了getSplashScreen(),getGraphics(),getSize(),與update()這四個方法。這段程序定制了啟動畫面,在四周加上了一個紅色邊框,并且在水平中心顯示一條信息“Registering plug-ins...”。
          Listing 3. PhotoAlbum.java
          列表 3. PhotoAlbum.java
          // PhotoAlbum.java

          import java.awt.*;

          public class PhotoAlbum
          {
          ?? public static void main (String [] args)
          ?? {
          ??????SplashScreen ss = SplashScreen.getSplashScreen ();
          ??????if (ss != null)
          ??????{
          ??????????Graphics g = ss.getGraphics ();
          ??????????g.setColor (Color.red); // Color.white在我這個版本的Mustang中是默認顏色

          ??????????Dimension size = ss.getSize ();

          ??????????// 每個邊框寬度都是圖象的寬和高中較小值的10%

          ??????????int borderSize;
          ??????????if (size.width < size.height)
          ??????????????borderSize = (int) (size.width * 0.01);
          ??????????else
          ??????????????borderSize = (int) (size.height * 0.01);

          ??????????for (int i = 0; i < borderSize; i++)
          ?????????????? g.drawRect (i, i, size.width-1-i*2, size.height-1-i*2);

          ??????????// 計算字符串在當前字體時的寬和高

          ??????????FontMetrics fm = g.getFontMetrics ();
          ??????????int strWidth = fm.stringWidth ("Registering plug-ins...");
          ??????????int strHeight = fm.getHeight ();

          ??????????// 在字符串沒有超出啟動畫面窗口范圍時

          ??????????if (strWidth < size.width && 4*strHeight < size.height)
          ??????????{
          ??????????????g.setColor (Color.blue);
          ??????????????g.drawString ("Registering plug-ins...",
          ????????????????????????????(size.width-strWidth)/2,
          ????????????????????????????size.height-4*strHeight);
          ??????????}

          ??????????// 拷貝覆蓋圖象到啟動畫面窗口

          ??????????ss.update ();

          ??????????try
          ??????????{
          ??????????????Thread.sleep (3000); // 暫停3秒鐘以便查看圖象
          ??????????}
          ??????????catch (InterruptedException e)
          ??????????{
          ??????????}
          ??????}
          ?? }
          }


          為演示PhotoAlbum程序,隨本文的代碼一起,我加入了一張圖片palogo.jpg。當你執行java -splash:palogo.jpg PhotoAlbum之后,將首先在屏幕的中間看見palogo.jpg的圖象。在JVM完成載入并且開始運行main(),你會看見如圖2所示的合成圖象(也是居中的)。

          image
          圖2. 標識圖片改變了自身的邊框顏色,同時提示用戶正在注冊插件。點擊縮略圖以觀看全尺寸圖象。

          當第一個AWT或者Swing窗口變為可見的時候,啟動畫面窗口會自動關閉,然而,也許你想要在軟件窗口出現之前就將其關閉,或者用你自己的窗口來替換它。SplashScreen類提供了以下3個方法來幫助你達到這個目的:
          ·????????public void close():隱藏并關閉啟動畫面窗口,釋放分配給該窗口的全部資源。如果在啟動畫面窗口關閉之后調用這個方法,它將會拋出IllegalStateException。
          ·????????public Rectangle getBounds():返回啟動畫面窗口的邊界。如果你調用setImageURL()創建了一個不同尺寸的新啟動畫面圖象,這些邊界將會改變。如果在啟動畫面窗口關閉之后調用這個方法,它將會拋出IllegalStateException。
          ·????????public boolean isVisible():如果啟動畫面窗口是可見的,將返回一個Boolea值true,在窗口關閉之后調用則返回false。

          假如你用自己的窗口進行了替換,你可以調用getBounds()來賦予它跟啟動畫面窗口同樣的初始坐標和尺寸。此外,當你自己的窗口變為可見的時候,啟動畫面窗口將自動關閉。


          系統托盤API

          系統托盤是桌面上一個專門的區域,它顯示當前的時間和常駐內存的桌面應用的圖標,并且被桌面上當前運行的所有應用共享。表3展示了Windows ME的系統托盤,它位于Windows任務欄的右側。

          image
          表3. 系統托盤上的顯示屬性應用程序圖標相關聯的菜單和工具提示

          用戶只需要適當地輕點鼠標,就隨時都可以與這些應用進行交互。例如,當鼠標位于應用軟件的圖標上時,雙擊鼠標左鍵通常就可以打開應用的主窗口(在Windows平臺上)。同樣地,把鼠標移到應用的圖標上面并且單擊右鍵,通常就可以顯示特定應用的彈出菜單。

          Mustang引入了類java.awt.SystemTray和java.awt.TrayIcon來與系統托盤進行交互:SystemTray代表桌面的系統托盤,TrayIcon代表可以加入到系統托盤的一個圖標。

          同SplashScreen 一樣,你不能創建SystemTray對象,而是必須調用SystemTray的public static SystemTray getSystemTray()方法來返回一個代表系統托盤區域的SystemTray實例。由于在底層平臺不支持系統托盤時,該方法會拋出 UnsupportedOperationException,所以你必須首先調用public static boolean isSupported()。如果系統托盤能得到最低限度的支持(除了顯示圖標之外,最低限度的支持包括右鍵點擊圖標時顯示的彈出式菜單,或者是左鍵雙擊圖標時響應的彈出事件),這個方法就將返回true,否則返回false。下列代碼段演示了獲取SystemTray實例的正確方式:
          if (SystemTray.isSupported ())
          {

          ????SystemTray tray = SystemTray.getSystemTray ();

          ????// Do stuff with tray.
          }


          假設系統托盤可以被支持,你可以調用下列7個方法來實現各種各樣的功能:
          ·????????public void add(TrayIcon trayIcon): 為SystemTray加入一個TrayIcon,在這之后,trayIcon所描述的圖標將在系統托盤顯示出來。圖標加入到系統托盤的順序取決于平臺的實現。同樣,當應用程序退出或者系統托盤變為不可用的時候,圖標將被自動移除。如果trayIcon為null,該方法將拋出 NullPointerException;如果你試圖多次添加同樣的TrayIcon實例,將拋出IllegalArgumentException;如果系統托盤不可用,將拋出AWTException。
          ·????????public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener): 為trayIcons屬性加入一個java.beans.PropertyChangeListener到listener表,trayIcons屬性必須為propertyName的值。當程序從系統托盤添加或者刪除一個TrayIcon或者圖標被自動移除時,listener將被調用。該方法不拋出任何異常,即使propertyName或者listener為null。
          ·????????public PropertyChangeListener [] getPropertyChangeListeners(String propertyName): 返回一個與指定屬性相關聯的PropertyChangeListener(對于當前程序)的數組。目前只支持trayIcons,并且必須以 propertyName值來指定。如果你傳遞了null或者任何其它值,該方法將返回一個空數組。
          ·????????public TrayIcon [] getTrayIcons(): 返回含有被應用程序加入到SystemTray 的全部TrayIcon的一個數組。所返回的數組是真實數組的一個拷貝,可以隨意修改而不會反映到系統托盤的圖標上。如果沒有TrayIcon被加入,該方法將返回一個空數組。
          ·????????public Dimension getTrayIconSize(): 以java.awt.Dimension對象的形式,返回圖標出現在系統托盤時將占用的水平和垂直尺寸,其單位是像素。在創建圖標之前調用該方法來決定圖標的首選尺寸。這是TrayIcon的public Dimension getSize()方法的一種方便的實現。
          ·????????public void remove(TrayIcon trayIcon): 從SystemTray移除指定的TrayIcon。圖標將從系統托盤移除,同時將通報所有的屬性改變listener。該方法不拋出任何異常,即使 trayIcon為null。
          ·????????public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener): 移除propertyName指定屬性的listener,propertyName必須是trayIcon(除此之外就沒有任何地方需要調用這個方法了)。該方法不拋出任何異常,即使propertyName或者listener為null。

          表4是一個用來演示上述方法的SystemTrayDemo1程序。這個程序先創建了一個內部為實心紅色矩形的實心黃色圓形圖標,將這個圖標添加到系統托盤,暫停3秒鐘,之后從系統托盤將其移除,再暫停3秒鐘,然后結束。
          表4. SystemTrayDemo1.java
          // SystemTrayDemo1.java

          import java.awt.*;
          import java.awt.image.*;
          import java.beans.*;

          public class SystemTrayDemo1
          {
          ?? public static void main (String [] args)
          ?? {
          ??????if (SystemTray.isSupported ())
          ??????{
          ??????????// 獲取系統托盤

          ??????????SystemTray tray = SystemTray.getSystemTray ();

          ??????????// 注冊一個屬性變化listener來通知系統托盤上圖標的添加和移除

          ??????????PropertyChangeListener pcl;
          ??????????pcl = new PropertyChangeListener ()
          ????????????????{
          ????????????????????public void propertyChange (PropertyChangeEvent pce)
          ????????????????????{
          ?????????????????????? System.out.println ("Property changed = " +
          ?????????????????????????????????????????? pce.getPropertyName ());
          ?????????????????????? System.out.println ();

          ?????????????????????? TrayIcon [] tia = (TrayIcon []) pce.getOldValue ();
          ?????????????????????? if (tia != null)
          ?????????????????????? {
          ?????????????????????????? System.out.println ("Old tray icon array contents: ");
          ?????????????????????????? for (int i = 0; i < tia.length; i++)
          ????????????????????????????????System.out.println (tia [i]);
          ?????????????????????????? System.out.println ();
          ?????????????????????? }


          ?????????????????????? tia = (TrayIcon []) pce.getNewValue ();
          ?????????????????????? if (tia != null)
          ?????????????????????? {
          ?????????????????????????? System.out.println ("New tray icon array contents: ");
          ?????????????????????????? for (int i = 0; i < tia.length; i++)
          ????????????????????????????????System.out.println (tia [i]);
          ?????????????????????????? System.out.println ();
          ?????????????????????? }
          ????????????????????}
          ????????????????};
          ??????????tray.addPropertyChangeListener ("trayIcons", pcl);

          ??????????// 為圖標創建一個圖象

          ??????????Dimension size = tray.getTrayIconSize ();
          ??????????BufferedImage bi = new BufferedImage (size.width, size.height,
          ????????????????????????????????????????????????BufferedImage.TYPE_INT_RGB);
          ??????????Graphics g = bi.getGraphics ();

          ??????????g.setColor (Color.red);
          ??????????g.fillRect (0, 0, size.width, size.height);
          ??????????g.setColor (Color.yellow);
          ??????????int ovalSize = (size.width < size.height) ? size.width : size.height;
          ??????????ovalSize /= 2;
          ??????????g.fillOval (size.width/4, size.height/4, ovalSize, ovalSize);

          ??????????// 由該圖像創建一個圖標并且將其添加到系統托盤。如果不能添加則結束程序。

          ??????????TrayIcon icon = null;
          ??????????try
          ??????????{
          ??????????????tray.add (icon = new TrayIcon (bi));
          ??????????}
          ??????????catch (AWTException e)
          ??????????{
          ??????????????System.out.println (e.getMessage ());
          ??????????????return;
          ??????????}

          ??????????// 暫停以便查看系統托盤

          ??????????try
          ??????????{
          ??????????????Thread.sleep (3000);
          ??????????}
          ??????????catch (InterruptedException e)
          ??????????{
          ??????????}

          ??????????// 從系統托盤移除圖標

          ??????????tray.remove (icon);

          ??????????// 暫停以查看沒有了圖標的系統托盤

          ??????????try
          ??????????{
          ??????????????Thread.sleep (3000);
          ??????????}
          ??????????catch (InterruptedException e)
          ??????????{
          ??????????}

          ??????????// 結束程序

          ??????????System.exit (0);
          ??????}
          ?? }
          }


          在獲取了系統托盤實例之后,為這個實例注冊一個屬性改變listener,并且為圖標創建一個java.awt.Image,表4基于這個圖標創建了一個 TrayIcon對象,然后將該對象加入到系統托盤。這個對象是通過調用TrayIcon's public TrayIcon(Image image)構造器創建的。這個構造器在TrayIcon對象中存儲了image。然后,當tray.add (icon = new TrayIcon (bi));將TrayIcon加入到SystemTray的時候,這個image被取出并且顯示在系統托盤上。如果image為null,該構造器和 TrayIcon的另外兩個構造器都拋出IllegalArgumentException。如果當前平臺不支持系統托盤,這些構造器將拋出 UnsupportedOperationException。

          如果你在命令行運行SystemTrayDemo1,你將會在系統托盤發現發現一個新圖標,在控制臺窗口將會獲得類似如下的輸出:
          Property changed = trayIcons

          Old tray icon array contents:

          New tray icon array contents:
          java.awt.TrayIcon@199f91c

          Property changed = trayIcons

          Old tray icon array contents:
          java.awt.TrayIcon@199f91c


          幾分鐘之后,圖標就會消失,SystemTrayDemo1程序結束,這對于持續運行的應用來說是不合適的。與此相反,你也許希望當用戶右鍵點擊程序圖標以彈出菜單,并且選擇了適當的項目之后,程序才會結束。你可以用如下方式實現這個行為:創建一個java.awt.PopupMenu的實例,創立一個帶有動作 listener的菜單項目,使用戶選擇該項即可結束程序,然后將該菜單項加入到彈出菜單中,最后調用public TrayIcon(Image image, String tooltip, PopupMenu popup)構造器使彈出菜單和圖標相關聯。與圖標的Image和相關聯的PopupMenu一起,你可以指定一個String來確定工具提示的文本內容,當鼠標指針移動到圖標上面時該文本就會出現。把null傳遞給tooltip就可以使它不再顯示。下列代碼段向系統托盤添加了一個帶有工具提示和彈出菜單的圖標:
          PopupMenu popup = new PopupMenu ();
          MenuItem miExit = new MenuItem ("Exit");
          ActionListener al;
          al = new ActionListener ()
          {
          ???? public void actionPerformed (ActionEvent e)
          ???? {
          ????????System.out.println ("Goodbye");
          ????????System.exit (0);
          ???? }
          };
          miExit.addActionListener (al);
          popup.add (miExit);

          TrayIcon ti = new TrayIcon (bi, "System Tray Demo #2", popup);

          tray.add (ti); // Assume that tray was previously created.
          ???????????????????????????????? // 假定托盤在之前已經被創建了


          當用戶右鍵單擊鼠標時彈出菜單就會出現,然后用戶可以選擇菜單的Exit項目來執行菜單項的動作listener,它就可以終止程序。
          除了鼠標右鍵動作之外,你也許還希望當用戶在圖標上使用其它的鼠標動作,例如雙擊。為了使程序響應這些鼠標動作,TrayIcon提供了下列注冊listener的方法:
          ·????????public void addActionListener(ActionListener listener):為TrayIcon添加一個動作listener。當用戶雙擊圖標時,這個listener的public void actionPerformed(ActionEvent e)方法將被調用。
          你也許想要在多個TrayIcon之間共享一個動作 listener,那么決定哪個圖標響應動作事件并且調用listener就變得很重要了。你可以通過TrayIcon 的public void setActionCommand(String command)方法來為其分配一個唯一的指令。然后你就可以在listener中調用java.awt.event.ActionEvent的 public String getActionCommand()方法來返回指令名稱以及識別TrayIcon。為方便起見,TrayIcon也指定了一個public String getActionCommand()方法。
          調用TrayIcon的public void removeActionListener(ActionListener listener)方法從TrayIcon上移除listener。你也可以通過調用public ActionListener [] getActionListeners()方法來獲取一個含有全部注冊的動作listener的數組。
          ·????????public void addMouseListener(MouseListener listener): 為TrayIcon添加一個鼠標listener。當鼠標指針位于圖標上方并且用戶按下、釋放、或者點擊鼠標左鍵時,這個listener的各種方法(除了public void mouseEntered(MouseEvent e)和public void mouseExited(MouseEvent e)不被支持)將會被調用。
          如果你調用java.awt.event.MouseEvent從父類繼承的public Component getComponent()方法,你將得到一個null值。然而,MouseEvent從父類繼承的public Object getSource()方法將返回與事件相關聯的TrayIcon。調用MouseEvent的public int getX()和public int getY()方法可以得到鼠標坐標。這些坐標是相對于屏幕的——而不是TrayIcon。調用TrayIcon's public void removeMouseListener(MouseListener listener)方法可以從TrayIcon中移除listener。通過調用public MouseListener [] getMouseListeners()可以獲取含有所有注冊的鼠標listener的一個數組。
          ·????????public void addMouseMotionListener(MouseMotionListener listener): 為TrayIcon添加一個鼠標運動listener。當用戶在圖標上移動鼠標指針時,這個listener的public void mouseMoved(MouseEvent e)方法將被調用。(public void mouseDragged(MouseEvent e)方法不支持)
          再一次,getComponent()返回null,getSource()返回一個與事件相關聯的TrayIcon,getX()和getY()返回的坐標是相對于屏幕的——而不是TrayIcon。

          調用TrayIcon的public void removeMouseMotionListener(MouseMotionListener listener)方法來從TrayIcon移除listener。你可以通過調用public MouseMotionListener [] getMouseMotionListeners()來得到一個含有全部注冊的鼠標動作listener的數組。

          表5展示了一個擁有自己的String工具提示和PopupMenu的程序SystemTrayDemo2。該程序也調用了之前介紹的listener注冊方法來注冊用于響應動作、鼠標和鼠標運動事件的listener。

          表5. SystemTrayDemo2.java
          // SystemTrayDemo2.java

          import java.awt.*;
          import java.awt.event.*;
          import java.awt.image.*;
          import java.beans.*;

          public class SystemTrayDemo2
          {
          ?? public static void main (String [] args)
          ?? {
          ??????if (SystemTray.isSupported ())
          ??????{
          ??????????// 獲取系統托盤

          ??????????SystemTray tray = SystemTray.getSystemTray ();

          ??????????// 為圖標創建圖像

          ??????????Dimension size = tray.getTrayIconSize ();
          ??????????BufferedImage bi = new BufferedImage (size.width, size.height,
          ????????????????????????????????????????????????BufferedImage.TYPE_INT_RGB);
          ??????????Graphics g = bi.getGraphics ();

          ??????????g.setColor (Color.red);
          ??????????g.fillRect (0, 0, size.width, size.height);
          ??????????g.setColor (Color.yellow);
          ??????????int ovalSize = (size.width < size.height) ? size.width : size.height;
          ??????????ovalSize /= 2;
          ??????????g.fillOval (size.width/4, size.height/4, ovalSize, ovalSize);

          ??????????try
          ??????????{
          ??????????????// 創建一個與程序的圖標相關聯的彈出菜單。選擇菜單唯一的一個菜單項就會結束程序。
          ??????????????PopupMenu popup = new PopupMenu ();
          ??????????????MenuItem miExit = new MenuItem ("Exit");
          ??????????????ActionListener al;
          ??????????????al = new ActionListener ()
          ?????????????????? {
          ?????????????????????? public void actionPerformed (ActionEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println ("Goodbye");
          ??????????????????????????System.exit (0);
          ?????????????????????? }
          ?????????????????? };
          ??????????????miExit.addActionListener (al);
          ??????????????popup.add (miExit);

          ??????????????// 從圖像創建一個圖標,當鼠標位于圖標上方式,選擇顯示一個工具提示,以及為圖標分配彈出式菜單。

          ??????????????TrayIcon ti = new TrayIcon (bi, "System Tray Demo #2", popup);

          ??????????????// 創建并且關聯一個listener到圖標上。當你采用了適當的動作,例如在Windows下雙擊鼠標,actionPerformed()方法將會被調用。
          ??????????????al = new ActionListener ()
          ?????????????????? {
          ?????????????????????? public void actionPerformed (ActionEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println (e.getActionCommand ());
          ?????????????????????? }

          ?????????????????? };
          ??????????????ti.setActionCommand ("My Icon");
          ??????????????ti.addActionListener (al);

          ??????????????// 創建并關聯一個鼠標listener來記錄于圖標相關聯的鼠標事件

          ??????????????MouseListener ml;
          ??????????????ml = new MouseListener ()
          ?????????????????? {
          ?????????????????????? public void mouseClicked (MouseEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println ("Tray icon: Mouse clicked");
          ?????????????????????? }

          ?????????????????????? public void mouseEntered (MouseEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println ("Tray icon: Mouse entered");
          ?????????????????????? }

          ?????????????????????? public void mouseExited (MouseEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println ("Tray icon: Mouse exited");
          ?????????????????????? }

          ?????????????????????? public void mousePressed (MouseEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println ("Tray icon: Mouse pressed");
          ?????????????????????? }

          ?????????????????????? public void mouseReleased (MouseEvent e)
          ?????????????????????? {
          ??????????????????????????System.out.println ("Tray icon: Mouse released");
          ?????????????????????? }
          ?????????????????? };
          ??????????????ti.addMouseListener (ml);

          ??????????????//創建并關聯一個鼠標運動listener來記錄于圖標相關聯的鼠標運動事件

          ??????????????MouseMotionListener mml;
          ??????????????mml = new MouseMotionListener ()
          ????????????????????{
          ????????????????????????public void mouseDragged (MouseEvent e)
          ????????????????????????{
          ?????????????????????????? System.out.println ("Tray icon: Mouse dragged");
          ????????????????????????}

          ????????????????????????public void mouseMoved (MouseEvent e)
          ????????????????????????{
          ?????????????????????????? System.out.println ("Tray icon: Mouse moved");
          ????????????????????????}
          ????????????????????};
          ??????????????ti.addMouseMotionListener (mml);

          ??????????????// 將圖標加入系統托盤

          ??????????????tray.add (ti);
          ??????????}
          ??????????catch (AWTException e)
          ??????????{
          ??????????????System.out.println (e.getMessage ());
          ??????????????return;
          ??????????}
          ??????}
          ?? }
          }


          與SystemTrayDemo1 同樣,SystemTrayDemo2在系統托盤顯示了同樣的圖標。移動鼠標到圖標上,除了在控制臺窗口顯示信息“Mouse moved”之外,工具提示也將會出現。當鼠標指針位于圖標上方時,試一下左鍵單擊,各種按下、釋放、點擊消息將會在控制臺窗口出現。如果你雙擊圖標,動作指令的名字將會在控制臺窗口出現。最后,右鍵單擊圖標,你將會看見一個帶有Exit選項的彈出菜單。表4演示了SystemTrayDemo2的圖標的兩個視圖。在左側你能看見圖標上的工具提示,在右側你可以看見圖標的彈出菜單。


          表4. 托盤圖標有自己的工具提示和彈出菜單

          TrayIcon類提供了額外的一些方法——其中一些是通過構造器調用的——你也許會感興趣。這些方法包括:
          ·????????public void displayMessage(String caption, String text, TrayIcon.MessageType messageType): 在系統托盤上圖標附近顯示一條彈出消息。這條消息持續的時間依賴于平臺,之后便會消失。(我相信這個方法并不影響與TrayIcon相關聯的工具提示。)
          caption 顯示在消息內容text的上方,caption或text都可以為null,但是如果均為null,該方法將拋出 NullPointerException。最后,messageType可以為下列消息類型之一: TrayIcon.MessageType.ERROR (錯誤消息), TrayIcon.MessageType.INFO (通知消息), TrayIcon.MessageType.NONE (簡單消息), or TrayIcon.MessageType.WARNING (警告消息)。當消息出現時,平臺可以使用messageType來決定什么動作——顯示圖形還是發出聲音——需要被執行。
          這個方法并不是所有的平臺都支持。例如,我在Windows ME平臺上就無法顯示消息。
          ·????????public void setImage(Image image): 創建一個image作為TrayIcon的Image。這用于指示程序狀態的改變是非常方便的。前一個Image會丟棄而不調用Image的flush ()方法——你必須明確的調用這個方法來轉儲所有被前一個Image對象使用的資源(包括為了屏幕繪制而緩存的像素數據)。如果image為null,該方法將拋出NullPointerException。調用public Image getImage()方法來返回當前的Image。
          ·????????public void setImageAutoSize(boolean autosize): 設置TrayIcon的自動調整尺寸屬性。這個屬性決定了Image是否自動調整尺寸來適應分配給系統托盤圖標的空間。如果你傳遞true給 autosize,Image將按照需要自動縮小或擴大。否則,Image將被裁減以適應分配的空間。調用public boolean isImageAutoSize()方法可以返回自動調整大小屬性的值。
          ·????????public void setPopupMenu(PopupMenu popup): 為TrayIcon設置彈出菜單。如果popup為null,沒有PopupMenu與相關聯TrayIcon。如果你試圖為不同的TrayIcon設置同樣的彈出菜單,該方法將拋出IllegalArgumentException。一些平臺也許不支持彈出菜單,當用戶右鍵單擊圖標時,他們或者不顯示菜單,或者不顯示本地版本的菜單。調用public PopupMenu getPopupMenu()方法來返回當前的PopupMenu。
          ·????????public void setToolTip(String tooltip): 為TrayIcon設置工具提示。當用戶把鼠標移動到系統托盤上的圖標上方時,工具提示會被顯示出來。傳遞null作為tooltip的值可以移除工具提示。工具提示在一些平臺上可能被簡化了。調用public String getToolTip()方法來返回當前的工具提示String。

          在文章的結尾,我希望可以避開一個潛在的易混淆點。關于isSupported()方法的SystemTray文檔建議“將默認動作同時加入到動作listener和彈出菜單中”以便保證托盤圖標的默認動作始終可用。這是什么意思呢?就是簡單地向PopupMenu添加一個Default菜單項,它擁有與你關聯到TrayIcon的動作listener同樣的動作listener,像如下代碼段所示:

          PopupMenu popup = new PopupMenu ();

          MenuItem miDefault = new MenuItem ("Default");
          ActionListener alDefault;
          alDefault = new ActionListener ()
          ???? {
          ???????? public void actionPerformed (ActionEvent e)

          ???????? {
          ????????????System.out.println (e.getActionCommand ());
          ???????? }
          ???? };
          miDefault.addActionListener (alDefault);
          popup.add (miDefault);

          MenuItem miExit = new MenuItem ("Exit");
          ActionListener alExit;
          alExit = new ActionListener ()
          ???? {
          ???????? public void actionPerformed (ActionEvent e)
          ???????? {
          ????????????System.out.println ("Goodbye");
          ????????????System.exit (0);
          ???????? }
          ???? };
          miExit.addActionListener (alExit);
          popup.add (miExit);

          TrayIcon ti = new TrayIcon (bi, "System Tray Demo #2", popup);
          ti.addActionListener (alDefault);


          結論

          Mustang 就要到來了,你一定已經忍不住想要在今年晚些時候正式版問世之前就體驗一下這個最新的Java平臺。為了幫助你順利起步,本文展示了一些你也許可以在這個平臺上找到的小示例。本文關注了4個重要的新特性:控制臺輸入/輸出、分區空間方法、啟動畫面API和系統托盤API。

          Jeff Friesen是一個自由軟件開發者和教育者,擅長C、C++和Java技術。

          資源
          ·???????? Matrix Java社區:
          http://www.matrix.org.cn
          ·????????下載本文的代碼文件:
          http://www.javaworld.com/javaworld/jw-01-2006/mustang/jw-0109-mustang.zip
          ·????????“創立與鞏固Java品牌” Jon Byous (Sun Developer Network, June 2005):
          http://java.sun.com/developer/technicalArticles/JavaOne2005/naming.html
          ·????????函數 GetDiskFreeSpaceEx:
          http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/getdiskfreespaceex.asp
          ·????????JSR 270: Java SE 6 ("Mustang") 發行目錄:
          http://www.jcp.org/en/jsr/detail?id=270
          ·????????“Mustang新的啟動畫面功能” Oleg Semenov and Dana Nourie (Sun Developer Network, September 2005):
          http://java.sun.com/developer/technicalArticles/J2SE/Desktop/mustang/splashscreen/index.html
          ·????????RFE #4050435: 不顯示字符的密碼輸入:
          http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4050435
          ·????????RFE #4057701: 空閑磁盤空間方法:
          http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4057701
          posted on 2006-06-28 15:55 rd2pm 閱讀(590) 評論(0)  編輯  收藏

          只有注冊用戶登錄后才能發表評論。


          網站導航:
           

          主站蜘蛛池模板: 建水县| 太仓市| 湾仔区| 蛟河市| 马关县| 乐山市| 曲松县| 个旧市| 洪湖市| 阳江市| 临武县| 淮安市| 喀喇沁旗| 临泽县| 来宾市| 乌海市| 乡城县| 奈曼旗| 重庆市| 繁峙县| 惠州市| 英吉沙县| 乐业县| 治多县| 寿光市| 麻江县| 文安县| 沁阳市| 拉萨市| 汶上县| 宁远县| 阳原县| 嵊泗县| 休宁县| 延津县| 象山县| 磐安县| 永年县| 达拉特旗| 阿荣旗| 西乡县|