久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

創(chuàng)建窗口與Applet

 開心豆豆2010 2010-12-06


設(shè)計(jì)的宗旨是"能輕松完成簡單的任務(wù),,有辦法完成復(fù)雜的任務(wù)",。

本章只介紹Java 2的Swing類庫,并且合理假定Swing是Java GUI類庫的發(fā)展方向,。

本章的開頭部分會(huì)講,,用Swing創(chuàng)建applet與創(chuàng)建應(yīng)用程序有什么不同,以及怎樣創(chuàng)建一個(gè)既能當(dāng)applet在瀏覽器里運(yùn)行,,又能當(dāng)普通的應(yīng)用程序,,在命令行下運(yùn)行程序。

Swing類庫的體系龐大,,而本章的目的也只是想讓你從基礎(chǔ)開始理解并且熟悉這些概念,。如果你有更高的要求,只要肯花精力研究,,Swing大概都能做到,。

你越了解Swing,你就越能體會(huì)到:
與其它語言或開發(fā)環(huán)境相比,,Swing是一個(gè)好得多的編程模型,。而JavaBeans (本章的臨近結(jié)尾的地方會(huì)作介紹)則是專為這個(gè)構(gòu)架服務(wù)的類庫。 就整個(gè)Java開發(fā)環(huán)境來講,,"GUI builders" (可視化編程環(huán)境)只是一個(gè)"社交"的層面,。當(dāng)你用圖形工具把組件放到窗體上的時(shí)候,實(shí)際上是GUI builder在調(diào)用JavaBeans和Swing為你編寫代碼,。這樣不僅能可以加快GUI的開發(fā)速度,,而且能讓你做更多實(shí)驗(yàn)。這樣你就可以嘗試更多的方案,,得到更好的效果了,。 Swing的簡單易用與設(shè)計(jì)合理,使你即便用GUI builder也能得到可讀性頗佳的代碼,。這一點(diǎn)解決了GUI builder的一個(gè)老問題,,那就是代碼的可讀性。
Swing囊括了所有比較時(shí)髦的用戶界面元素:從帶圖像的按鈕到樹型控件和表格控件,,應(yīng)有盡有,??紤]到類庫的規(guī)模,其復(fù)雜性還是比較理想的,。如果要做的東西比較簡單,,代碼就不會(huì)很多,如果項(xiàng)目很復(fù)雜,,那么代碼就會(huì)相應(yīng)地變得復(fù)雜,。也就是說入門很容易,但是如果有必要,,它也可以變得很強(qiáng)大,。

對(duì)Swing的好感,很大程度上源于其"使用的正交性",。也就是說,,一旦領(lǐng)會(huì)了這個(gè)類庫的精神,你就可以把這種概念應(yīng)用到任何地方,。這一點(diǎn)首先就緣于其標(biāo)準(zhǔn)的命名規(guī)范,。通常情況下,如果想把組件嵌套到其它組件里,,直接插就行了,。

為了照顧運(yùn)行速度,組件都是"輕量級(jí)"的,。為了能跨平臺(tái),,Swing是完全用Java寫的。

鍵盤支持是內(nèi)置的,;運(yùn)行Swing應(yīng)用的時(shí)候可以完全不用鼠標(biāo),,這一點(diǎn),不需要額外的編程,。加滾動(dòng)條,,很容易,只要把控件直接嵌到JScrollPane里就行了,。加Tooltip也只要一行代碼,。

Swing還提供一種更前衛(wèi)的,被稱為"pluggable look andfeel(可插接式外觀)"的功能,,也就是說用戶界面的外觀可以根據(jù)操作系統(tǒng)或用戶的習(xí)慣動(dòng)態(tài)地改變,。你甚至可以自己發(fā)明一套外觀(當(dāng)然是很難的)。

基本的applet

Java能創(chuàng)建applet,,也就是一種能在Web瀏覽器里運(yùn)行的小程序,。Applet必須安全,所以它的功能很有限,。但是applet是一種很強(qiáng)大的客戶端編程工具,,而后者是Web開發(fā)的一個(gè)大課題,。

Applet的限制

applet編程的限制是很多的,所以也經(jīng)常被稱作關(guān)在"沙箱"里,。因?yàn)闀r(shí)時(shí)刻刻都有一個(gè)人——也就是Java的運(yùn)行時(shí)安全系統(tǒng)——在監(jiān)視著你,。

不過你也可以走出沙箱,編寫普通的應(yīng)用程序而不是applet,,這樣就可以訪問操作系統(tǒng)的其它功能了,。迄今為止,我們寫的都是這種應(yīng)用程序,,只不過它們都是些沒有圖形界面的控制臺(tái)程序罷了,。Swing也可以寫GUI界面的應(yīng)用程序,。

大體上說,,要想知道applet能做什么,最好先去了解一下,,為什么要有applet:用它來擴(kuò)展瀏覽器里的Web頁面的功能,。因?yàn)樯暇W(wǎng)的人是不可能真正知道這個(gè)頁面是不是來自于一個(gè)無惡意的網(wǎng)站的,但是你又必須確保所有運(yùn)行的代碼都是安全的,。所以你會(huì)發(fā)現(xiàn),,它的最大的限制是:
Applet不能訪問本地磁盤。Java為applet提供了數(shù)字簽名,。你可以選擇讓有數(shù)字簽名(由可信的開發(fā)商簽署)的applet訪問你的硬盤,,這樣applet的很多限制就被解除了。本章的后面在講Java Web Start的時(shí)候,,會(huì)介紹一個(gè)這樣的例子的,。Web Start是一種通過Internet將應(yīng)用程序安全地送到客戶端的方案。 Applet的啟動(dòng)時(shí)間很長,,因?yàn)槊看味嫉孟螺d所有東西,,而且每下載一個(gè)類都要向服務(wù)器發(fā)一個(gè)請(qǐng)求?;蛟S瀏覽器會(huì)作緩存,,但這并不是一定的。所以一定要把a(bǔ)pplet的全部組件打成一個(gè)JAR的(Java ARchive)卷宗(除了.class文件之外,,還包括圖像,,聲音),這樣只要發(fā)一個(gè)請(qǐng)求就可以下載整個(gè)applet了,。JAR卷宗里的東西可以逐項(xiàng)地"數(shù)字簽名",。
Applet的優(yōu)勢

如果你不在意這些限制,applet還是有明顯優(yōu)勢的,,特別是在構(gòu)建client/server 或是其他網(wǎng)絡(luò)應(yīng)用的時(shí)候:
沒有安裝的問題。Applet是真正平臺(tái)無關(guān)的(包括播放音頻文件) ,所以你用不著去為不同的平臺(tái)修改程序,,用戶也用不著安裝完了之后再作調(diào)整,。實(shí)際上每次載入有applet的Web頁面時(shí),,安裝就自動(dòng)完成了,。因此軟件的更新可以不驚動(dòng)客戶自動(dòng)地完成,。為傳統(tǒng)的client/server系統(tǒng)構(gòu)建和安裝一個(gè)新版的軟件,通常都是一場惡夢,。 由于Java語言和applet內(nèi)置了安全機(jī)制,,因此你不用擔(dān)心錯(cuò)誤代碼會(huì)破壞別人的機(jī)器,。有了這兩個(gè)優(yōu)勢,,Java就能在intranet的client/server應(yīng)用里大展身手了。所謂intranet的client/server應(yīng)用,,是指僅存在于公司內(nèi)部的,,或者可以限定和控制用戶環(huán)境的(Web瀏覽器和插件)特殊場合的client/server應(yīng)用,。
由于applet是自動(dòng)集成到HTML里面的,因此你就有了一種與平臺(tái)無關(guān)的,能支持applet的文檔系統(tǒng)了(譯者注:指HTML),。這真是太有趣了,,因?yàn)槲覀兺ǔ6颊J(rèn)為文檔是程序的一部分,而不是相反,。

應(yīng)用框架

類庫通常按功能進(jìn)行分類,。有些類庫是拿來直接用的,比如Java標(biāo)準(zhǔn)類庫里面的String和ArrayList,。有些類庫則是用來創(chuàng)建其它類的,。此外還有一種被稱為應(yīng)用框架(application framework)的類庫。它的目的是,,提供一個(gè)或一組具備某些基本功能的類,,幫助程序員創(chuàng)建應(yīng)用程序。而這些基本功能,,是這類應(yīng)用程序所必備的,。于是你寫應(yīng)用程序的時(shí)候,只要繼承這個(gè)類,,然后再根據(jù)需要,,覆寫幾個(gè)你感興趣的方法,定制一下它的行為就可以了,。應(yīng)用框架的默認(rèn)控制機(jī)制會(huì)在適當(dāng)?shù)臅r(shí)機(jī),,調(diào)用那些你寫的方法。應(yīng)用框架是一種"將會(huì)變和不會(huì)變的東西分開來"的絕好的例子,。它的設(shè)計(jì)思想是,,通過覆寫方法把程序的個(gè)性化部分留在本地。[76]

Applet是用應(yīng)用框架創(chuàng)建的,。你只要繼承JApplet類,,再覆寫幾個(gè)方法就可以了,。下面幾個(gè)方法可以控制Web頁面上的applet的創(chuàng)建和執(zhí)行:

方法

操作

init( )

applet初始化的時(shí)候會(huì)自動(dòng)調(diào)用,其任務(wù)包括裝載組件的布局,。必須覆寫,。

start( )

在Web瀏覽器上顯示applet的時(shí)候調(diào)用。顯示完畢之后,,applet才開始正常工作,,(特別是那些用stop( )關(guān)閉的applet)。(此外,,應(yīng)用框架)調(diào)用完init( )之后也會(huì)調(diào)用這個(gè)方法,。

stop( )

讓applet從Web瀏覽器上消失的時(shí)候調(diào)用,這樣它就能關(guān)閉一些很耗資源的操作了,。此外(應(yīng)用框架調(diào)用)destroy( )之前也會(huì)先調(diào)用這個(gè)方法,。

destroy( )

當(dāng)(瀏覽器)不再需要這個(gè)applet了,要把它從頁面里卸載下來的時(shí)候,,就會(huì)調(diào)用這個(gè)方法以釋放資源了,。

注意applet不需要main()。它已經(jīng)包括在應(yīng)用框架里了,;你只要把啟動(dòng)代碼放到init( )里面就行了,。

JLabel的構(gòu)造函數(shù)需要一個(gè)String作參數(shù)。

init( )方法負(fù)責(zé)將組件add( )到表單上,?;蛟S你會(huì)覺得,應(yīng)該能直接調(diào)用它自己(JApplet)的add( )方法,。實(shí)際上AWT就是這么做的,。Swing要求你將所有的組件都加到表單的"內(nèi)容面板(content pane)"上,所以add( )的時(shí)候,,必須先調(diào)用getContentPane( ),。

在Web瀏覽器里運(yùn)行applet

要想運(yùn)行程序,先得把a(bǔ)pplet放到Web頁面里,,然后用一個(gè)能運(yùn)行Java的Web瀏覽器打開頁面,。你得用一種特殊的標(biāo)記把a(bǔ)pplet放到Web頁面里,然后讓它來告訴頁面該怎樣裝載并運(yùn)行這個(gè)applet,。

這個(gè)步驟曾經(jīng)非常簡單。那時(shí)Java自己就很簡單,,所有人都用同一個(gè)Java,,瀏覽器里的Java也都一樣。所以你只要稍微改一下HTML就行了,,就像這樣:

<applet code=Applet1 width=100 height=50>

</applet>

但是隨后而來的瀏覽器和語言大戰(zhàn)使我們(不僅是程序員,,還包括最終用戶)都成了輸家,。不久,Sun發(fā)現(xiàn)再也不能指望靠瀏覽器來支持風(fēng)味醇正的Java了,,唯一的解決方案是利用瀏覽器的擴(kuò)展機(jī)制來提供"插件(add-on)",。通過這個(gè)辦法(要想禁掉Java,除非把所有第三方的插件全都禁掉,,但是為了獲取競爭優(yōu)勢,,沒有一個(gè)瀏覽器的提供商會(huì)這么做的),Sun確保了Java不會(huì)被敵對(duì)的瀏覽器提供商給禁掉,。

對(duì)于Internet Explorer,這種擴(kuò)展機(jī)制就是ActiveX的控件,而Netscape的則是plugin,。你做頁面時(shí)必須為這兩個(gè)瀏覽器各寫一套標(biāo)記,,不過JDK自帶了一個(gè)能自動(dòng)生成標(biāo)記的HTMLconverter工具。下面就是用HTMLconverter處理過的Applet1的HTML頁面:

<!--"CONVERTED_APPLET"-->

<!-- HTML CONVERTER -->

<OBJECT

    classid = "clsid:CAFEEFAC-0014-0001-0000-ABCDEFFEDCBA"

    codebase = "http://java./products/plugin/autodl/jinstall-1_4_1-windows-i586.cab#Version=1,4,1,0"

    WIDTH = 100HEIGHT = 50 >

    <PARAM NAME= CODE VALUE = Applet1 >

    <PARAM NAME= "type" VALUE = "application/x-java-applet;jpi-version=1.4.1">

    <PARAM NAME= "scriptable" VALUE = "false">

    <COMMENT>

      <EMBED

          type = "application/x-java-applet;jpi-version=1.4.1"

          CODE =Applet1

          WIDTH =100

          HEIGHT =50  

         scriptable = false

         pluginspage = "http://java./products/plugin/index.html#download">

         <NOEMBED>

         </NOEMBED>

     </EMBED>

   </COMMENT>

