sun 將要在今年的晚些時候發布最新的Java平台(開發代號Mustang)作為正式的Java平台Standard Edition 這個版本關注了幾個重要的主題例如兼容性和穩定性有關完整的主題列表參閱Java Specification Request JSE 的版本目錄
Mustang預期擁有的新特性包括(除了別的以外) 一個編譯器API 控制台輸入/輸出(I/O) 一個啟動畫面API 眾多的Java D性能改進 XML數字簽名 一個系統托盤API javaioFile類的分區空間方法 Java數據庫連接(JDBC) 公共注釋(Common annotations) 腳本支持(Scripting) 一個用於XML的流(streaming)API 排序過濾和加亮javaxswingJTable內容的能力 Javadoc標記的更新 可編程操作網絡屬性(例如廣播地址和子網掩碼) 方便地打印javaxswingtextJTextComponent的內容的能力
Mustang擁有遠遠超出一篇文章探討范圍的新特性因此本文只關注新特性的一小部份確切地說本文將討論用於控制台輸入/輸出和分區空間的方法用於啟動畫面以及與系統托盤交互的API
警告由於Mustang目前沒有最終發布一些特性還可能會被改變或者去掉所以當Sun公司最終發布Mustang的時候本文中的一些代碼可能會需要改動或者變得完全不相干了
注意我使用Sun公司的Java SDK版本rc (build )創建和測試了本文的代碼底層平台是Microsoft Windows ME
控制台輸入/輸出
在年月Sun收到了一個改進控制台輸入/輸出的增強請求(RFE)申請人特別要求一種可以提示用戶輸入密碼並且允許用戶輸入密碼(任意長度)而不會在控制台顯示出密碼字符的方法申請人指出抽象窗口工具包(AWT)的setEchoChar()方法並不合用因為它依賴於GUI的可用性然而很多基於服務器的操作系統根本不使用GUI
在年後期Sun回應了RFE #為Mustang (build )添加了javaioConsole類這個類所提供的方法可以訪問與當前虛擬機相關聯的基於字符的控制台設備但是在調用這些方法之前需要首先調用System的public static Console console()方法來獲取一個Console對象該方法將返回一個用來與控制台設備交互的Console對象但是如果控制台設備不存在就會返回null例如當你重定向標准輸入或標准輸出(或二者皆有)的時候在調用nsole()來返回Console對象之後下面的一段代碼將檢查返回的Console實例是否為null來測定控制台設備是否存在
Console console = nsole ()if (console == null){ Systemerrprintln (Console not available) return}
假設控制台設備是存在的你可以從控制台輸入流讀取密碼和整行的字符還可以向控制台輸出流寫入字符為了讀取密碼(而不會將密碼字符顯示到控制台輸出流)你必須調用Console的兩個readPassword()方法之一這兩個方法不允許換行符作為密碼的一部分如果達到了控制台輸入流的字符數目限制他們將返回null
舉例來說你可以調用public char [] readPassword(String fmt Object…… args)來提示用戶輸入密碼其中提示是由javautilFormatter類型格式化字符串(fmt)和它的變量表(args)來描述的然後從一個字符數組中返回用戶選擇的密碼下面的代碼段反復調用readPassword(String fmt Object…… args)來提示用戶輸入密碼直到用戶輸入了一個字符長度不小於MIN_PWD_LEN的密碼為止
char [] pwddo{ pwd = consolereadPassword (Enter password (%d characters min) MIN_PWD_LEN)}while (pwdlength < MIN_PWD_LEN)
在密碼被存儲到pwd之後就可以按需使用了然而出於安全性考慮在不需要使用密碼的時候應該將pwd清零
除了readPassword()方法之外Console提供了兩個readLine()方法以便於從控制台讀取一整行的字符並且將這些字符(不包括換行符)存儲在一個String中如果達到了控制台輸入流的字符數目限制這兩個方法都將返回null舉例來說你可以在不提示用戶的情況下調用public String readLine()來返回一行字符下列代碼段演示了這個方法
關於兩個readPassword()方法和兩個readLine()方法有一些有趣的事當這些方法遇到輸入/輸出錯誤時它們不拋出javaioIOException對象(例如會被Systeminread()拋出)而是拋出一個javaioIOError對象由於IOError是Error的子類你無需像捕捉IOException那樣來捕捉這個對象
向控制台輸出流輸出字符的配套方法是public Console format(String fmt Object…… args)和作用相同的public Console printf(String fmt Object…… args)方法後者內部調用了format()下列代碼段演示了printf()
consoleprintf (%s input)
輸出字符之後format()——和printf()(按擴展名)——自動轉儲清除控制台輸出流
注意控制台對象與唯一的一個javaioReader對象和唯一的一個javaioPrintWriter對象相關聯調用控制台的public Reader reader()方法可以返回Reader例如你可以把Reader傳遞給javautilScanner的一個構造函數來對控制台輸入流進行復雜的解析調用控制台的public PrintWriter writer()方法可以獲取PrintWriter然後你可以調用各種各樣有用的方法來輸出不同類型的數據到控制台上為方便起見控制台提供了一個public void flush()方法來調用PrinterWriter的flush()方法
讓我們用關於控制台輸入/輸出的知識來做一個實際應用——數據庫訪問最終我建立了一個Microsoft Access salesmdb銷售數據庫它隨著本文的代碼一同發布(可以在資源下載)該數據庫包含一個單獨的territories表它有兩列Name代表售貨員的名字Territory代表售貨員可以合法地出售產品的區域我將下面的數據輸入到了數據庫中並且為它設置了密碼保護——密碼是mustang
| —— | | Name | Territory | | —— | | John Doe | North | | Jane Doe | South | | Paul Smith | East | | Kathy Smith | West | | —— |
銷售數據庫由一個Sales應用軟件來訪問在訪問數據庫之前程序請求用戶輸入一個用戶名和密碼然後它使用這些數據通過JDBCODBC bridge驅動來連接sales數據源(你可以用Windows控制面板的ODBC數據源小程序來創建它用於識別salesmdb的位置)如果連接成功程序將輸出territories表的全部行和列的值列表顯示了Sales程序的源代碼
Listing Salesjava
// Salesjavaimport javaio*import javasql*import javautil*class Sales{ public static void main (String [] args) throws ClassNotFoundException SQLException { // Attempt to obtain a console // 嘗試獲取控制台 Console console = nsole () if (console == null) { Systemerrprintln (sales unable to obtain console) return } // Obtain username // 獲取用戶名 String username = consolereadLine (Enter username ) // Obtain password // 獲取密碼 String password = new String (consolereadPassword (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 = DriverManagergetConnection (jdbcodbcsales username password) // Garbage collect the password—not a good idea to keep passwords // hanging around // 對密碼進行垃圾收集——始終留著密碼可不是個好主意 password = null // Attempt to create a statement // 嘗試建立一個statement stmt = concreateStatement () // Establish the maximum number of rows that can be returned // 設置允許返回的最大行數 stmtsetMaxRows () // Attempt to fetch all rows from the territories table // 嘗試讀取territories表的所有行 String query = SELECT * FROM territories rs = stmtexecuteQuery (query) // Get number of columns // 獲取列數 int nCols = rsgetMetaData ()getColumnCount () // Read all rows of all columns into storage // 讀取所有列的每一行到向量存儲 int i = while (rsnext ()) { Object [] buffer = new Object [nCols] for (int j = j < nCols j++) buffer [j] = rsgetObject (j + ) // NOTE getObject requires a based column index // 注意getObject要求列索引號從開始 vadd (buffer) } // Extract rows from the array // 從數組中取出全部行 Object [] rows = vtoArray () for (i = i < rowslength i++) { // Extract columns from the row // 取出一行中的每列值 Object [] cols = (Object []) rows [i] // Print out the values from each column // 顯示輸出每一列的值 for (int j = j < colslength j++) consoleprintf (%s cols [j]) consoleprintf (\n) } consoleprintf (Value at Row Col = %s\n getValue (v )) } catch (SQLException e) { consoleprintf (sales %s\n egetMessage ()) } finally { if (stmt != null) try { stmtclose () } catch (SQLException e) {} if (con != null) try { conclose () } catch (SQLException e) {} } } static Object getValue (Vector v int row int col) { // The following conversion should really not be done in this method // because its inefficient Placing the conversion here is a matter of // convenience // 由於效率低下下列的轉換其實本不該在這個方法中進行把轉換放在這裡只是為了方便 Object [] rows = vtoArray () Object [] cols = (Object []) rows [row] return cols [col] }}
編譯Salesjava然後執行程序首先呈現在你面的是Enter username的提示你可以隨意輸入任何用戶名但至少要輸入點什麼接下來你將被提示輸入密碼確定這裡要使用mustang假如連接成功建立了你將會看到我在上面已經展示的表值
列表使用了Console的printf()方法來輸出表的內容然而這種方法有一個不十分明顯的問題如果territories表有很多行的數據在不滾動的情況下並不是所有的數據都適合控制台窗口的顯示為了獲取這些行通常需要重定向標准輸出到一個文件但是如果你這樣做的話nsole()將返回null你也就無法使用控制台輸入/輸出了因此在你的應用中使用Console的printf()和format()方法之前要考慮清楚不要使用它們向屏幕輸出大量的數據否則你可能就會無法看到全部數據了
注意
仔細地分析列表的代碼它並沒有通過ClassforName()來嘗試加載JDBCODBC bridge驅動由於Mustang支持JDBC ——它提供了一種內在機制來使DriverManager定位和加載驅動類——你不再需要通過ClassforName()來顯式地加載驅動類了
盡管Console解決了最初的RFE問題但是很多開發者都對這個類存在異議主要的批評包括 nsole()方法應該被命名為SystemgetConsole()然而一些不贊成的人爭辯說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 #的一個月之前Sun收到了另外一個RFE要求提供一種方法來得到可用的磁盤空間Sun回應了這個RFE #為File類加入了個分區空間方法 public long getFreeSpace()返回以抽象路徑名命名的分區的未分配空間字節數 如果抽象路徑名不是磁盤分區的名字返回值為空 public long getTotalSpace()返回以抽象路徑名命名的分區的總磁盤空間 如果抽象路徑名不是磁盤分區的名字返回值為空public long getUsableSpace()返回以抽象路徑名命名的虛擬機可用的空間字節數 如果抽象路徑名不是磁盤分區的名字返回值為空
對於Windows版本的Mustang這些方法是按照Microsoft的GetDiskFreeSpaceEx函數實現的讓我們基於這個函數的文檔重新定義一下這些方法 getFreeSpace() 將返回當前根目錄的磁盤的全部空閒字節數若當前根目錄是一個CDROM驅動器則返回 如果有不可寫CD在一個CDRW驅動器中會返回非值 getTotalSpace()將返回與調用線程相關的用戶可用的磁盤字節總數如果用戶配額被開啟這個值可能會小於磁盤的空閒字節總數getUsableSpace()將返回與調用線程相關的用戶可用的磁盤全部空閒字節數如果用戶配額被開啟這個值可能會小於磁盤的空閒字節總數如果當前根目錄是一個CDROM驅動器則返回如果有不可寫CD在一個CDRW驅動器中會返回非值
注意很多的現代操作系統允許限制每個用戶可用的最大磁盤空間這個最大空間叫做用戶的配額為用戶留出的磁盤空間區域叫做該用戶的分區在這種情況下getTotalSpace()返回的是與調用線程相關的用戶的配額如果用戶沒有配額限制(只有一個用戶)該方法返回磁盤字節總數同樣getUsableSpace()返回的是與調用線程相關的用戶的配額限制內的可用字節數如果用戶沒有配額限制該方法則返回磁盤的全部空閒字節數最後基於GetDiskFreeSpaceEx()的文檔getFreeSpace()磁盤的空閒字節數——而不考慮用戶的配額
列表展示了一個SpaceChecker程序的源代碼它將顯示文件系統的每一個根目錄的空閒空間以及分配給當前用戶的可用空間和全部空間
Listing SpaceCheckerjava
列表 SpaceCheckerjava
// SpaceCheckerjavaimport javaioFilepublic class SpaceChecker{ public static void main (String [] args) { File [] roots = FilelistRoots () for (int i = i < rootslength i++) { Systemoutprintln (roots [i]) Systemoutprintln (Free space = + roots [i]getFreeSpace ()) Systemoutprintln (Usable space = + roots [i]getUsableSpace ()) Systemoutprintln (Total space = + roots [i]getTotalSpace ()) Systemoutprintln () } }}
在我的Windows ME平台上運行該程序時我觀察到了下列信息A\ Free space = Usable space = Total space =
C\ Free space = Usable space = Total space =
M\ Free space = Usable space = Total space =
N\ Free space = Usable space = Total space =
由於磁盤配額沒有被應用到Windows ME操作系統上所以C的空閒空間與可用空間是相同的
啟動畫面 API
啟動畫面是基於GUI的現代應用軟件的一個重要部分啟動畫面不僅可以在漫長的軟件啟動過程中占據用戶的注意力(也可能用來通報用戶版本信息或者其他細節)還可以使用戶確定軟件正在啟動中這在Java環境中是尤其重要的因為JVM需要一段時間來加載和啟動圖列舉了一個啟動畫面的例子
圖 使用啟動畫面來顯示版權和其他重要信息
Mustang對啟動畫面的實現是使用一個能夠顯示GIF(包括動畫GIF)PNG或者JPEG圖像的無修飾窗口Java應用程序加載器創建一個啟動畫面窗口然後響應命令行參數或者JAR manifest入口從而在窗口中顯示指定的圖片 splash命令行參數創建一個啟動畫面窗口並且顯示一個指定的圖片例如java splashmylogogif MyApp將創建啟動畫面窗口並且在這個窗口中顯示mylogogif 確定的圖像(見圖)然後加載和啟動JVM最後使JVM加載MyAppclass並且執行該類的public static void main(String [] args)方法 由於你最有可能把重要的應用打包進一個jar文件 Mustang提供了一個SplashScreenImage manifest項目用來從jar文件的manifest中訪問啟動畫面的圖像例如SplashScreenImagemylogogif把mylogogif識別為在啟動畫面窗口中顯示的圖像
假設下列信息被放置在了一個manifest文件中
ManifestVersion MainClass MyAppSplashScreenImagemylogogif
將這個manifest文件mylogogif和全部相關的類文件都打包進myAppjarjava jar myAppjar會在加載和啟動JVM及運行應用程序之前創建啟動畫面並且顯示mylogogif
如果你調用java splashyourlogogif jar myAppjar將會發生什麼呢?在這種情況下yourlogogif將會在啟動畫面窗口中出現因為命令行參數splash優先替代了manifest設置SplashScreenImage
假如你希望在圖像上加入視覺效果來定制啟動畫面例如為一個有著漫長初始化過程的軟件的啟動畫面圖像加入一個動態的進度條來告知用戶剩余的時間(參考Mustang新的啟動畫面功能一文中的SplashTest程序)Mustang的javaawtSplashScreen類提供了對啟動畫面定制的支持
在沒有啟動畫面窗口的情況下一個SplashScreen對象不具有任何意義所以你不能從SplashScreen類實例化對象而啟動畫面窗口只當你指定了splash命令行選項或者SplashScreenImage manifest項目時才會存在當你指定了此命令行選項或者manifest項目並且窗口被創建出來作為其啟動過程的一部分Mustang會創建一個SplashScreen對象調用SplashScreen的public static SplashScreen getSplashScreen()方法可以返回這個對象的一個實例切記如果沒有啟動畫面窗口的話將會返回null在調用了SplashScreengetSplashScreen()來返回SplashScreen對象之後下力代碼段首先通過檢查返回的SplashScreen實例是否為null來測定啟動畫面窗口是否存在
SplashScreen ss = SplashScreengetSplashScreen ()if (ss != null){ // Customize the splash screen}
假設啟動畫面窗口是存在的你可以調用下列五種SplashScreen方法來獲取一個圖形環境以便於在緩沖區繪制圖象得到啟動畫面圖象獲取圖象的尺寸用其他圖象替換現有圖象或者是使用緩沖區中的內容來更新啟動畫面窗口
public Graphics getGraphics()以javaawtimageBufferedImage的形式創建一個覆蓋圖象它具有與啟動畫面圖象相同的尺寸還有一個窗口類型設置為BufferedImageTYPE_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()更新啟動畫面窗口使得覆蓋圖內容與當前的啟動畫面窗口的像素相合成由於采用了Sourceover合成覆蓋圖象的透明像素可以使啟動畫面圖象的像素顯示出來而覆蓋圖象的不透明像素則遮擋了它下面啟動畫面圖象的像素如果沒有調用getGraphics()來創建覆蓋圖象或者在啟動畫面圖象關閉之後它才被調用它將拋出IllegalStateException
列表通過一個PhotoAlbum軟件的輪廓演示了getSplashScreen()getGraphics()getSize()與update()這四個方法這段程序定制了啟動畫面在四周加上了一個紅色邊框並且在水平中心顯示一條信息Registering plugins……
Listing PhotoAlbumjava列表 PhotoAlbumjava
// PhotoAlbumjavaimport javaawt*public class PhotoAlbum{ public static void main (String [] args) { SplashScreen ss = SplashScreengetSplashScreen () if (ss != null) { Graphics g = ssgetGraphics () gsetColor (Colorred) // Colorwhite在我這個版本的Mustang中是默認顏色 Dimension size = ssgetSize () // 每個邊框寬度都是圖象的寬和高中較小值的% int borderSize if (sizewidth < sizeheight) borderSize = (int) (sizewidth * ) else borderSize = (int) (sizeheight * ) for (int i = i < borderSize i++) gdrawRect (i i sizewidthi* sizeheighti*) // 計算字符串在當前字體時的寬和高 FontMetrics fm = ggetFontMetrics () int strWidth = fmstringWidth (Registering plugins……) int strHeight = fmgetHeight () // 在字符串沒有超出啟動畫面窗口范圍時 if (strWidth < sizewidth && *strHeight < sizeheight) { gsetColor (Colorblue) gdrawString (Registering plugins…… (sizewidthstrWidth)/ sizeheight*strHeight) } // 拷貝覆蓋圖象到啟動畫面窗口 ssupdate () try { Threadsleep () // 暫停秒鐘以便查看圖象 } catch (InterruptedException e) { } } }}
為演示PhotoAlbum程序隨本文的代碼一起我加入了一張圖片palogojpg當你執行java splashpalogojpg PhotoAlbum之後將首先在屏幕的中間看見palogojpg的圖象在JVM完成載入並且開始運行main()你會看見如圖所示的合成圖象(也是居中的)
圖 標識圖片改變了自身的邊框顏色同時提示用戶正在注冊插件點擊縮略圖以觀看全尺寸圖象
當第一個AWT或者Swing窗口變為可見的時候啟動畫面窗口會自動關閉然而也許你想要在軟件窗口出現之前就將其關閉或者用你自己的窗口來替換它SplashScreen類提供了以下個方法來幫助你達到這個目的 public void close()隱藏並關閉啟動畫面窗口釋放分配給該窗口的全部資源如果在啟動畫面窗口關閉之後調用這個方法它將會拋出IllegalStateException public Rectangle getBounds()返回啟動畫面窗口的邊界如果你調用setImageURL()創建了一個不同尺寸的新啟動畫面圖象這些邊界將會改變如果在啟動畫面窗口關閉之後調用這個方法它將會拋出IllegalStateException public boolean isVisible()如果啟動畫面窗口是可見的將返回一個Boolea值true在窗口關閉之後調用則返回false
假如你用自己的窗口進行了替換你可以調用getBounds()來賦予它跟啟動畫面窗口同樣的初始坐標和尺寸此外當你自己的窗口變為可見的時候啟動畫面窗口將自動關閉
系統托盤API
系統托盤是桌面上一個專門的區域它顯示當前的時間和常駐內存的桌面應用的圖標並且被桌面上當前運行的所有應用共享表展示了Windows ME的系統托盤它位於Windows任務欄的右側
表 系統托盤上的顯示屬性應用程序圖標相關聯的菜單和工具提示
用戶只需要適當地輕點鼠標就隨時都可以與這些應用進行交互例如當鼠標位於應用軟件的圖標上時雙擊鼠標左鍵通常就可以打開應用的主窗口(在Windows平台上)同樣地把鼠標移到應用的圖標上面並且單擊右鍵通常就可以顯示特定應用的彈出菜單
Mustang引入了類javaawtSystemTray和javaawtTrayIcon來與系統托盤進行交互SystemTray代表桌面的系統托盤TrayIcon代表可以加入到系統托盤的一個圖標
同SplashScreen一樣你不能創建SystemTray對象而是必須調用SystemTray的public static SystemTray getSystemTray()方法來返回一個代表系統托盤區域的SystemTray實例由於在底層平台不支持系統托盤時該方法會拋出UnsupportedOperationException所以你必須首先調用public static boolean isSupported()如果系統托盤能得到最低限度的支持(除了顯示圖標之外最低限度的支持包括右鍵點擊圖標時顯示的彈出式菜單或者是左鍵雙擊圖標時響應的彈出事件)這個方法就將返回true否則返回false下列代碼段演示了獲取SystemTray實例的正確方式
if (SystemTrayisSupported ()){ SystemTray tray = SystemTraygetSystemTray () // Do stuff with tray}
假設系統托盤可以被支持你可以調用下列個方法來實現各種各樣的功能 public void add(TrayIcon trayIcon) 為SystemTray加入一個TrayIcon在這之後trayIcon所描述的圖標將在系統托盤顯示出來圖標加入到系統托盤的順序取決於平台的實現同樣當應用程序退出或者系統托盤變為不可用的時候圖標將被自動移除如果trayIcon為null該方法將拋出NullPointerException如果你試圖多次添加同樣的TrayIcon實例將拋出IllegalArgumentException如果系統托盤不可用將拋出AWTException public void addPropertyChangeListener(String propertyName PropertyChangeListener listener) 為trayIcons屬性加入一個javabeansPropertyChangeListener到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() 以javaawtDimension對象的形式返回圖標出現在系統托盤時將占用的水平和垂直尺寸其單位是像素在創建圖標之前調用該方法來決定圖標的首選尺寸這是TrayIcon的public Dimension getSize()方法的一種方便的實現 public void remove(TrayIcon trayIcon) 從SystemTray移除指定的TrayIcon圖標將從系統托盤移除同時將通報所有的屬性改變listener該方法不拋出任何異常即使trayIcon為null public void removePropertyChangeListener(String propertyName PropertyChangeListener listener) 移除propertyName指定屬性的listenerpropertyName必須是trayIcon(除此之外就沒有任何地方需要調用這個方法了)該方法不拋出任何異常即使propertyName或者listener為null
表是一個用來演示上述方法的SystemTrayDemo程序這個程序先創建了一個內部為實心紅色矩形的實心黃色圓形圖標將這個圖標添加到系統托盤暫停秒鐘之後從系統托盤將其移除再暫停秒鐘然後結束
表 SystemTrayDemojava
// SystemTrayDemojavaimport javaawt*import javaawtimage*import javabeans*public class SystemTrayDemo{ public static void main (String [] args) { if (SystemTrayisSupported ()) { // 獲取系統托盤 SystemTray tray = SystemTraygetSystemTray () // 注冊一個屬性變化listener來通知系統托盤上圖標的添加和移除 PropertyChangeListener pcl pcl = new PropertyChangeListener () { public void propertyChange (PropertyChangeEvent pce) { Systemoutprintln (Property changed = + pcegetPropertyName ()) Systemoutprintln () TrayIcon [] tia = (TrayIcon []) pcegetOldValue () if (tia != null) { Systemoutprintln (Old tray icon array contents ) for (int i = i < tialength i++) Systemoutprintln (tia [i]) Systemoutprintln () } tia = (TrayIcon []) pcegetNewValue () if (tia != null) { Systemoutprintln (New tray icon array contents ) for (int i = i < tialength i++) Systemoutprintln (tia [i]) Systemoutprintln () } } } trayaddPropertyChangeListener (trayIcons pcl) // 為圖標創建一個圖象 Dimension size = traygetTrayIconSize () BufferedImage bi = new BufferedImage (sizewidth sizeheight BufferedImageTYPE_INT_RGB) Graphics g = bigetGraphics () gsetColor (Colorred) gfillRect ( sizewidth sizeheight) gsetColor (Coloryellow) int ovalSize = (sizewidth < sizeheight) ? sizewidth sizeheight ovalSize /= gfillOval (sizewidth/ sizeheight/ ovalSize ovalSize) // 由該圖像創建一個圖標並且將其添加到系統托盤如果不能添加則結束程序 TrayIcon icon = null try { trayadd (icon = new TrayIcon (bi)) } catch (AWTException e) { Systemoutprintln (egetMessage ()) return } // 暫停以便查看系統托盤 try { Threadsleep () } catch (InterruptedException e) { } // 從系統托盤移除圖標 trayremove (icon) // 暫停以查看沒有了圖標的系統托盤 try { Threadsleep () } catch (InterruptedException e) { } // 結束程序 Systemexit () } }}
在獲取了系統托盤實例之後為這個實例注冊一個屬性改變listener並且為圖標創建一個javaawtImage表基於這個圖標創建了一個TrayIcon對象然後將該對象加入到系統托盤這個對象是通過調用TrayIcons public TrayIcon(Image image)構造器創建的這個構造器在TrayIcon對象中存儲了image然後當trayadd (icon = new TrayIcon (bi))將TrayIcon加入到SystemTray的時候這個image被取出並且顯示在系統托盤上如果image為null該構造器和TrayIcon的另外兩個構造器都拋出IllegalArgumentException如果當前平台不支持系統托盤這些構造器將拋出UnsupportedOperationException
如果你在命令行運行SystemTrayDemo你將會在系統托盤發現發現一個新圖標在控制台窗口將會獲得類似如下的輸出Property changed = trayIcons
Old tray icon array contents
New tray icon array contentsjavaawtTrayIcon@fc
Property changed = trayIcons
Old tray icon array contentsjavaawtTrayIcon@fc
幾分鐘之後圖標就會消失SystemTrayDemo程序結束這對於持續運行的應用來說是不合適的與此相反你也許希望當用戶右鍵點擊程序圖標以彈出菜單並且選擇了適當的項目之後程序才會結束你可以用如下方式實現這個行為創建一個javaawtPopupMenu的實例創立一個帶有動作 listener的菜單項目使用戶選擇該項即可結束程序然後將該菜單項加入到彈出菜單中最後調用public TrayIcon(Image image String tooltip PopupMenu popup)構造器使彈出菜單和圖標相關聯與圖標的Image和相關聯的PopupMenu一起你可以指定一個String來確定工具提示的文本內容當鼠標指針移動到圖標上面時該文本就會出現把null傳遞給tooltip就可以使它不再顯示下列代碼段向系統托盤添加了一個帶有工具提示和彈出菜單的圖標
PopupMenu popup = new PopupMenu ()MenuItem miExit = new MenuItem (Exit)ActionListener alal = new ActionListener (){ public void actionPerformed (ActionEvent e) { Systemoutprintln (Goodbye) Systemexit () }}miExitaddActionListener (al)popupadd (miExit)TrayIcon ti = new TrayIcon (bi System Tray Demo # popup)trayadd (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中調用javaawteventActionEvent的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)不被支持)將會被調用
如果你調用javaawteventMouseEvent從父類繼承的public Component getComponent()方法你將得到一個null值然而MouseEvent從父類繼承的public Object getSource()方法將返回與事件相關聯的TrayIcon調用MouseEvent的public int getX()和public int getY()方法可以得到鼠標坐標這些坐標是相對於屏幕的——而不是TrayIcon調用TrayIcons 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()返回nullgetSource()返回一個與事件相關聯的TrayIcongetX()和getY()返回的坐標是相對於屏幕的——而不是TrayIcon
調用TrayIcon的public void removeMouseMotionListener(MouseMotionListener listener)方法來從TrayIcon移除listener你可以通過調用public MouseMotionListener [] getMouseMotionListeners()來得到一個含有全部注冊的鼠標動作listener的數組
表展示了一個擁有自己的String工具提示和PopupMenu的程序SystemTrayDemo該程序也調用了之前介紹的listener注冊方法來注冊用於響應動作鼠標和鼠標運動事件的listener
表 SystemTrayDemojava
// SystemTrayDemojavaimport javaawt*import javaawtevent*import javaawtimage*import javabeans*public class SystemTrayDemo{ public static void main (String [] args) { if (SystemTrayisSupported ()) { // 獲取系統托盤 SystemTray tray = SystemTraygetSystemTray () // 為圖標創建圖像 Dimension size = traygetTrayIconSize () BufferedImage bi = new BufferedImage (sizewidth sizeheight BufferedImageTYPE_INT_RGB) Graphics g = bigetGraphics () gsetColor (Colorred) gfillRect ( sizewidth sizeheight) gsetColor (Coloryellow) int ovalSize = (sizewidth < sizeheight) ? sizewidth sizeheight ovalSize /= gfillOval (sizewidth/ sizeheight/ ovalSize ovalSize) try { // 創建一個與程序的圖標相關聯的彈出菜單選擇菜單唯一的一個菜單項就會結束程序 PopupMenu popup = new PopupMenu () MenuItem miExit = new MenuItem (Exit) ActionListener al al = new ActionListener () { public void actionPerformed (ActionEvent e) { Systemoutprintln (Goodbye) Systemexit () } } miExitaddActionListener (al) popupadd (miExit) // 從圖像創建一個圖標當鼠標位於圖標上方式選擇顯示一個工具提示以及為圖標分配彈出式菜單 TrayIcon ti = new TrayIcon (bi System Tray Demo # popup) // 創建並且關聯一個listener到圖標上當你采用了適當的動作例如在Windows下雙擊鼠標actionPerformed()方法將會被調用 al = new ActionListener () { public void actionPerformed (ActionEvent e) { Systemoutprintln (egetActionCommand ()) } } tisetActionCommand (My Icon) tiaddActionListener (al) // 創建並關聯一個鼠標listener來記錄於圖標相關聯的鼠標事件 MouseListener ml ml = new MouseListener () { public void mouseClicked (MouseEvent e) { Systemoutprintln (Tray icon Mouse clicked) } public void mouseEntered (MouseEvent e) { Systemoutprintln (Tray icon Mouse entered) } public void mouseExited (MouseEvent e) { Systemoutprintln (Tray icon Mouse exited) } public void mousePressed (MouseEvent e) { Systemoutprintln (Tray icon Mouse pressed) } public void mouseReleased (MouseEvent e) { Systemoutprintln (Tray icon Mouse released) } } tiaddMouseListener (ml) //創建並關聯一個鼠標運動listener來記錄於圖標相關聯的鼠標運動事件 MouseMotionListener mml mml = new MouseMotionListener () { public void mouseDragged (MouseEvent e) { Systemoutprintln (Tray icon Mouse dragged) } public void mouseMoved (MouseEvent e) { Systemoutprintln (Tray icon Mouse moved) } } tiaddMouseMotionListener (mml) // 將圖標加入系統托盤 trayadd (ti) } catch (AWTException e) { Systemoutprintln (egetMessage ()) return } } }}
與SystemTrayDemo同樣SystemTrayDemo在系統托盤顯示了同樣的圖標移動鼠標到圖標上除了在控制台窗口顯示信息Mouse moved之外工具提示也將會出現當鼠標指針位於圖標上方時試一下左鍵單擊各種按下釋放點擊消息將會在控制台窗口出現如果你雙擊圖標動作指令的名字將會在控制台窗口出現最後右鍵單擊圖標你將會看見一個帶有Exit選項的彈出菜單表演示了SystemTrayDemo的圖標的兩個視圖在左側你能看見圖標上的工具提示在右側你可以看見圖標的彈出菜單
表 托盤圖標有自己的工具提示和彈出菜單
TrayIcon類提供了額外的一些方法——其中一些是通過構造器調用的——你也許會感興趣這些方法包括 public void displayMessage(String caption String text TrayIconMessageType messageType) 在系統托盤上圖標附近顯示一條彈出消息這條消息持續的時間依賴於平台之後便會消失(我相信這個方法並不影響與TrayIcon相關聯的工具提示)
caption顯示在消息內容text的上方caption或text都可以為null但是如果均為null該方法將拋出NullPointerException最後messageType可以為下列消息類型之一TrayIconMessageTypeERROR (錯誤消息) TrayIconMessageTypeINFO (通知消息) TrayIconMessageTypeNONE (簡單消息) or TrayIconMessageTypeWARNING (警告消息)當消息出現時平台可以使用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給autosizeImage將按照需要自動縮小或擴大否則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 alDefaultalDefault = new ActionListener () { public void actionPerformed (ActionEvent e) { Systemoutprintln (egetActionCommand ()) } }miDefaultaddActionListener (alDefault)popupadd (miDefault) MenuItem miExit = new MenuItem (Exit)ActionListener alExitalExit = new ActionListener () { public void actionPerformed (ActionEvent e) { Systemoutprintln (Goodbye) Systemexit () } }miExitaddActionListener (alExit)popupadd (miExit)TrayIcon ti = new TrayIcon (bi System Tray Demo # popup)tiaddActionListener (alDefault)
結論
Mustang就要到來了你一定已經忍不住想要在今年晚些時候正式版問世之前就體驗一下這個最新的Java平台為了幫助你順利起步本文展示了一些你也許可以在這個平台上找到的小示例本文關注了個重要的新特性控制台輸入/輸出分區空間方法啟動畫面API和系統托盤API
Jeff Friesen是一個自由軟件開發者和教育者擅長CC++和Java技術
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25834.html