</OBJECT>

<!--

<APPLET CODE = Applet1 WIDTH = 100 HEIGHT = 50>

</APPLET>

-->

<!--"END_CONVERTED_APPLET"-->

 

code的值是applet所處的.class文件的名字,,width和height則表示applet的初始尺寸(和前面一樣,,以象素為單位)。此外applet標(biāo)記里面還可以放一些東西:到哪里去找.class文件(codebase),,怎樣對(duì)齊(align),,applet相互通訊的時(shí)候要用的標(biāo)識(shí)符(name),以及提供給applet的參數(shù),。參數(shù)的格式是這樣的:

<param name="identifier"value = "information">

你可以根據(jù)需要,,加任意多個(gè)參數(shù)。

Appletviewer的用法

Sun的JDK包含一個(gè)名為Appletviewer的工具,,它可以根據(jù)<applet>標(biāo)記在HTML文件里找出applet,,然后不顯示HTML文本,直接運(yùn)行這個(gè)applet,。由于Appletviewer忽略了除applet標(biāo)記之外的所有其他東西,,因此你可以直接把a(bǔ)pplet標(biāo)記當(dāng)作注釋放到Java的源文件里:

// <applet code=MyApplet width=200height=100></applet>

這樣你就可以用"appletviewer MyApplet.java"來啟動(dòng)applet了,不用再寫HTML的測試文件了,。

Applet的測試

如果只是想簡單的測試一下,,那么根本不用上網(wǎng)。直接啟動(dòng)Web瀏覽器,,打開含applet標(biāo)記的HTML文件就可以了,。瀏覽器裝載HTML的時(shí)候會(huì)發(fā)現(xiàn)applet標(biāo)記,并且按照code的值去找相應(yīng)的.class文件,。當(dāng)然,,是在CLASSPATH里面找。如果CLASSPATH里沒有,,它就在瀏覽器的狀態(tài)欄里給一個(gè)錯(cuò)誤信息,,告訴你找不到.class文件,。

如果客戶端的瀏覽器不能在服務(wù)器上找到.class文件,它會(huì)到客戶機(jī)的CLASSPATH里面去找,。這樣一來,,就有可能發(fā)生,瀏覽器在服務(wù)器上找不到.class文件,,因此用你機(jī)器上的文件啟動(dòng)了applet的情形了,。于是在你看來一切都很正常,而其他人則什么都看不見了,。所以測試之前,,一定要把本地的.class文件(或.jar文件)全部刪了,只有這樣才能知道程序是不是呆在正確的服務(wù)器目錄里,。

我就曾經(jīng)在這里栽過一個(gè)很冤枉的跟頭,。有一次,我上載HTML和applet的時(shí)候搞錯(cuò)了package的名字,,因此把a(bǔ)pplet放錯(cuò)了目錄,。但是我的瀏覽器還能在本地的CLASSPATH里找到程序,所以我成了唯一一個(gè)能正常運(yùn)行這個(gè)applet的人了,。所以給applet標(biāo)記的CODE參數(shù)賦值的時(shí)候,,一定要給全名,這一點(diǎn)非常重要,。很多公開發(fā)表的applet都沒有打包,,但是實(shí)際工作中,最好還是打個(gè)包,。

用命令行啟動(dòng)applet

有時(shí)你還會(huì)希望讓GUI程序能做一些別的事情,。比方說在保留Java引以為豪的跨平臺(tái)性的前提下,讓它做一些"普通"程序能作的事,。

你可以用Swing類庫寫出與當(dāng)前操作系統(tǒng)的外觀風(fēng)格完全一致的應(yīng)用程序,。如果你要編寫GUI程序,先看看是不是能最新版的Java及與之相關(guān)的工具,,只有是,,那才值得去寫。因?yàn)橹挥羞@樣,,才能寫出不會(huì)讓用戶覺得討厭的程序,。。

你肯定會(huì)希望能編寫出既能在命令行下啟動(dòng),,又能當(dāng)applet運(yùn)行的程序,。這樣測試applet的時(shí)候,會(huì)非常方便,。因?yàn)橥ǔ拿钚袉?dòng)要比從Web瀏覽器和Appletviewer啟動(dòng)更快,,也更方便。

要想創(chuàng)建能用命令行啟動(dòng)的applet,,只要在類里加一個(gè)main( ),,讓它把這個(gè)applet的實(shí)例嵌到JFrame里面就行了。

我們只加了一個(gè)main( ),,其它什么都沒動(dòng),。main( )創(chuàng)建了applet的實(shí)例,并把它加到JFrame里面,,這樣我們就能看到它了,。

你會(huì)看到main( )明確地調(diào)用了applet的init()和start( )。這是因?yàn)?,這兩步原先是交給瀏覽器做的,。當(dāng)然這并不能完全替代瀏覽器,因?yàn)榍罢哌€會(huì)調(diào)用stop( )和destroy( ),,不過大致說來,,這個(gè)辦法還是可行的。如果還有問題,,你可以親自去調(diào)用,。[80]

注意,要是沒有這行:

frame.setVisible(true);

那么你什么都看不見,。

一個(gè)專門用來顯示Applet的框架

雖然將applet轉(zhuǎn)換成應(yīng)用程序的代碼是非常有用的,,但寫多了就既浪費(fèi)紙張又讓人生厭了。

setDefaultCloseOperation( )的作用是,,告訴程序,,一旦JFrame被關(guān)掉了,程序也應(yīng)該退出了,。默認(rèn)情況下關(guān)掉JFrame并不意味著退出程序,,所以除非你調(diào)用setDefaultCloseOperation( )或者寫一些類似的代碼,否則程序是不會(huì)中止的,。

制作按鈕

做按鈕很簡單:想讓按鈕顯示什么,,就拿它作參數(shù)去調(diào)用JButton的構(gòu)造函數(shù)。

通常按鈕是類的一個(gè)字段,,這樣就能用它來表示按鈕了,。

JButton是一個(gè)組件——它有自己的小窗口——更新的時(shí)候會(huì)自動(dòng)刷新。也就是說,,你不用明確地指示該如何顯示按鈕或是其他什么控件,;只要把它們放到表單上,它們就能自己把自己給畫出來了,。所以你得在init( )把按鈕放到表單上,。

在往content pane里面加?xùn)|西之前,,我們先把它的"布局管理器(layout manager)"設(shè)成FlowLayout。布局管理器的作用是告訴面板將控件放在表單上的哪個(gè)位置,。這里我們不能用applet默認(rèn)的BorderLayout,,這是因?yàn)槿绻盟坏┠阃韱紊霞恿诵碌目丶?,舊控件就會(huì)被全部遮住了,。而FlowLayout則會(huì)讓控件均勻地散布在表單上,從左到右,,從上到下,。

捕捉事件

編寫事件驅(qū)動(dòng)的程序是GUI編程的一個(gè)重要重要組成部分,而驅(qū)動(dòng)程序的基本思路就是,,將事件與代碼聯(lián)系起來,。

Swing的思路是,將接口(圖形組件)和實(shí)現(xiàn)(當(dāng)組件發(fā)生了某個(gè)事件之后,,你要運(yùn)行的代碼)明明白白地分開來,。Swing組件能通報(bào)在它身上可以發(fā)生什么事件,以及發(fā)生了什么事件,。所以,,如果你對(duì)某個(gè)事件不感興趣,比如鼠標(biāo)從按鈕的上方經(jīng)過,,你完全可以不去理會(huì)這個(gè)事件,。用這種方法能非常簡潔優(yōu)雅地解決事件驅(qū)動(dòng)的問題,一旦你理解了其基本概念,你甚至可以去直接使用過去從未看到過的Swing組件,。實(shí)際上這個(gè)模型也適用于JavaBean,。

我們還是先來關(guān)心一下我們要用的這個(gè)控件的主要事件,。就這個(gè)例子而言,,這個(gè)控件JButton,,而"我們感興趣的事件"就是按下按鈕,。要想表示你對(duì)按鈕被按下感興趣,可以調(diào)用addActionListener(),。這個(gè)方法需要一個(gè)ActionListener參數(shù)。ActionListener是一個(gè)接口,,它只有一個(gè)方法,actionPerformed( )。所以要想讓JButton執(zhí)行任務(wù),,你就得實(shí)現(xiàn)先定制一個(gè)ActionListener,然后用addActionListener方法把這個(gè)類的實(shí)例注冊(cè)給JButton,。當(dāng)按鈕被按下的時(shí)候,,這個(gè)方法就會(huì)啟動(dòng)了。(這通常被稱作回調(diào)(callback))

JTextField這個(gè)組件可以顯示文本,。它的文本既可以是用戶輸入的,,也可以是程序插的。雖然創(chuàng)建JTextField的方法有很多,,但是最簡單的還是把寬度直接傳給JTextField的構(gòu)造函數(shù),。等到JTextField被放上了表單,你就可以用setText( )來修改內(nèi)容了(JTextField還有很多方法,,請(qǐng)參閱JDK文檔),。

JTextArea

與JTextField相比,JTextArea能容納多行文字,,提供更多的功能,,除此之外它們很相似。append( )方法很有用,;你可以用它把程序的輸出直接導(dǎo)到JTextArea里,,這樣你就能用Swing程序來改進(jìn)我們先前寫的,,往標(biāo)準(zhǔn)輸出上打印的命令行程序了(因?yàn)槟隳苡脻L動(dòng)條看到前面的輸出了)。

在把JTextArea加入applet的時(shí)候,我們先把它嵌入JScrollPane,這樣一旦文本多了出來,滾動(dòng)條就會(huì)自動(dòng)顯現(xiàn)了,。裝滾動(dòng)條就這么簡單,。相比其它GUI編程環(huán)境下的類似控件,JScrollPane的簡潔與優(yōu)雅真是令人折服。

控制布局

用Java把組件放到表單上的方法可能與你見過的其他GUI系統(tǒng)的都不同。首先,它全部都是代碼,,根本沒有"資源"這個(gè)故事。其次,,組件在表單上的位置不是由絕對(duì)定位來控制的,而是由一個(gè)被稱為"布局管理器"的對(duì)象來控制的。布局管理器的作用是根據(jù)組件add( )的順序,,決定其擺放的位置,。組件的大小,,形狀,擺放的位置會(huì)隨布局管理器的不同而有明顯的差別,。此外布局管理器會(huì)根據(jù)applet或應(yīng)用程序的窗口大小,,自動(dòng)調(diào)整組件的位置,這樣一旦窗口的大小改變了,,組件的尺寸,,形狀,擺放位置也會(huì)相應(yīng)的作出調(diào)整,。

JApplet,,JFrame ,JWindow以及JDialog,,這幾個(gè)組件都有一個(gè)getContentPane( )方法,。這個(gè)方法能返回一個(gè)Container,而這個(gè)Container的作用是安置(contain)和顯示Component,。Container有一個(gè)setLayout( )方法,你就是用它來選擇布局管理器的。其它類,比如JPanel,能直接安置和顯示組件,,所以你得跳過內(nèi)容面板(contentpane)直接設(shè)置它的布局管理器,。

BorderLayout

Applet的默認(rèn)布局管理器是BorderLayout,。如果沒有其它指令,BorderLayout會(huì)把所有控件全都放到表單的正中,并且拉伸到最大。

好在BorderLayout的功能還不止這些,。它還有四個(gè)邊界以及中央的概念,。當(dāng)你往BorderLayout的面板上加控件加時(shí),,你還可以選擇重載過的add( )方法。這時(shí)要給它一個(gè)常量。這個(gè)常量可以是下邊五個(gè)中的一個(gè):

BorderLayout. NORTH

頂邊

BorderLayout. SOUTH

底邊

BorderLayout. EAST

右邊

BorderLayout. WEST

左邊

BorderLayout.CENTER

填滿當(dāng)中,,除非碰到其它組件或表單的邊緣

如果你不指明擺放對(duì)象的區(qū)域,默認(rèn)它就使用CENTER。

除了CENTER,其它控件都會(huì)在一個(gè)方向上壓縮到最小,在另一個(gè)方向上拉伸到最大。而CENTER則會(huì)往兩個(gè)方向上拉伸,,直至占滿整個(gè)中間區(qū)域。

FlowLayout

它會(huì)讓組件直接"流"到表單上,,從左到右,,從上到下,一行滿了再換一行,。

用了FlowLayout之后,,組件就會(huì)凍結(jié)在它的"固有"尺寸上。比如JButton就會(huì)根據(jù)字符串的長度來安排大小,。

由于FlowLayout面板上的控件會(huì)被自動(dòng)地壓縮到最小,,因此時(shí)常會(huì)出現(xiàn)一些意外。比方說,,JLabel是根據(jù)字符串來決定控件的大小的,,所以當(dāng)你對(duì)FlowLayout面板上的JLable控件的文本作右對(duì)齊時(shí),顯示效果不會(huì)有任何變化,。

GridLayout

GridLayout的意思是,,把表單視作組件的表格,當(dāng)你往里加?xùn)|西的時(shí)候,,它會(huì)按從左到右,,從上到下的順序把組件放到格子里。創(chuàng)建布局管理器的時(shí)候,你得在構(gòu)造函數(shù)里指明行和列的數(shù)目,這樣它就能幫你把表單劃分成相同大小的格子了。

GridBagLayout

GridBagLayout的作用是,,當(dāng)窗口的大小發(fā)生變化時(shí),你能用它來準(zhǔn)確地控制窗口各部分的反應(yīng)。但與此同時(shí),,它也是最難和最復(fù)雜的布局管理器,。它主要是供GUI builder生成代碼用的(或許GUIbuilder用的不是絕對(duì)定位而是GridBagLayout)。如果你的設(shè)計(jì)非常復(fù)雜,以至于不得不用GridBagLayout,,那么建議你使用GUI builder,。

絕對(duì)定位

還可以這樣對(duì)圖形組件進(jìn)行絕對(duì)定位:
將Container的布局管理器設(shè)成null:setLayout(null),。 往面板上加組件的時(shí)候,,先調(diào)用setBounds( )或reshape( )方法(根據(jù)各個(gè)版本)為組件設(shè)一個(gè)以象素為單位的矩形,。這個(gè)步驟可以放在構(gòu)造函數(shù)里,,也可以放在paint( )里面,看你的需要,。
有些GUI builder主要用的就是這個(gè)方法,但是通常情況下,,這并不是最好的方法,。

BoxLayout

由于GridBagLayout實(shí)在是太難學(xué)難用了,,所以Swing引入了一個(gè)新的BoxLayout,。它保留了GridBagLayout的很多優(yōu)點(diǎn),但是卻沒那么復(fù)雜,。所以當(dāng)你想手工控制控件的布局時(shí),,可以優(yōu)先考慮用它(再重申一遍,如果設(shè)計(jì)非常復(fù)雜,,最好還是用GUI builder),。BoxLayout能讓你在垂直和水平兩個(gè)方向上控制組件的擺放,它用一些被稱為支柱(struts)和膠水(glue)的東西來控制組件間的距離,。

BoxLayout的構(gòu)造函數(shù)同其它布局管理器稍有些不同——它的第一個(gè)參數(shù)是這個(gè)BoxLayout將要控制的Container,,第二個(gè)參數(shù)是布局的方向。

為了簡化起鑒,,Swing還提供了一種內(nèi)置BoxLayout的特殊容器,,Box(譯者注:這里的容器是指java.awt.Container類)。Box有兩個(gè)能創(chuàng)建水平和垂直對(duì)齊的box的static的方法,,下面我們就用它來排列組件,。

有了Box之后,再要往content pane里加組件的時(shí)候,,你就可以把它成第二的參數(shù)了,。

支柱(struts)會(huì)把組件隔開,它是大小是按象素算的,。用的時(shí)候,,只要把它當(dāng)作空格加在兩個(gè)組件的中間就行了。

與固定間距的struts不同,,glue(膠水)會(huì)盡可能地將組件隔開,。所以更準(zhǔn)確的說,,它應(yīng)該是"彈簧(spring)"而不是"膠水"(再加上這種設(shè)計(jì)是基于被稱為"springs and struts"的思想的,因此這個(gè)術(shù)語就顯得有些奇怪了),。

strut只控制一個(gè)方向上的距離,,而rigid area(剛性區(qū)域)則在兩個(gè)方向上固定組件間的距離。

應(yīng)該告訴你,,對(duì)rigid area的評(píng)價(jià)不是那么的統(tǒng)一,。實(shí)際上它就是絕對(duì)定位,因此有些人認(rèn)為,,它根本就是多余的,。

最佳方案?

Swing的功能非常強(qiáng)大,短短幾行代碼就能做很多事,。實(shí)際上你完全可以把這幾種布局結(jié)合起來,,完成一些比較復(fù)雜的任務(wù)。但是,,有些時(shí)候用手寫代碼創(chuàng)建GUI表單就不那么明智了;首先是太復(fù)雜,,其次也不值得,。Java和Swing的設(shè)計(jì)者們是想用語言和類庫去支持GUI builder工具,然后讓你用工具來簡化編程,。實(shí)際上只要知道布局是怎么一回事,,以及事件該怎么處理(下面講)就行了,懂不懂手寫代碼控制控件的擺放,,其實(shí)并不重要,。你完全可以選一個(gè)趁手的工具(畢竟Java的初衷就是想讓你提高編程的效率)。

Swing的事件模型

在Swing的事件模型中,,組件可以發(fā)起(或"射出")事件,。各種事件都是類。當(dāng)有事件發(fā)生時(shí),,一個(gè)或多個(gè)"監(jiān)聽器(listener)"會(huì)得到通知,,并做出反應(yīng)。這樣事件的來源就同它的處理程序分隔開來了,。一般說來,,程序員是不會(huì)去修改Swing組件的,他們寫的都是些事件處理程序,,當(dāng)組件收到事件時(shí),,會(huì)自動(dòng)調(diào)用這些代碼,因此Swing的事件模型可稱得上是將接口與實(shí)現(xiàn)分隔開來的絕好范例了,。

實(shí)際上事件監(jiān)聽器(event listener)就是一個(gè)實(shí)現(xiàn)listener接口的對(duì)象,。所以,,程序員要做的就是創(chuàng)建一個(gè)listener對(duì)象,然后向發(fā)起事件的組件注冊(cè)這個(gè)對(duì)象,。注冊(cè)的過程就是調(diào)用組件的addXXXListener()方法,,這里"XXX"表示組件所發(fā)起的事件的類型。只要看一眼"addListener"方法的名字就能知道組件能處理哪種事件了,,所以如果你讓它聽錯(cuò)了事件,,那么編譯就根本通不過。到后面你就會(huì)看到,,JavaBean在決定它能處理哪些事件時(shí),,也遵循"addListener"的命名規(guī)范。

事務(wù)邏輯都應(yīng)該封裝成listener,。創(chuàng)建listener的唯一的條件是,,它必須實(shí)現(xiàn)接口。你完全可以創(chuàng)建一個(gè)"全局的listener(global listener)",,但是內(nèi)部類或許更合適,。這么做不僅是因?yàn)橐鶕?jù)UI或事務(wù)邏輯對(duì)listener進(jìn)行邏輯分組,更重要的是(你很快就會(huì)看到),,要利用內(nèi)部類可以引用宿主類對(duì)象的特性,,這樣就能跨越類或子系統(tǒng)的邊界進(jìn)行調(diào)用了。

我們前面的例子里已經(jīng)涉及到Swing的事件模型了,,下面我們把這個(gè)模型的細(xì)節(jié)補(bǔ)充完整,。

事件與監(jiān)聽器種類

所有Swing組件都有addXXXListener( )和removeXXXListener( )方法,因此組件都能添加和刪除監(jiān)聽器,。你會(huì)發(fā)現(xiàn),,這里的"XXX"也正好是方法的參數(shù),例如addMyListener(MyListenerm),。下面這張表列出了基本的事件,,監(jiān)聽器和方法,以及與之相對(duì)應(yīng)的,,提供了addXXXListener( )和removeXXXListener( )方法的組件,。要知道,這個(gè)事件模型在設(shè)計(jì)時(shí)就強(qiáng)調(diào)了擴(kuò)展性,,因此你很可能會(huì)遇到這張表里沒有講到過的事件和監(jiān)聽器,。

事件,listener接口以及add和remove方法

支持這一事件的組件

ActionEvent ActionListener addActionListener( ) removeActionListener( )

JButton, JList, JTextField, JMenuItem 以及它們的派生類JCheckBoxMenuItem, JMenu,和JpopupMenu

AdjustmentEvent AdjustmentListener addAdjustmentListener( ) removeAdjustmentListener( )

JScrollbar以及實(shí)現(xiàn)Adjustable接口的組件

ComponentEvent ComponentListener addComponentListener( ) removeComponentListener( )

*Component及其派生類JButton, JCheckBox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, JFrame, JLabel, JList, JScrollbar, JTextArea,和JTextField

ContainerEvent ContainerListener addContainerListener( ) removeContainerListener( )

Container及其派生類JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog,和JFrame

FocusEvent FocusListener addFocusListener( ) removeFocusListener( )

Component及其"派生類(derivatives*)"

KeyEvent KeyListener addKeyListener( ) removeKeyListener( )

Component及其"派生類(derivatives*)"

MouseEvent(包括點(diǎn)擊和移動(dòng)) MouseListener addMouseListener( ) removeMouseListener( )

Component及其"派生類(derivatives*)"

MouseEvent[81](包括點(diǎn)擊和移動(dòng)) MouseMotionListener addMouseMotionListener( ) removeMouseMotionListener( )

Component及其"派生類(derivatives*)"

WindowEvent WindowListener addWindowListener( ) removeWindowListener( )

Window及其派生類JDialog, JFileDialog,和JFrame

ItemEvent ItemListener addItemListener( ) removeItemListener( )

JCheckBox, JCheckBoxMenuItem, JComboBox, JList, 以及實(shí)現(xiàn)了ItemSelectableinterface的組件

TextEvent TextListener addTextListener( ) removeTextListener( )

JTextComponent的派生類,,包括JTextArea和JTextField

一旦你知道了組件所支持的事件,,你就用不著再去查文檔了。你只要:
把event類的名字里的"Event"去掉,加上"Listener",,這就是你要實(shí)現(xiàn)的接口的名字了,。 實(shí)現(xiàn)上述接口,想捕捉哪個(gè)事件就實(shí)現(xiàn)它的接口,。比方說,,如果你對(duì)鼠標(biāo)的移動(dòng)感興趣,你可以去實(shí)現(xiàn)MouseMotionListener接口的mouseMoved( )方法,。(你必須實(shí)現(xiàn)這個(gè)接口的全套方法,,但是這種情況下,通常都會(huì)有捷徑,,過一會(huì)就會(huì)看到了,。) 創(chuàng)建一個(gè)listener的對(duì)象。在接口的名字前面加一個(gè)"add",,然后用這個(gè)方法向組件注冊(cè),。比如,addMouseMotionListener( ),。
下面是部分listener接口的方法:

Listener接口/Adapter

接口所定義的方法

ActionListener

actionPerformed(ActionEvent)

AdjustmentListener

adjustmentValueChanged(AdjustmentEvent)

ComponentListener ComponentAdapter

componentHidden(ComponentEvent) componentShown(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent)

ContainerListener ContainerAdapter

componentAdded(ContainerEvent) componentRemoved(ContainerEvent)

FocusListener FocusAdapter

focusGained(FocusEvent) focusLost(FocusEvent)

KeyListener KeyAdapter

keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent)

MouseListener MouseAdapter

mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent)

MouseMotionListener MouseMotionAdapter

mouseDragged(MouseEvent) mouseMoved(MouseEvent)

WindowListener WindowAdapter

windowOpened(WindowEvent) windowClosing(WindowEvent) windowClosed(WindowEvent) windowActivated(WindowEvent) windowDeactivated(WindowEvent) windowIconified(WindowEvent) windowDeiconified(WindowEvent)

ItemListener

itemStateChanged(ItemEvent)

之所以這張表不是很全,,部分是因?yàn)槭录P驮试S你創(chuàng)建自己的事件及相關(guān)的listener。所以你時(shí)常會(huì)碰到一些在事件類型方面自成體系的類庫,,而你在本章所學(xué)到的知識(shí)會(huì)幫助你學(xué)會(huì)使用這些事件,。

用listener的adapter簡化編程

可以看到上面那張表里的一些listener只有一個(gè)方法。實(shí)現(xiàn)這種接口的工作量并不大,,因?yàn)閷懲攴椒ǎ涌谝簿蛯?shí)現(xiàn)了,。但是如果你要使用有多個(gè)方法的listener的話,,事情就不那么輕松愉快了。

為了解決這個(gè)問題,,有些(但不是全部)多方法的listener接口提供了適配器(adapter),。從上面那張表已經(jīng)列出了它們的名字。適配器會(huì)為接口提供默認(rèn)的空方法,。這樣,,你只要繼承適配器,根據(jù)需要覆寫方法就可以了,。

adapter就是用來簡化listener的創(chuàng)建的,。

但是適配器這種東西也有缺點(diǎn)。

只是大小寫方面的一個(gè)疏漏,,它就成為一個(gè)新方法了,。還好,這還不是關(guān)閉窗口時(shí)會(huì)調(diào)用的方法,所以最壞的結(jié)果也只是不能實(shí)現(xiàn)預(yù)期的效果,。雖然有種種不方便,,但接口卻能保證讓你實(shí)現(xiàn)所有應(yīng)該實(shí)現(xiàn)的方法。

跟蹤多個(gè)事件

Swing組件的一覽表

現(xiàn)在你已經(jīng)知道布局管理器和事件模型了,,接下來就要學(xué)習(xí)怎樣使用Swing組件了,。這部分只是一個(gè)大致的介紹,我們講的都是常用的Swing組件及其特性,。

記?。?br>JDK文檔里有所有Swing組件的資料。 Swing事件的命名規(guī)范比較合理,,所以要猜該怎樣編寫和安裝事件處理程序也比較簡單,。用我們前面講的ShowAddListeners.java來檢查組件。 如果事情開始變得復(fù)雜了,,那么恭喜你畢業(yè)了,,該用GUI Builder了。
Button

Swing收錄了很多Button,,包括各種按鈕,,check box, radio button,,甚至連菜單項(xiàng)(menu item)都是繼承AbstractButton的(鑒于菜單項(xiàng)也牽涉進(jìn)來了,,(AbstractButton)可能還是叫"AbstractSelector"或其它什么名字更好一些)。

Button組

如果想讓radio button以"幾選一"的方式運(yùn)行(譯者注:原文為exclusive or,,字面的意思是"排他性的邏輯與"),,你就必須把他們加到一個(gè)"button組(button group)"里。但只要是AbstractButton,,都可以加進(jìn)ButtonGroup,。

這幾步給原本很簡單任務(wù)加了點(diǎn)難度。要讓button做到"幾選一",,你必須先創(chuàng)建一個(gè)button組,,然后把要加進(jìn)去的button加進(jìn)去。

Icon

Icon能用于JLabel和AbstractButton(包括JButton,,JCheckBox,,JRadioButton以及JMenuItem)。把Icon用于JLabel的語法非常簡單

你也可以使用你自己的gif文件,。如果想打開文件讀取圖像,,只要?jiǎng)?chuàng)建一個(gè)ImageIcon,并且把文件的名字傳給它就行了,。接下來,,你就可以在程序中使用這個(gè)Icon了。

很多Swing組件構(gòu)造函數(shù)都可以拿Icon作參數(shù),不過你也可以用setIcon( )方法添加或修改Icon,。

Tool tips

幾乎所有與GUI相關(guān)的類都繼承自JComponent,,而JComponent又包含了一個(gè)setToolText(String)方法。因此不管是哪種組件,,只要能放到表單上,,你幾乎都可以用(假設(shè)這是個(gè)JComponent的派生類對(duì)象jc)

jc.setToolTipText("Mytip");

來設(shè)置tool tip。這樣只要鼠標(biāo)停在JComponent上一段時(shí)間,,旁邊就會(huì)跳出一個(gè)提示框,,里面就是你設(shè)置的文本。

Text fields

Borders

JComponent里面有一個(gè)setBorder( )方法,,它能讓你為各種可視組件安上有趣的邊框,。

你還可以創(chuàng)建你自己的邊框,然后把它放到按鈕(button),,標(biāo)簽(label)或者其它控件里面,,——只要它是繼承JComponent的就行。

JScrollPanes

大多數(shù)時(shí)候,,你只需要JScrollPane能干好它的本職工作,,但是你也可以去控制它,告訴它顯示哪根滾動(dòng)條——垂直的,,水平的,,還是兩個(gè)都顯示或者兩個(gè)都不顯示。

通過給JScrollPane的構(gòu)造函數(shù)傳不同的參數(shù),,可以控制它的滾動(dòng)條,。

一個(gè)袖珍的編輯器

不用費(fèi)多大的勁,JTextPane已經(jīng)提供了很多編輯功能,。

applet的默認(rèn)布局是BorderLayout,。所以,如果你什么都不說,,直接往面板里面加組件,,那么它會(huì)填滿整個(gè)面板,。但是如果你指明其位置(NORTH,,SOUTH, EAST,,或WEST),,控件就會(huì)被釘在這個(gè)區(qū)域里。

注意JTextPane的內(nèi)置功能,,比如自動(dòng)換行,。此外它還有很多其他功能,具體詳情請(qǐng)參閱JDK文檔。

Check boxes

Check box能讓你做逐項(xiàng)的開/關(guān)選擇,。它由一個(gè)小框和一個(gè)標(biāo)簽組成,。一般來說,選中之后框里會(huì)有一個(gè)'x'(或者其它什么表示選中的標(biāo)記),,否則就是空的,。

一般來說,你用構(gòu)造函數(shù)創(chuàng)建JCheckBox的時(shí)候,,會(huì)傳給它一個(gè)用作標(biāo)簽的參數(shù),。JCheckBox創(chuàng)建完了之后,你還可以隨時(shí)讀取或設(shè)置它的狀態(tài),,或者讀取或重設(shè)它的標(biāo)簽,。

不管是選中還是清除,JCheckBox都會(huì)引發(fā)一個(gè)事件,。捕捉方法同按鈕事件完全相同:用ActionListener,。

Radio buttons

GUI編程里的radio button的概念來自于老式的汽車收音機(jī)里的機(jī)械式按鈕;當(dāng)你按下一個(gè)按鈕之后,,另一個(gè)就會(huì)彈出來,。因此你只能選一個(gè)。

要想創(chuàng)建一組相關(guān)聯(lián)的JRadioButton,,只要把它們加進(jìn)ButtonGroup就可以了(一個(gè)表單里允許有任意數(shù)量的ButtonGroup),。如果你(用構(gòu)造函數(shù)的第二個(gè)參數(shù))把多個(gè)radiobutton設(shè)成true了,那么只有最后一個(gè)才是有效的,。

注意,,捕捉radio button事件的方法同捕捉其它事件的完全相同。

text field它可以可以代替JLabel,。

Combo boxes (下拉式列表)

同radiobutton組一樣,,下拉式列表只允許用戶在一組選項(xiàng)里面選取一個(gè)元素。但是這種做法更簡潔,,而且能在不驚擾客戶的情況下修改列表中的元素,。(你也可以動(dòng)態(tài)地修改radio button,但是這個(gè)視覺效果就太奇怪了),。

JComboBox的默認(rèn)行為同Windows的combo box不太一樣,。在Windows里,你既可以從combo box的列表里選一個(gè),,也可以自己輸入,,但是在JComboBox里,這么做就必須先調(diào)用setEditable( )了,。此外你只能從列表中選擇一個(gè),。下面我們舉一個(gè)例子,。我們先給JComboBox加幾個(gè)選項(xiàng),然后每按一下按鈕就加一個(gè)選項(xiàng),。

List boxes

List box與JComboBox的不同不僅僅在外觀上,,它們之間有著非常重大的區(qū)別。JComboBox激活之后會(huì)彈出下拉框而JList則總是會(huì)在屏幕上占一塊固定大小,,永遠(yuǎn)也不會(huì)變,。如果你想列表里的東西,只要調(diào)用getSelectedValues( )就行了,,它會(huì)返回一個(gè)String的數(shù)組,,其中包含著被選中的元素。

JList能做多項(xiàng)選擇,;如果你control-click了一個(gè)以上的選項(xiàng)(在用鼠標(biāo)進(jìn)行選擇的時(shí)候,,一直按著"control"鍵),那么已選中的選項(xiàng)就會(huì)一直保持"高亮(highlighted)",,這樣你能選任意多個(gè)了,。如果你先選一項(xiàng),然后再shift-click另一個(gè),,那么兩個(gè)選項(xiàng)之間的所有選項(xiàng)就都被選中了,。要取消一個(gè)選項(xiàng)只要control-click就可以了。

如果你只是想把String數(shù)組放到JList里面,,那么還有一個(gè)更簡單的辦法,;就是把數(shù)組當(dāng)作參數(shù)傳給JList的構(gòu)造函數(shù),這樣它就會(huì)自動(dòng)創(chuàng)建一個(gè)列表了,。

JList不會(huì)自動(dòng)提供滾動(dòng)軸,。當(dāng)然只要把它嵌到JScrollPane里它就會(huì)自動(dòng)鑲上滾動(dòng)軸了,具體的細(xì)節(jié)它自會(huì)打理,。

Tabbed panes

JTabbedPane能創(chuàng)建"帶頁簽的對(duì)話框(tabbed dialog)",,也就是對(duì)話框的邊上有一個(gè)像文件夾的頁簽一樣的東西,你只要點(diǎn)擊這個(gè)頁簽,,對(duì)話框就把這一頁顯示出來,。

在Java編程當(dāng)中熟練使用頁簽面板(tabbed panel)相當(dāng)重要,因?yàn)槊慨?dāng)applet要彈出一個(gè)對(duì)話框的時(shí)候,,它都會(huì)自動(dòng)加上一段警告,,所以彈出式對(duì)話框在applet里并不受歡迎。

程序運(yùn)行起來你就會(huì)發(fā)現(xiàn),,如果tab太多了,,JTabbedPane還會(huì)自動(dòng)把它們堆起來以適應(yīng)一定的行寬,。

Message boxes

圖形界面系統(tǒng)通常都包含一套標(biāo)準(zhǔn)的,,能讓你迅速地將消息傳給用戶,,或者從用戶那里得到信息的對(duì)話框。對(duì)于Swing來說,,這些消息框就包含在JOptionPane里面了,。你有很多選擇(有些還相當(dāng)復(fù)雜),但是最常用的可能還是"確認(rèn)對(duì)話框(confirmation dialog)",,它用staticJOptionPane.showMessageDialog( )和JOptionPane.showConfirmDialog()啟動(dòng),。

注意showOptionDialog( )和showInputDialog( )會(huì)返回用戶輸入的信息。

菜單

所有能包含菜單的組件,,包括JApplet,,JFrame,JDialog以及它們所派生的組件,,都有一個(gè)需要JMenuBar作參數(shù)的setJMenuBar( )方法(一個(gè)組件只能有一個(gè)JMenuBar),。你可以把JMenu加入JMenuBar,再把JMenuItem加入JMenu,。每個(gè)JMenuItem都可以連一個(gè)ActionListener,,當(dāng)你選中菜單項(xiàng)(menu item)的時(shí)候,事件就發(fā)出了,。

與使用資源的系統(tǒng)不同,,Java和Swing要求你必須用源代碼來組裝菜單。

通常情況下每個(gè)JMenuItem都得有一個(gè)它自己的ActionListener,。

JMenuItem是AbstractButton的派生類,,所以它有一些類似按鈕的行為。JMenuItem本身就是一個(gè)可以置入下拉菜單的菜單選項(xiàng),。此外JMenuItem還有三個(gè)派生類:用來持有其它JMenuItem的JMenu(這樣你就可以做層疊式菜單了),;有一個(gè)能表示是否被選中的"候選標(biāo)記(checkmark)"的JCheckBoxMenuItem;以及包含一個(gè)radio button的JRadioButtonMenuItem,。

為了演示在程序運(yùn)行期間動(dòng)態(tài)地交換菜單條,,我們創(chuàng)建了兩個(gè)JMenuBar。你會(huì)發(fā)現(xiàn)JMenuBar是由JMenu組成的,,而JMenu又是由JMenuItem,,JCheckBoxMenuItem甚至JMenu(它會(huì)創(chuàng)建子菜單)組成的,。等JMenuBar組裝完畢,,你就可以用setJMenuBar( )方法把它安裝到當(dāng)前程序里面了。注意當(dāng)你按下鈕按的時(shí)候,,它會(huì)用getJMenuBar()來判斷當(dāng)前的菜單,,然后把另一菜單換上去。

字符串的比較是引發(fā)編程錯(cuò)誤的一大誘因,。

程序會(huì)自動(dòng)的將菜單項(xiàng)勾掉或恢復(fù),。JCheckBoxMenuItem的代碼演示了兩種怎樣決定該勾掉哪個(gè)菜單項(xiàng)的方法,。一個(gè)是匹配字符串(雖然也管用,但就像上面我們所講的,,不是很安全),,另一個(gè)則是匹配事件目標(biāo)對(duì)象。正如你所看到的,,getState( )方法可以返回JCheckBoxMenuItem的狀態(tài),,而setState( )則可以設(shè)置它的狀態(tài)。

菜單事件不是很一致,,這一點(diǎn)可能會(huì)引起混亂,。JMenuItem使用ActionListener事件,而JCheckBoxMenuItem則使用ItemListener事件,。JMenu也支持ActionListener,,但通常沒什么用??傊?,你得為每個(gè)JMenuItem,JCheckBoxMenuItem或JRadioButtonMenuItem都制備一個(gè)listener,,不過這里我們偷了點(diǎn)懶,,把一個(gè)ItemListener和ActionListener連到多個(gè)菜單組件上了。

Swing支持助記符,,或者說"快捷鍵",,這樣你就可以扔掉鼠標(biāo)用鍵盤來選取AbstractButton了(按鈕,菜單項(xiàng)等),。要這么做很容易,,就拿JMenuItem舉例,你可以用它重載了的構(gòu)造函數(shù),,把快捷鍵的標(biāo)識(shí)符當(dāng)作第二個(gè)參數(shù)傳給它,。不過絕大多數(shù)AbstractButton都沒有提供類似的構(gòu)造函數(shù),所以比較通用的辦法還是用setMnemonic()方法,。在上述例程中我們?yōu)榘粹o和多個(gè)菜單項(xiàng)加上了快捷鍵,,這些快捷鍵的提示會(huì)自動(dòng)顯示在組件上。

好工具應(yīng)該能幫你維護(hù)好菜單,。

彈出式菜單

要想實(shí)現(xiàn)JPopupMenu,,最直截了當(dāng)?shù)霓k法就是創(chuàng)建一個(gè)繼承MouseAdapter的內(nèi)部類,然后把這內(nèi)部類的實(shí)例加到要提供彈出式菜單的組件里:

JMenuItem用的是同一個(gè)ActionListener,,它負(fù)責(zé)從菜單標(biāo)簽里面提文本并且把它插入JTextField,。

畫屏幕

一個(gè)好的GUI框架能讓作圖相對(duì)而言比較簡單——確實(shí)如此,Swing就做到了,。所有作圖問題都面臨同一個(gè)難點(diǎn),,那就是相比調(diào)用畫圖函數(shù),,計(jì)算該在哪里畫東西通常會(huì)更棘手,但不幸的是這種計(jì)算又常常和作圖函數(shù)的調(diào)用混在一起,,所以作圖函數(shù)的接口的實(shí)際的復(fù)雜程度很可能會(huì)比你認(rèn)為的要簡單。

雖然你可以在任何一個(gè)JComponent上作畫,,也就是說它們都能充當(dāng)畫布(canvas),,但是如果你想要一塊能直接畫東西的白板,最好還是創(chuàng)建一個(gè)繼承JPanel的類,。這樣你只需要覆寫一個(gè)方法,,也就是paintComponent( )就行了。當(dāng)系統(tǒng)需要重畫組件的時(shí)候,,會(huì)自動(dòng)調(diào)用這個(gè)方法(通常情況下,,你不必為此操心,因?yàn)檫@是由Swing控制的),。調(diào)用的時(shí)候,,Swing會(huì)傳一個(gè)Graphics對(duì)象給這個(gè)方法,這樣你就能用這個(gè)對(duì)象作畫了,。

覆寫paintComponent( )的時(shí)候,,必須首先調(diào)用其基類的同名方法。接下來再做什么就由你了決定了,;通常是調(diào)用Graphics的方法在JPanel上畫畫或者是在其象素上著色,。要想了解具體怎樣使用這些方法,可以查閱java.awt.Graphics文檔(可以到j(luò)ava.上面去找JDK文檔),。

如果問題非常復(fù)雜,,那么還有一些更復(fù)雜的解決方案,比如第三方的JavaBean或者Java 2D API,。

對(duì)話框

所謂對(duì)話框是指,,能從其他窗口里面彈出來的窗口。其作用是,,在不搞亂原先窗口前提下,,具體處理其某一部分的細(xì)節(jié)問題。對(duì)話框在GUI編程中的用途很廣,,但是在applet中用的不多,。

要想創(chuàng)建對(duì)話框,你得先繼承JDialog,。和JFrame一樣,,JDialog也是另一種Window,它也有布局管理器(默認(rèn)情況下是BorderLayout),,也可以用事件監(jiān)聽器來處理事件,。它同JFrame有一個(gè)重要的區(qū)別,,那就是在關(guān)對(duì)話框的時(shí)候別把程序也關(guān)了。相反你得用dispose( )方法將對(duì)話框窗口占用的資源全部釋放出來,。

一旦創(chuàng)建完JDialog,,你就得用show( )來顯示和激活它了。關(guān)閉對(duì)話框的時(shí)候,,還得記住要dispose( ),。

你會(huì)發(fā)現(xiàn),對(duì)applet來說,,包括對(duì)話框在內(nèi)所有彈出的東西都是"不可信任的",。也就是說彈出來的窗口里面有一個(gè)警告。這是因?yàn)?,從理論上講惡意代碼可以利用這個(gè)功能來愚弄用戶,,讓他們覺得自己是在運(yùn)行一個(gè)本地應(yīng)用程序,然后誤導(dǎo)它們輸入自己的信用卡號(hào)碼,,再通過Web傳出去,。applet總是和網(wǎng)頁聯(lián)在一起,因此只能用瀏覽器運(yùn)行,,但是對(duì)話框卻可以脫離網(wǎng)頁,,所以從理論上講這種欺騙手段是成立的。所以這么一來,,applet就不太會(huì)用到對(duì)話框了,。

由于static只能用于宿主類,因此內(nèi)部類里不能再有static的數(shù)據(jù)或是嵌套類了,。

paintComponent( )負(fù)責(zé)把panel的周圍的方框以及"x"或"o"畫出來,。雖然充斥著單調(diào)的計(jì)算,但是還算簡明,。

文件對(duì)話框

有些操作系統(tǒng)還內(nèi)置了一些特殊的對(duì)話框,,比如讓你選擇字體,顏色,,打印機(jī)之類的對(duì)話框,。實(shí)際上所有的圖形操作系統(tǒng)都提供了打開和存儲(chǔ)文件的對(duì)話框,所以為了簡化起鑒,,Java把它們都封裝到JFileChooser里面了,。

注意JFileChooser有很多變例可供選擇,比方說加一個(gè)過濾器濾文件名之類的,。

要用"open file"對(duì)話框就調(diào)用showOpenDialog( ),,要用"save file"對(duì)話框,就調(diào)用showSaveDialog()。在對(duì)話框關(guān)閉之前,,這兩個(gè)函數(shù)是不會(huì)返回的,。即便對(duì)話框關(guān)了,JFileChooser對(duì)象仍然還在,,所以你還去讀它的數(shù)據(jù),。要想知道操作的結(jié)果,可以用getSelectedFile( )和getCurrentDirectory( ),。如果返回null則說明用戶按了cancel,。

Swing組件上的HTML

所有能顯示文件的組件都可以按照HTML的規(guī)則顯示HTML的文本。也就是說你可以很方便地讓Swing組件顯示很炫的文本,。比如:

文本必須以"<html>"開頭,,下面你就可以用普通的HTML標(biāo)記了,。注意,,它沒有強(qiáng)制你一定要關(guān)閉標(biāo)記。

JTabbedPane,,JMenuItem,,JToolTip,JRadioButton以及JCheckBox都支持HTML文本,。

Slider和進(jìn)程條

Slider能讓用戶通過來回移動(dòng)一個(gè)點(diǎn)來輸入數(shù)據(jù),,有時(shí)這種做法還是很直觀的(比方說調(diào)節(jié)音量)。進(jìn)程條(progress bar)則以一種用類比的方式顯示數(shù)據(jù),,它表示數(shù)據(jù)是"全部"還是"空的",,這樣用戶就能有一個(gè)比較全面的了解了。

JProgressBar還比較簡單,,而JSlider的選項(xiàng)就比較多了,,比如擺放的方向,大小刻度等等,。注意一下給Slider加帶抬頭的邊框的那行代碼,,多簡潔。



JTree的用法可以簡單到只有下面這行代碼:

add(new JTree(new Object[] {"this","that", "other"}));

這樣顯示的是一棵最基本的樹,。JTree的API非常龐大,,應(yīng)該是Swing類庫里最大的之一了。雖然你可以用它來做任何事情,,但是要想完成比較復(fù)雜任務(wù),,就需要一定的研究和實(shí)驗(yàn)了。

好在這個(gè)類庫還提供了變通手段,,也就是一個(gè)"默認(rèn)"的,,能滿足一般需求的樹型組件。所以絕大多數(shù)情況下你都可以使用這個(gè)組件,只有在特殊情況下,,你才需要去深入研究樹,。

Trees類包含一個(gè)用來創(chuàng)建多個(gè)Branch的兩維String數(shù)組,以及一個(gè)用來給數(shù)組定位的static int i,。節(jié)點(diǎn)放在DefaultMutableTreeNode里面,,但是實(shí)際在屏幕上顯示則是由JTree及與之相的model——DefaultTreeModel控制的。注意JTree在加入applet之前,,先套了一件JScrollPane,,這樣它就能提供自動(dòng)的滾動(dòng)軸了。

對(duì)JTree的操控是經(jīng)由它的model來實(shí)現(xiàn)的,。當(dāng)model發(fā)生變化時(shí),,它會(huì)產(chǎn)生一個(gè)事件,讓JTree對(duì)樹的顯示作出必要的更新,。init( )用getModel( )提取這個(gè)model,。當(dāng)你按下按鈕的時(shí)候,它會(huì)創(chuàng)建一個(gè)新的新的"branch" ,。等它找到當(dāng)前選中的那個(gè)節(jié)點(diǎn)(如果什么也沒選,,就用根節(jié)點(diǎn))之后,model的insertNodeInto()方法就會(huì)接管所有的任務(wù)了,,包括修改樹,,刷新顯示等等。

或許上述例程已能滿足你的需求了,。但是樹的功能強(qiáng)大到只要你能想到它就能做到的地步,。但是要知道:差不多每個(gè)類都有一個(gè)非常龐大的接口,所以你要花很多時(shí)間和精力去理解它的內(nèi)部構(gòu)造,。但話說回來,,它的設(shè)計(jì)還是很優(yōu)秀的,其競爭者往往更糟,。

表格

和樹一樣,,Swing的表格控件也非常復(fù)雜強(qiáng)大。剛開始的時(shí)候,,他們是想把它做成用JDBC連接數(shù)據(jù)庫時(shí)常用的"grid"接口,,因此它具有極高的靈活性,不過代價(jià)就是復(fù)雜度了,。它能讓你輕易創(chuàng)建一個(gè)全功能的電子表格程序,,不過這要花整整一本書篇幅才能講清楚。但是如果你弄懂了基本原理,,也可以用它來創(chuàng)建一個(gè)相對(duì)簡單的JTable,。

JTable只負(fù)責(zé)怎樣顯示數(shù)據(jù),,而數(shù)據(jù)本身是由TableModel控制的。所以在創(chuàng)建JTable之前,,你通常都得先創(chuàng)建一個(gè)TableModel,。你可以從頭開始去實(shí)現(xiàn)TableModel接口,但是Java提供了一個(gè)AbstractTableModel的幫助類,,繼承它會(huì)比較簡單,。

選擇Look & Feel

所謂"可插接式的外觀風(fēng)格(look &feel)"是指,你可以讓程序模擬其他操作環(huán)境的外觀,。你甚至可以做一些更炫的事情,,比如在程序運(yùn)行期間動(dòng)態(tài)改變其外觀。但是通常情況下,,你只會(huì)在下面兩項(xiàng)中選一個(gè):選擇"跨平臺(tái)"的外觀(也就是Swing的"metal"),,或者選當(dāng)前操作系統(tǒng)的外觀,讓Java程序看上去就像是為這個(gè)操作系統(tǒng)定制的(絕大多數(shù)情況下,,這幾乎是勿庸置疑的選擇,,這樣用戶就不至于被搞糊涂了)。不管你選哪個(gè),,代碼都很簡單,,但是必須先執(zhí)行這些代碼再創(chuàng)建組件,,因?yàn)榻M件是按照當(dāng)前的lookand feel創(chuàng)建的,,而且程序運(yùn)行到一半的時(shí)候,你再去改look and feel,,它就不會(huì)跟著你去改了,。(這個(gè)過程非常復(fù)雜,而且并不實(shí)用,,所以我們把它留給Swing專著了),。

實(shí)際上如果你認(rèn)為跨平臺(tái)的("metal")外觀是Swing程序的特色,而你也想用它,,那你就可以什么都不作了——它是默認(rèn)的look and feel,。但是如果你選擇當(dāng)前操作系統(tǒng)的外觀風(fēng)格,那么只要插入下面這段代碼就可以了,,一般來說是放在main( )開頭的地方,,但是最晚要在加第一個(gè)組件之前:

try {

 UIManager.setLookAndFeel(UIManager.

   getSystemLookAndFeelClassName());

} catch(Exception e) {

  throw newRuntimeException(e);

}

你根本不用在catch里面做任何事,因?yàn)槿绻x擇其他look and feel失敗的話,,UIManager會(huì)回到默認(rèn)的跨平臺(tái)的look and feel,。但是在調(diào)試的時(shí)候這個(gè)異常還是很起作用的,最起碼你可以從catch里看到些什么,。

下面是一個(gè)用命令行參數(shù)來選擇look and feel的程序,,順便也看看這幾個(gè)組件在不同的look and feel下都是什么樣子:

假如你為一個(gè)對(duì)程序外觀有特殊要求的公司做一個(gè)framework的話,你甚至可以自創(chuàng)一套look and feel。不過這可是一個(gè)大工程,,其難度遠(yuǎn)遠(yuǎn)超出了本書的范圍,。

剪貼板

JFC與系統(tǒng)剪貼板的互動(dòng)功能非常有限(在java.awt.datatransfer package里面)。你可以把String對(duì)象當(dāng)作文本復(fù)制到剪貼板里,,也可以把剪貼板里的文本粘貼到String對(duì)象里,。當(dāng)然剪貼板支持任何類型的數(shù)據(jù),至于數(shù)據(jù)在剪貼板里該怎么表示,,那是粘貼數(shù)據(jù)的程序的事,。Java通過"flavor"這個(gè)概念加強(qiáng)了剪貼板API的擴(kuò)展性。當(dāng)數(shù)據(jù)進(jìn)到剪貼板的時(shí)候還跟著一組與這個(gè)數(shù)據(jù)相關(guān)聯(lián)的,,可以轉(zhuǎn)換這些數(shù)據(jù)的flavor(比方說一幅畫可以表示成一個(gè)全部有數(shù)字組成的字符串或一個(gè)image),,這樣你就能知道剪貼板里的數(shù)據(jù)是否支持你感興趣的flavor了。

JTextField和JTextArea它們?cè)揪鸵呀?jīng)支持剪貼板了,。

可以期待,,未來Java會(huì)提供更多的flavor。你能得到更多的數(shù)據(jù)flavor的支持,。

將applet打成JAR卷宗

JAR的一個(gè)主要用途就是優(yōu)化applet的裝載,。在Java 1.0時(shí)代,程序員們都盡量把a(bǔ)pplet的代碼塞進(jìn)一個(gè)類里,,這樣當(dāng)用戶下載applet的時(shí)候只需向服務(wù)器發(fā)一次請(qǐng)求就可以了,。但這么做不僅使代碼變得非常難讀(也難維護(hù)),而且.class文件也是未經(jīng)壓縮的,,因此下載速度仍有提升的潛力,。

JAR解決了這個(gè)問題,它把所有的.class文件全都?jí)嚎s在一個(gè)供瀏覽器下載的文件里?,F(xiàn)在你可以大膽運(yùn)用正確的設(shè)計(jì)方案而不用再擔(dān)心它會(huì)產(chǎn)生多少.class文件了,,而用戶的下載速度也更快了。

簽發(fā)applet

由于沙箱安全模型的限制,,未獲簽名的applet是不能在客戶端進(jìn)行某些操作的,,比如寫文件,連接本地網(wǎng)絡(luò)等,。一旦你簽發(fā)了applet,,用戶就可以去核對(duì)那個(gè)自稱創(chuàng)建了這個(gè)applet的人是不是真的就是創(chuàng)建者了,同時(shí)他們也可以確認(rèn)JAR文件是不是在離開服務(wù)器之后被篡改了,。沒有這些最起碼的保證,,applet是根本不可能去做任何可能損壞計(jì)算機(jī)或泄漏個(gè)人隱私的事的。這層限制對(duì)于applet在Internet上的安全運(yùn)用是至關(guān)重要的,,但同時(shí)也削弱了applet的功能,。

自從有了Java Plugin,,簽發(fā)applet的步驟也變得更簡單也更標(biāo)準(zhǔn)化了,而applet也成為一種更簡便的部署應(yīng)用程序的方法了,。簽發(fā)applet已經(jīng)變得非常簡單了,,而且也有了標(biāo)準(zhǔn)的Java工具了。

早先plugin還沒出來的時(shí)候,,你得用Netscape的工具為Netscape的用戶簽署.jar文件,,用Microsoft的工具為Internet Explorer用戶簽署.cab文件,然后在HTML文件里面為兩個(gè)平臺(tái)各自準(zhǔn)備一套標(biāo)記,。而用戶也必須在瀏覽器里安裝證書,,這樣applet才能獲得信任,。

Plugin不僅提供了標(biāo)準(zhǔn)化的簽署和部署applet的方法,而且能自動(dòng)安裝證書,方便了用戶,。

} ///:~

要想簽名,,你必須先把它做成一個(gè)JAR文件(見本章前面講過的jar工具這一節(jié)),,然后再簽署這個(gè)文件,。

有了JAR文件,你就得用證書或是密鑰來簽名了,。如果是一個(gè)大公司,,那么你可以跟Verisign或Thawte這樣的"認(rèn)證中心(signingauthority)"提申請(qǐng),它們會(huì)給你發(fā)給你證書的,。證書是用來給代碼簽名的,,這樣用戶就能確信你確實(shí)是他所下載的這段代碼的提供者了,而且自你簽發(fā)之后,,這段代碼未被篡改過,。電子簽名的本質(zhì)是一串兩進(jìn)制的數(shù),,當(dāng)有人要核對(duì)簽名的時(shí)候,,那個(gè)給你發(fā)證書的認(rèn)證中心會(huì)為你作證。

認(rèn)證中心發(fā)的證書是要付錢的,,而且得定期更新,。就這個(gè)問題而言,我們可以自己給自己簽一張證書,。這個(gè)證書會(huì)存在文件里(通常被稱為keychain),。你可以用下面這條命令:

keytool –list

訪問默認(rèn)的文件。如果默認(rèn)的文件不存在,,那么你還得先建一個(gè),,或者告訴它去找哪個(gè)文件?;蛟S你應(yīng)該去試試"cacerts"文件,。

keytool -list -file <path/filename>

其默認(rèn)路徑通常是

{java.home}/lib/security/cacerts

其中,,java.home表示JRE所在的目錄。

你也可以用keytool給自己發(fā)一份證書,,供測試用,。如果PATH環(huán)境變量里已經(jīng)有Java的"bin"目錄了,那么這個(gè)命令就是:

keytool –genkey –alias <keyname> -keystore<url>

其中keyname表示key的別名,,比如“mykeyname”,,url表示存放密鑰的位置,通常就放在上面講的cacerts文件里,。

它會(huì)提示你輸入(keystore的)密碼,。默認(rèn)是"changeit"(提醒你該做些什么)。然后是姓名,,部門,,單位,城市,,州,,國家。這些信息會(huì)被放進(jìn)證書里,。最后它會(huì)要你給證書設(shè)一個(gè)密碼,。如果你對(duì)安全問題真的很在意,可以給它設(shè)一個(gè)單獨(dú)的密碼,,默認(rèn)情況下,,證書的密碼就是"存證書的文件(keystore)"的密碼,一般來說這已經(jīng)夠了,。上面這些信息還可以用命令行提供給像Ant這樣的編譯工具使用,。

如果你不給參數(shù),直接在命令行下用keytool命令,,那么它會(huì)把所有的選項(xiàng)全部都打印出來,。你或許想用-valid 選項(xiàng),看看證書的有效期還有多長,。

如果想確認(rèn)證書確實(shí)保存在cacerts文件里,,用

keytool –list –keystore <url>

然后輸入前面設(shè)的密碼?;蛟S你的證書和別人的存放在一起(如果別人已經(jīng)在這個(gè)keystore里存了證書的話),。

你剛獲得的那張證書是你自己簽發(fā)的,所以認(rèn)證中心是不會(huì)認(rèn)帳的,。如果你用這張證書簽發(fā)JAR文件,,最終用戶那里就會(huì)看到一個(gè)警告窗口,同時(shí)強(qiáng)烈建議他們不要使用這個(gè)程序,。除非你去買一份有效力的證書,,否則否則你和你的用戶就得忍著,。

簽發(fā)JAR文件要用Java的jarsigner標(biāo)準(zhǔn)工具,命令如下:

jarsigner –keystore <url> <jarfile><keyname>

url表示cacerts文件的位置,,jarfile表示JAR文件的名字,,而keyname則是證書的別名。你還得再輸一遍密碼,。

現(xiàn)在這個(gè)JAR文件就帶上你的這張證書的簽名了,,而用戶也能知道它在簽發(fā)之后是不是被篡改了(包括修改,添加或刪除等),。

接下來你得操心一下HTML文件的applet標(biāo)記的"archive"屬性了,,JAR的文件名就在這里。

如果瀏覽器用的是Java的plugin,,applet的標(biāo)記還要更復(fù)雜一些,,不過你可以創(chuàng)建一個(gè)簡單點(diǎn)的,就像這樣:

<APPLET

  CODE=package.AppletSubclass.class

  ARCHIVE =myjar.jar

  WIDTH=300

  HEIGHT=200>

</APPLET>

然后用HTMLConverter過一遍,,它會(huì)自動(dòng)幫你生成正確的applet標(biāo)記,。

現(xiàn)在當(dāng)用戶下載applet時(shí),瀏覽器就會(huì)提醒他們現(xiàn)在正在裝載的是一個(gè)帶簽名的applet,,并且問他是不是信任這個(gè)簽發(fā)者,。正如我們前面所講的,測試用的證書并不具備很高的可信度,,因此它會(huì)給一個(gè)警告,。如果客戶信任了,applet就能訪問整個(gè)客戶系統(tǒng)了,,于是它就和普通的程序沒什么兩樣了,。

JNLP和JavaWeb Start

雖然經(jīng)過簽名的applet功能強(qiáng)大,甚至能在有效地取代應(yīng)用程序,,但它還是得在Web瀏覽器上運(yùn)行,。這不僅使客戶端增加了額外的運(yùn)行瀏覽器的開銷,而且常常使用戶界面變得非常的單調(diào)和混亂,。瀏覽器有它自己的菜單和工具條,,而他們正好壓在applet的上面。

Java的網(wǎng)絡(luò)啟動(dòng)協(xié)議(Java Network LaunchProtocol簡稱JNLP)能在不犧牲applet優(yōu)點(diǎn)的前提下解決這個(gè)問題,。你可以在客戶端上下載并安裝單獨(dú)的JNLP應(yīng)用程序。它可以用命令行,,桌面圖標(biāo),,或隨JNLP一同分發(fā)的應(yīng)用程序管理器啟動(dòng)。程序甚至可以從最初下載的那個(gè)網(wǎng)站上啟動(dòng),。

JNLP程序運(yùn)行的時(shí)候會(huì)動(dòng)態(tài)地從Internet上下載資源,,并且自動(dòng)檢查其版本(如果用戶連著Internet的話),。也就是說它同時(shí)具備applet和application的優(yōu)點(diǎn)。

和applet一樣,,客戶機(jī)在對(duì)待JNLP應(yīng)用程序的時(shí)候也必須注意安全問題,。JNLP應(yīng)用程序是一種易于下載的,基于Web的應(yīng)用程序,,因此有可能會(huì)被惡意利用,。有鑒于此,JNLP應(yīng)用程序應(yīng)該和applet一樣被放在沙箱里,。同applet一樣,,它可以用帶簽名的JAR文件部署,這時(shí)用戶可以選擇是不是信任簽發(fā)者,。和applet的不同之處在于,,即便沒有簽名,它仍然可以通過JNLP API去訪問客戶系統(tǒng)的某些資源(這就需要用戶在程序運(yùn)行時(shí)認(rèn)可這些請(qǐng)求了),。

JNLP是一個(gè)協(xié)議而非產(chǎn)品,,因而得先把它實(shí)現(xiàn)了才能用。Java Web Start有稱JAWS就是Sun提供的,,能免費(fèi)下載的,,JNLP的官方樣板實(shí)現(xiàn)。你只要下載安裝就行了,,如果要做開發(fā),,不要忘了把JAR文件放到classpath里面。要想在網(wǎng)站上部署JNLP應(yīng)用程序,,只要確保服務(wù)器能認(rèn)得application/x-java-jnlp-file的MIME類型就行了,。如果是用最新版的Tomcat服務(wù)器(http://jakarta./tomcat),那它應(yīng)該已經(jīng)幫你配置好了,。否則就去查查服務(wù)器的用戶手冊(cè),。

創(chuàng)建JNLP應(yīng)用程序并不難。先創(chuàng)建一個(gè)標(biāo)準(zhǔn)的應(yīng)用程序,,然后用JAR打包,,最后再準(zhǔn)備一個(gè)啟動(dòng)文件就行了。啟動(dòng)文件是一個(gè)很簡單的XML文件,,它負(fù)責(zé)向客戶端傳遞下載和安裝應(yīng)用程序的信息,。如果你決定用不帶簽名的JAR文件來部署軟件,那還得用JNLP API來訪問客戶端系統(tǒng)上的資源,。

注意,,F(xiàn)ileOpenService和FileCloseService是javax.jnlp里的類,要使用這兩個(gè)服務(wù),,不但要用ServiceManager.lookup()提出請(qǐng)求,,而且要用這個(gè)方法所返回的對(duì)象來訪問客戶端資源,。如果你不想受JNLP束縛,要直接使用這些類的話,,那就必須使用簽名的JAR文件,。

這個(gè)啟動(dòng)文件的后綴名必須是.jnlp,此外它還必須和JAR文件呆在一個(gè)目錄里,。

這是一個(gè)根節(jié)點(diǎn)為<jnlp>標(biāo)記的XML文件,。這個(gè)節(jié)點(diǎn)下面還包括了一些子元素,其中絕大部分是自解釋的,。

jnlp元素的spec屬性告訴客戶端系統(tǒng),,這個(gè)應(yīng)用程序需要哪個(gè)版本的JNLP。codebase屬性告訴客戶端到哪個(gè)目錄去找啟動(dòng)文件和資源,。通常它應(yīng)該是一個(gè)指向Web服務(wù)器的HTTP URL,,但這里為了測試需要,我們把它指到本機(jī)的目錄了,。href屬性表示文件的名字,。

information標(biāo)記里有多個(gè)提供與程序相關(guān)的信息的子元素。它們是供Java WebStart的管理控制臺(tái)或其它類似程序使用的,。這些程序會(huì)把JNLP應(yīng)用安裝到客戶端上,,讓后讓用戶通過命令行,快捷方式或者其它什么方法啟動(dòng),。

resource標(biāo)記的功能HTML文件里的applet標(biāo)記相似,。j2se子元素指明程序運(yùn)行所需的j2se的版本,jar子元素告訴客戶端class文件被打在哪個(gè)JAR文件里,。此外jar元素還有一個(gè)download屬性,,其值可以是"eager"或"lazy",它的作用是告訴JNLP是不是應(yīng)該下載完這個(gè)jar再開始運(yùn)行程序,。

application-desc屬性告訴客戶端系統(tǒng),,可執(zhí)行的class,也就是JAR文件的入口是哪個(gè)類,。

jnlp標(biāo)記還有一個(gè)很有用的子元素,,那就是這里沒用到的security標(biāo)記。下面我們來看看security標(biāo)記長什么樣子:

<security>

  <all-permissions/>

<security/>

只有在部署帶簽名的JAR文件時(shí)才能使用security標(biāo)記,。上面那段程序不需要這個(gè)標(biāo)記,,因?yàn)樗械谋镜刭Y源都是通過JNLP服務(wù)來訪問的。

此外還有一些其它標(biāo)記,,具體細(xì)節(jié)可以參考http://java./products/javawebstart/download-spec.htm

現(xiàn)在.jnlp文件也寫好了,,接下來就是在網(wǎng)頁里加超鏈接了。這個(gè)頁面應(yīng)該是個(gè)下載頁面,。頁面上除了有復(fù)雜的格式和詳細(xì)介之外,,千萬別忘了把這條加上:

<a href="classname.jnlp">clickhere</a>

這樣你就可以點(diǎn)擊鏈接啟動(dòng)JNLP應(yīng)用程序的安裝進(jìn)程了。你只要下載一次,,以后就可以通過管理控制臺(tái)來進(jìn)行配置了,。如果你用的是Windows的Java Web Start的話,那么第二次啟動(dòng)程序的時(shí)候,,它會(huì)提示你,,是不是創(chuàng)建一個(gè)快捷方式。這種東西是可以配置的,。

我們這里只介紹了兩個(gè)JNLP服務(wù),,而當(dāng)前版本里有七種。它們都是為特定的任務(wù)所設(shè)計(jì)的,,比如像打印,,剪貼板操作等。

編程技巧

由于Java的GUI編程是一個(gè)還在不斷改進(jìn)的技術(shù),,Java 1.0/1.1與Java 2的Swing類庫之間就有著非常重大的區(qū)別,,與舊模式相比,Swing能讓你用一種更好的方式編程,。這里,,我們會(huì)就其中一些問題做個(gè)介紹,同時(shí)檢驗(yàn)一下這些編程技巧,。

動(dòng)態(tài)綁定事件

Swing的事件模型的優(yōu)點(diǎn)就在于它的靈活性,。你可以調(diào)用方法給組件添加或刪除事件。
Button可以連不止一個(gè)listener,。通常組件是以多播(multicast)方式處理事件的,,也就是說你可以為一個(gè)事件注冊(cè)多個(gè)listener。但是對(duì)于一些特殊的,,以單播(unicast)方式處理事件的組件,,這么做就會(huì)引發(fā)TooManyListenersException了。 程序運(yùn)行的時(shí)候能動(dòng)態(tài)地往Button b2上面添加或刪除listener,。你應(yīng)該已經(jīng)知道加listener的方法了,,此外每個(gè)組件還有一個(gè)能用來刪listener的removeXXXListener( )方法。
這種靈活性為你帶來更大的便利

值得注意的是,,listener的添加順序并不一定就是它們的調(diào)用順序(雖然絕大多數(shù)JVM確實(shí)是這么實(shí)現(xiàn)的),。

將業(yè)務(wù)邏輯(business logic)與用戶界面分離開來

一般情況下,設(shè)計(jì)類的時(shí)候總是強(qiáng)調(diào)一個(gè)類"只作一件事情",。涉及用戶界面的時(shí)候更是如此,,因?yàn)槟愫芸赡軙?huì)把"要作什么"同"要怎樣顯示"給混在一起了。這種耦合嚴(yán)重妨礙了代碼的復(fù)用。比較好的做法是將"業(yè)務(wù)邏輯(business login)"同GUI分離開來,。這樣不僅方便了業(yè)務(wù)邏輯代碼的復(fù)用,,也簡化了GUI的復(fù)用。

還有一種情況,,就是多層系統(tǒng)(multitiered systems),,也就是說”業(yè)務(wù)對(duì)象(business object)"完全貯存在另一臺(tái)機(jī)器上。業(yè)務(wù)規(guī)則的集中管理能使規(guī)則的更新立即對(duì)新交易生效,,因此這是這類系統(tǒng)所追求的目標(biāo),。但是很多應(yīng)用程序都會(huì)用到這些業(yè)務(wù)對(duì)象,所以它們絕不能同特定的顯示模式連在一起,。它們應(yīng)該只做業(yè)務(wù)處理,,別的什么都不管。

樹立了將UI同業(yè)務(wù)邏輯相分離的觀點(diǎn)之后,,當(dāng)你再碰到用Java去維護(hù)遺留下來的老代碼時(shí),,也能稍微輕松一點(diǎn)。

范式

內(nèi)部類,,Swing事件模型,,還能繼續(xù)用下去的AWT事件模型,以及那些要我們用老辦法用的新類庫的功能,,所有這些都使程序設(shè)計(jì)變得更混亂了?,F(xiàn)在就連大家寫亂七八糟的代碼的方式也變得五花八門了。

這些情況都是事實(shí),,但是你應(yīng)該總是使用最簡單也最有條理的解決方案:用Listener(通常要寫成內(nèi)部類)來處理事件,。

用了這個(gè)模型,你可以少寫很多"讓我想想這個(gè)事件是誰發(fā)出的"這種代碼,。所有代碼都在解決問題,,而不是在做類型檢查。這是最佳的編程風(fēng)格,,寫出來的代碼不僅便于總結(jié),,可讀性和可維護(hù)性也高。

并發(fā)與Swing

寫Swing程序的時(shí)候,,你很可能會(huì)忘了它還正用著線程,。雖然你并沒有明確地創(chuàng)建過Thread對(duì)象,但它所引發(fā)的問題卻會(huì)乘你不備嚇你一跳,。絕大多數(shù)情況下,,你寫的Swing或其他帶窗口顯示的GUI程序都是事件驅(qū)動(dòng)的,而且除非用戶用鼠標(biāo)或鍵盤點(diǎn)擊GUI組件,,否則什么事都不會(huì)發(fā)生,。

只要記住Swing有一個(gè)事件分派線程就行了,,它會(huì)一直運(yùn)行下去,并且按順序處理Swing的事件,。如果你想確保程序不會(huì)發(fā)生死鎖或者競爭的情形,,那么倒是要考慮一下這個(gè)問題。

重訪Runnable

在第13章,,我曾建議大家在實(shí)現(xiàn)Runnable接口時(shí)一定要慎重,。 當(dāng)然如果你設(shè)計(jì)的類必須繼承另一個(gè)類而這個(gè)類又得有線程的行為,,那么選擇Runnable還是對(duì)的,。

不同的JVM,在如何實(shí)現(xiàn)線程方面,,存在著巨大的性能和行為差異,。

管理并發(fā)

當(dāng)你用main方法或另一個(gè)線程修改Swing組件的屬性時(shí),一定要記住,,有可能事件分派線程正在和你競爭同一個(gè)資源,。

看來線程遇到Swing的時(shí)候,麻煩也跟著來了,。要解決這個(gè)問題,,你必須確保Swing組件的屬性只能由事件分派線程來修改。

這要比聽上去的容易一些,。Swing提供了兩個(gè)方法,,SwingUtilities.invokeLater( )和SwingUtilities.invokeandWait( ),你可以從中選一個(gè),。它們負(fù)責(zé)絕大多數(shù)的工作,,也就是說你不用去操心那些很復(fù)雜的線程同步的事了。

這兩個(gè)方法都需要runnable對(duì)象作參數(shù),。當(dāng)Swing的事件處理線程處理完隊(duì)列里的所有待處理事件之后,,就會(huì)啟動(dòng)它的run( )方法了。

能用這兩個(gè)方法來設(shè)置Swing組件的屬性,。

可視化編程與JavaBeans

看到現(xiàn)在你已經(jīng)知道Java在代碼復(fù)用方面的價(jià)值了,。復(fù)用程度最高的代碼是類,因?yàn)樗怯梢唤M緊密相關(guān)的特征(字段field)和行為(方法)組成的,,它既能以合成(composition),,也能以繼承的方式被復(fù)用。

繼承和多態(tài)是面向?qū)ο缶幊痰幕A(chǔ),,但是在構(gòu)建應(yīng)用程序的時(shí)候,,絕大多數(shù)情況下,你真正需要的是能幫你完成特定任務(wù)的組件,。你希望能把這些組件用到設(shè)計(jì)里面,,就像電子工程師把芯片插到電路板上一樣,。同樣,也應(yīng)該有一些能加速這種"模塊化安裝"的編程方法,。

Microsoft的Visual Basic為"可視化編程(Visual programming)"贏得了初次成功——非常巨大的成功,,緊接著是第二代的Borland Delphi(直接啟發(fā)了JavaBean的設(shè)計(jì)) 。有了這些工具,,組件就變得看得見摸的著了,,而組件通常都表示像按鈕,文本框之類的可視組件,,因此這樣一來組件編程也變得有意義了,。實(shí)際上組件的外觀,通常是設(shè)計(jì)時(shí)是什么樣子運(yùn)行時(shí)也就這樣,,所以從控件框(palette)里往表單上拖放組件也就成了可視化編程的步驟了,。而當(dāng)你在這么做的時(shí)候,應(yīng)用程序構(gòu)造工具在幫你寫代碼,,所以當(dāng)程序運(yùn)行時(shí),,它就會(huì)創(chuàng)建那些組件了。

通常簡單地把組件拖到表單上還不足以創(chuàng)建程序,。你還得修改一些特征,,比如它的顏色,上面的文字,,所連接的數(shù)據(jù)庫等等,。這些在設(shè)計(jì)時(shí)可以修改的特征被稱為屬性(properties)。你可以在應(yīng)用程序的構(gòu)建工具里控制組件的屬性,。當(dāng)程序創(chuàng)建完畢,,這些配置信息也被保存下來,這樣程序運(yùn)行時(shí)就能激活這些配置信息了,。

看到現(xiàn)在你或許已經(jīng)習(xí)慣這樣來理解對(duì)象了,,也就是對(duì)象不僅是一組特征,還是一組行為,。設(shè)計(jì)的時(shí)候,,可視組件的行為部分的表現(xiàn)為事件,也就是說"是些能發(fā)生在這個(gè)組件上的事情",。一般來說你會(huì)用把代碼連到事件的方法來決定事件發(fā)生時(shí)該做些什么,。

下面就是關(guān)鍵部分了:應(yīng)用程序的構(gòu)建工具用reflection動(dòng)態(tài)地查詢組件,找出這個(gè)組件支持哪些屬性和事件,。一旦知道它是誰,,構(gòu)建工具就能把這些屬性顯示出來,然后讓你作修改了(創(chuàng)建程序的時(shí)候會(huì)把這些狀態(tài)保存下來),,當(dāng)然還有事件,??傊灰阍谑录想p擊鼠標(biāo)或者其他什么操作,,編程工具就會(huì)幫你準(zhǔn)備好代碼的框架,,然后連上事件。現(xiàn)在,,你只要編寫事件發(fā)生時(shí)該執(zhí)行的代碼就可以了,。

編程工具幫你做了這么多事,這樣你就能集中精力去解決程序的外觀和功能問題了,,至于把各部分銜接起來的細(xì)節(jié)問題,,就交給構(gòu)建工具吧??梢暬幊坦ぞ咧阅塬@得如此巨大的成功,,是因?yàn)樗軜O大的提高編程的效率,當(dāng)然這一點(diǎn)首先體現(xiàn)在用戶界面,,但是其它方面往往也受益頗豐。

JavaBean是干什么用的?

言歸正傳,,組件實(shí)際上是一段封裝成類的代碼,。關(guān)鍵在于,它能讓應(yīng)用程序的構(gòu)建工具把自己的屬性和事件提取出來,。創(chuàng)建VB組件的時(shí)候,,程序員必須按照特定的約定,用相當(dāng)復(fù)雜的代碼把屬性和事件發(fā)掘出來,。Delphi是第二代的可視化編程工具,,而且整個(gè)語言是圍繞著可視化編程設(shè)計(jì)的,所以用它創(chuàng)建可視化組件要簡單得多,。但是Java憑借其JavaBean在可視化組件的創(chuàng)建技術(shù)領(lǐng)域領(lǐng)先群雄,。Bean只是一個(gè)類,所以你不用為創(chuàng)建一個(gè)Bean而去編寫任何額外的代碼,,也不用去使用特殊的語言擴(kuò)展,。事實(shí)上你所要做的只是稍稍改變一下方法命名的習(xí)慣。是方法的名字告訴應(yīng)用程序構(gòu)建工具,,這是一個(gè)屬性,,事件還是一個(gè)普通的方法。

JDK文檔把命名規(guī)范(naming convention)錯(cuò)誤地表述成"設(shè)計(jì)模式(design pattern)”,。這真是不幸,,設(shè)計(jì)模式(請(qǐng)參閱www.BruceEckel.com上的Thinking in Patterns (with Java))本身已經(jīng)夠讓人傷腦筋的了,居然還有人來混淆視聽,。重申一遍,,這算不上是什么設(shè)計(jì)模式,,只是命名規(guī)范而已,而且還相當(dāng)簡單,。
對(duì)于名為xxx的屬性,,你通常都得創(chuàng)建兩個(gè)方法:getXxx( )和setXxx( )。注意構(gòu)建工具會(huì)自動(dòng)地將"get"和"set"后面的第一個(gè)字母轉(zhuǎn)換成小寫,,以獲取屬性的名字,。"get"所返回的類型與”set"所使用的參數(shù)的類型相同。屬性的名字同"get"和”set"方法返回的類型無關(guān),。 對(duì)于boolean型的屬性,,你既可以使用上述的"get"和"set"方法,也可以用"is"來代替"get",。 Bean的常規(guī)方法無需遵循上述命名規(guī)范,,但它們都必須是public的。 用Swing的listener來處理事件,。就是我們講到現(xiàn)在一直在用的這個(gè)方案:用addBounceListener(BounceListener)和removeBounceListener(BounceListener)來處理BounceListener,。絕大多數(shù)情況下,內(nèi)置的事件和監(jiān)聽器已經(jīng)可以滿足你的需要了,,但是你也可以創(chuàng)建你自己的事件和監(jiān)聽器接口,。
第一點(diǎn)回答了你在比較新舊代碼時(shí)或許會(huì)注意的一個(gè)問題:很多方法的名字都有了一些很小的,但明顯沒什么意義的變化?,F(xiàn)在你應(yīng)該知道了,,為了把組件做成JavaBean,絕大多數(shù)修改是在同"get"和"set"的命名規(guī)范接軌,。

用Introspector提取BeanInfo

當(dāng)你把Bean從控件框(palette)里拖到表單上的時(shí)候,,JavaBean架構(gòu)中最重要的一環(huán)就開始工作了。應(yīng)用程序構(gòu)建工具必須能創(chuàng)建這個(gè)Bean(有默認(rèn)構(gòu)造函數(shù)的話就可以了),,然后在不看Bean源代碼的情況下提取所有必須的信息,,然后創(chuàng)建屬性表和事件句柄。

從第十章看,,我們已經(jīng)能部分地解決這個(gè)問題了:Java的reflection機(jī)制可以幫我們找出類的所有方法,。我們不希望像別的可視化編程語言那樣用特殊的關(guān)鍵字來解決JavaBean的問題,因此這是個(gè)完美的解決方案,。實(shí)際上給Java加上reflection的主要原因,,就是為了支持JavaBean(雖然也是為了支持"對(duì)象的序列化(object serializaiton)"和"遠(yuǎn)程方法調(diào)用(remote method invocation)"。所以也許你會(huì)想設(shè)計(jì)應(yīng)用程序構(gòu)建工具的人會(huì)逐個(gè)地reflect Bean,,找出所有的方法,,再在里面挑出Bean的屬性和事件。

這么做當(dāng)然也可以,,但是Java為我們提供了一個(gè)標(biāo)準(zhǔn)的工具,。這不僅使Bean的使用變得更方便了,,而且也為我們創(chuàng)建更復(fù)雜的Bean指出了一條標(biāo)準(zhǔn)通道。這個(gè)工具就是Introspector,,其中最重要的方法就是static getBeanInfo( ),。當(dāng)你給這個(gè)方法傳一個(gè)Class對(duì)象時(shí),它會(huì)徹底盤查這個(gè)類,,然后返回一個(gè)BeanInfo對(duì)象,,這樣你就可以通過這個(gè)對(duì)象找出Bean的屬性,方法和事件了,。

通常你根本不用為此操心,;絕大多數(shù)Bean都是從供應(yīng)商那里直接買過來的,更何況你也不必知道它在底層都玩了什么花樣,。你只要直接把Bean放到表單上,,然后配置一下屬性,再寫個(gè)程序處理一下事件就可以了,。

一個(gè)更復(fù)雜的Bean

所有的字段都是private的這是Bean的通常做法——也就是說做成"屬性"之后,,通常只能用方法來訪問了。

JavaBeans和同步

只要你創(chuàng)建了Bean,,你就得保證它能在多線程環(huán)境下正常工作,,這就是說:
只要允許,所有Bean的public方法都必須是synchronized,。當(dāng)然這會(huì)影響性能(不過在最新版本的JDK里,這種影響已經(jīng)明顯下降了),。如果性能下降確實(shí)是個(gè)問題,,那么你可以把那些不致于引起問題的方法的synchronized給去掉,但是要記住,,會(huì)不會(huì)引發(fā)問題不是一眼就能看出來的,。這種方法首先是要小(就像上面那段程序里的getCircleSize( )),而且/或是"原子操作",,就是說這個(gè)方法所調(diào)用的代碼如此之少,,以至于執(zhí)行期間對(duì)象不會(huì)被修改了。所以把這種方法做成非synchronized的,,也不會(huì)對(duì)性能產(chǎn)生什么重大影響,。所以你應(yīng)該把Bean的所有public方法都做成synchronized,只有在有絕對(duì)必要,,而且確實(shí)對(duì)性能提高有效的時(shí)候,,才能把synchronized移掉。 當(dāng)你將多播事件發(fā)送給一隊(duì)對(duì)此感興趣的listener時(shí),,必須做好準(zhǔn)備,,listener會(huì)隨時(shí)加入或從隊(duì)列中刪除,。
第一個(gè)問題很好解決,但第二個(gè)問題就要好好想想了,。

paintComponent( )也沒有synchronized,。決定覆寫方法的時(shí)候是不是該加synchronized不像決定自己寫的方法那樣清楚。,。這里,,好像paintComponent()加不加synchronized一樣都能工作。但必須考慮的問題有:
這個(gè)方法是否會(huì)修改對(duì)象的"關(guān)鍵"變量,?變量是否”關(guān)鍵"的判斷標(biāo)準(zhǔn)是,,它們是否會(huì)被其它線程所讀寫。(這里,,讀寫實(shí)際上都是由synchronized方法來完成的,,所以你只要看這一點(diǎn)就可以了)在這段程序里,paintComponent( )沒有修改任何東西,。 這個(gè)方法是否與這種"關(guān)鍵"變量的狀態(tài)有關(guān),?如果有一個(gè)synchronized方法修改了這個(gè)方法要用到的變量,那么最好是把這個(gè)方法也作成synchronized的,?;谶@點(diǎn),你或許會(huì)發(fā)現(xiàn)cSize是由synchronized方法修改的,,因此paintComponent( )也應(yīng)該是synchronized,。但是這里你應(yīng)該問問"如果在執(zhí)行paintComponent( )的時(shí)候,cSize被修改了,,最糟糕的情況是什么呢,?"如果問題并不嚴(yán)重,而且轉(zhuǎn)瞬即逝的,,那么為了防止synchronized所造成的性能下降,,你完全可以把paintComponent( )做成非synchronized的。 第三個(gè)思路是看基類的paintComponent( )是不是synchronized,,答案是"否",。這不是一個(gè)萬無一失的判斷標(biāo)準(zhǔn),只是一個(gè)思路,。就拿上面那段程序說吧,,paintComponent( )里面混了一個(gè)通過synchronized方法修改的cSize字段,所以情況也改變了,。但是請(qǐng)注意,,synchronized不會(huì)繼承;也就是說派生類覆寫的基類synchronized方法不會(huì)自動(dòng)成為synchronized方法。 paint( )和paintComponent( )是那種執(zhí)行得越快越好的方法,。任何能夠提升性能的做法都是值得大力推薦的,,所以如果你發(fā)覺不得不對(duì)這些方法用synchronized,那么很有可能是一個(gè)設(shè)計(jì)失敗的信號(hào),。
main( )的測試代碼是根據(jù)BangBeanTest修改而得的,。為了演示BangBean2的多播功能,它多加了幾個(gè)監(jiān)聽器,。

封裝Bean

要想在可視化編程工具里面用JavaBean,,必須先把它放入標(biāo)準(zhǔn)的Bean容器里。也就是把所有Bean的class文件以及一份申明"這是一個(gè)Bean"的"manifest"文件打成一個(gè)JAR的包,。manifest文件是一種有一定格式要求的文本文件,。對(duì)于BangBean,它的manifest文件是這樣的:

Manifest-Version: 1.0

Name: bangbean/BangBean.class

Java-Bean: True

第一行表明manifest的版本,,除非Sun今后發(fā)通知,,否則就是1.0。第二行(空行忽略不計(jì))特別提到了BangBean.class文件,,而第三行的意思是"這是y一個(gè)Bean",。沒有第三行,應(yīng)用程序構(gòu)建工具不會(huì)把它看成Bean,。

唯一能玩點(diǎn)花樣的地方是,,你必須在"Name:"里指明正確的路徑。如果你翻到前面去看BangBean.java,,就會(huì)發(fā)覺它屬于bangbean package(因此必須放到classpath的某個(gè)目錄的"bangbean"的子目錄里),,而manifest的name也必須包含這個(gè)package的信息。此外還必須將manifest文件放到package路徑的根目錄的上一層目錄里,,這里就是將manifest放到"bangbean"子目錄的上一層目錄里,。然后在存放manifest文件的目錄里打入下面這條jar命令:

jar cfm BangBean.jar BangBean.mf bangbean

這里假定JAR文件的名字是BangBean.jar,而manifest文件的名字是BangBean.mf,。

或許你會(huì)覺得有些奇怪,"我編譯BangBean.java的時(shí)候還生成了一些別的class文件,,它們都放到哪里去了,?"是的,它們最后都放在bangbean子目錄里,,而上面那條jar命令的最后一個(gè)參數(shù)就是bangbean,。當(dāng)你給jar一個(gè)子目錄做參數(shù)時(shí),它會(huì)將整個(gè)子目錄都打進(jìn)JAR文件里(這里還包括BangBean.java的源代碼——你自己寫B(tài)ean的時(shí)候大概不會(huì)想把源代碼打進(jìn)包吧),。此外如果你把剛做好的JAR文件解開,,就會(huì)發(fā)現(xiàn)你剛寫的那個(gè)manifest已經(jīng)不在里面了,取而代之的是jar自己生成的(大致根據(jù)你寫的),名為MANIFEST.MF的manifest文件,,而且它把它放在META-INF子目錄里 (意思是“meta-information”),。如果你打開這個(gè)manifest文件,就會(huì)發(fā)現(xiàn)jar給每個(gè)文件加了條簽名的信息,,就像這樣:

Digest-Algorithms: SHA MD5

SHA-Digest: pDpEAG9NaeCx8aFtqPI4udSX/O0=

MD5-Digest: O4NcS1hE3Smnzlp2hj6qeg==

總之,,你不必為這些事?lián)摹D阕餍薷牡臅r(shí)候可以只改你自己寫的manifest文件,,然后重新運(yùn)行一遍jar,,讓它來創(chuàng)建新的JAR文件。你也可以往JAR文件里加新的Bean,,只是要把它們的信息加到manifest里面就行了,。

值得注意的是,你應(yīng)該為每個(gè)Bean創(chuàng)建一個(gè)子目錄,。這是因?yàn)楫?dāng)你創(chuàng)建JAR文件的時(shí)候,,你會(huì)把子目錄的名字交給jar,而jar又會(huì)把子目錄里的所有東西都放進(jìn)JAR,。所以Frog和BangBean都有它們自己的子目錄,。

等你把Bean封裝成JAR文件之后,你就能把它們用到支持Bean的IDE里了,。這個(gè)步驟會(huì)隨開發(fā)工具的不同有一些差別,,不過Sun在他們的"Bean Builder"里提供了一個(gè)免費(fèi)的JavaBean的測試床(可以到j(luò)ava./beans去下載)。要把Bean加入BeanBuiler,,只要把JAR文件拷貝到正確的目錄里就行了,。

Bean的高級(jí)功能

你已經(jīng)知道做一個(gè)Bean有多簡單了,但是它的功能并不僅限于此,。JavaBean的架構(gòu)能讓你很快上手,,但是經(jīng)過擴(kuò)展,它也可以適應(yīng)更復(fù)雜的情況,。這些用途已經(jīng)超出了本書的范圍,,但是我會(huì)做一個(gè)簡單的介紹。你可以在java./beans上找到更多的細(xì)節(jié),。

屬性是一個(gè)能加強(qiáng)的地方,。在我們舉的例子里,屬性都是單個(gè)的,,但是你也可以用一個(gè)數(shù)組來表示多個(gè)屬性,。這被稱為索引化的屬性(indexed property)。你只要給出正確的方法(還是要遵循方法的命名規(guī)范),,Introspector就能找到索引化的屬性,,這樣應(yīng)用程序構(gòu)建工具就能作出正確的反映了。

屬性可以被綁定,也就是說它們能通過PropertyChangeEvent通知其它對(duì)象,。而其它對(duì)象能根據(jù)Bean的變化,,修改自己的狀態(tài)。

屬性是可以被限制的,,也就是說如果其他對(duì)象認(rèn)為屬性的這個(gè)變化是不可接受的,,那么它們可以否決這個(gè)變化。Bean用PropertyChangeEvent通知其他對(duì)象,,而其他對(duì)象則用PropertyVetoException來表示反對(duì),,并且命令它將屬性的值恢復(fù)到原來的狀態(tài)。

你也可以修改Bean在設(shè)計(jì)時(shí)的表示方式:
你可以為Bean提供自定義的屬性清單,。當(dāng)用戶選擇其它Bean的時(shí)候,,構(gòu)?br>

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多