一、引言
對話框,是指一個最頂層的擁有標題和邊框的彈出窗口,典型地應用於用戶進行某種形式的輸入操作。在JDK 5.0和早期版本中,構建一個對話框時,它必須擁有一個作為它的所有者窗口的框架窗口或另一個對話框,即使窗口是不可見的。當用戶最小化一個可見對話框的所有者窗口時會自動隱藏該對話框;而當用戶隨後恢復所有者窗口時,該對話框再次出現。
一個對話框可以是無模式的也可以是模式的。除了該對話框的所有者的窗口之外,模式對話框將阻止在應用程序中其它頂層窗口的輸入。模式對話框捕獲窗口焦點直到其被關閉為止(經常響應於一次按鈕點擊)。另一方面,一個無模式對話框允許用戶改變它的狀態,而此時其它窗口仍然可擁有焦點。後者常用於工具欄窗口中,例如你在一個圖像編輯程序中所見的。
在JDK 5.0和早期版本中的模態模型有一些局限。而且,這種模態模型也存在一些問題。最著名的問題涉及到JavaHelp工具窗口。JavaHelp,Java應用程序中提供幫助信息的API,使用獨立的窗口來顯示所有的必要信息。然而,如果應用程序顯示任何模式對話框,例如一標准Save As對話框,那麼該對話框將阻止用戶與JavaHelp工具窗口進行交互。
但是Java 6,代碼名稱為Mustang,已經通過一種新的抽象窗口工具箱(AWT)模態模型解決了此問題以及其它幾個問題。這種新型模型允許開發者根據其選擇的模態類型指定范圍或限制一個對話框的模態阻斷。如此模態類型也允許窗口和對話框成為真正的無父窗口,也即,擁有一個null父窗口,它可以幫助限制窗口的范圍和對話框的模態。
二、模態類型
Java 6支持四種模態類型:
無模式。無模式對話框在自己為可見時並不阻斷任何其它窗口。
文檔-模式。文檔-模式對話框阻斷所有的來自同一文檔的窗口,除了那些來自於它的子層次上的窗口外。在此意義中,一個文檔是指一個窗口層次-框架窗口,對話框等等,它們共享一個文檔根窗口。文檔根窗口是所有沒有所有者的最頂級窗口。
應用程序-模式。應用程序-模式對話框能夠阻斷同一應用程序中的所有窗口,除了那些來自於它的子層次上的窗口外。如果在浏覽器中激活若干applet,那麼浏覽器將把它們當作獨立的應用程序或者作為一個單一的應用程序。具體情況依賴實現的具體環境。
工具箱-模式。工具箱-模式對話框能夠阻斷所有的運行於同樣的工具箱中的窗口,除了那些來自於它的子層次上的窗口外。如果激活多個applet,那麼它們都運行於同樣的工具箱上。因此,從applet中顯示的工具箱-模式對話框可能影響其它的applet。
正如以前的JDK一樣,一個對話框在缺省情況下是無模式的。但是如果你在Mustang中構造一個模式對話框的話,現在它將缺省使用應用程序-模式類型。另外,模式和無模式對話框的行為已經在Mustang作了改變,它們可以一直出現在其父窗口的頂部。
模態優先權是由阻斷強度決定的。這種模態優先權幫助處理兩個對話框可見並且能夠彼此阻斷的情形。優先權按升序排列分別是:無模式,文檔-模式,應用程序-模式和工具箱-模式。這種優先權自然地反映了一個對話框的范圍阻斷的嵌套情形。一個無模式對話框有一個空范圍的阻斷。文檔-模式對話框的范圍阻斷是在特定的應用程序中完成的,並且所有的應用程序是運行於一種工具箱中。圖1展示了這樣的一個例子。
圖1:對話框的模態優先權
注意,這種新型的模態模型並沒有實現一個系統模態,這將會阻斷(Java或其它)所有被顯示在桌面上的應用程序,而只有一個模式對話框處於活動狀態。
三、了解新的構造器
能提供真正無父窗口而不中斷向後兼容對於AWT開發小組來說是一個挑戰。在JDK 5.0及以前的版本中,允許傳遞null作為JDialog或Jwindow的父窗口。這意味著,一不可見的共享所有者的框架窗口將成為這個對話框或窗口的父窗口。這個共享所有者的框架窗口的創建是為了創建無父窗口對話框。這種情況直到Java SE 6才得以成功-Java SE 6引入了新型的文檔-模式對話框能夠阻止所有的窗口使用相同的文檔。
因而,工具箱會知道這樣的對話框或窗口是沒有null父窗口的。在Mustang中,仍然允許傳遞null作為一個父窗口到舊式的JDialog或JWindow構造器中。並且這可以完成相同的事情:其成為共享的所有者框架窗口而不是成為父窗口,這也照顧了向後兼容問題。然而,現在能把null傳遞到Dialog或Window構造器中,而且也能傳遞到新的JDialog或JWindow構造器中,這意味著這些對話框能夠真正成為無父窗口。
下面列舉的是一些比較有用的構造器:
JDialog(Dialog owner)
創建一個無標題並用一個指定的對話框作為其父窗口的無模式對話框
JDialog(Dialog owner,boolean modal)
用指定的所有者Dialog和模態創建一個具有指定的模態和所有者對話框的對話框
JDialog(Dialog owner,String title)
創建一個具有指定的標題和所有者對話框的無模式對話框
JDialog(Dialog owner,String title,boolean modal)
創建一個具有指定的標題,模態和所有者對話框的對話框
JDialog(Dialog owner,String title,boolean modal,GraphicsConfiguration gc)
創建一個具有指定的標題,模態和所有者對話框和GraphicsConfiguration的對話框
JDialog(Frame owner)
創建一個無標題但用指定的框架作為其所有者的無模式對話框
JDialog(Window owner,String title,Dialog.ModalityType modalityType)
創建一個具有指定的標題,模態和所有者窗口的對話框
有關這裡每個構造器的更多細節請參考Mustang有關文檔。
四、 與所有者窗口一同工作
如前面所提及的,當一個窗口或一個對話框是另一個窗口的父親時,就稱為該父組件"擁有"它的孩子。在此,當在新的模態模式下與所有者窗口一同工作時,你應該注意幾件事情:
創建一個沒有所有者的文檔-模式對話框。在這種情況中,因為Dialog是Window的一個子類;所以,如果一個Dialog實例沒有所有者的話,它自動地成為該文檔的根。這樣,如果這個對話框是文檔-模式的,那麼它的阻斷范圍是空的,並且其行為就象一個無模式對話框。
創建一個有所有者的應用程序-模式或工具箱-模式對話框。一個應用程序-模式或文檔-模式對話框的阻斷范圍並不依靠它的所有者。在這種情況中,所有者所唯一影響的是Z-順序(頂級組件的相對順序)。如果你有兩個窗口,一個遮住另一個,第一個窗口位於第二窗口上面,那麼最頂端的窗口通常是一個活動的窗口。一個相關概念是"總是位於頂層",這時一個窗口總是出現在系統中所有其它窗口之上。對話框總是會位於它的所有者的上部。
在運行時刻改變模態類型。改變一可見的對話框的模態類型可能沒有什麼影響,直到該對話框被隱蔽並且被再次顯示。
下列代碼實例展示了新型的模態API的應用,其中包括現在可靈活在應用於對話框窗口的java.awt.Dialog.ModalExclusionType和 java.awt.Dialog.ModalityType。
圖2顯示出當你運行該代碼後的最終結果。
import java.awt.*;
import java.awt.event.*;
import sun.awt.*;
public class ModalityDemo2 {
// 第一個文檔(red):框架,無模式對話框,文檔-模式對話框
private static Frame f1;
private static Dialog d11;
//……
圖2: 被阻斷的和未被阻斷的對話框
一個使用DOCUMENT_MODAL的對話框會阻止相同文檔中的所有頂層窗口的輸入,除了它自己的子窗口層次之外。一個文檔是一種沒有所有者的頂層窗口。它可以被當作單個文檔的子窗口和頂層窗口。因為每一個頂層窗口必須屬於某文檔,所以它的根可以在沒有所有者的最頂層窗口中找到。
d22.setBounds(sw - 500 + 32, 232, 300, 200);
d22.addWindowListener(closeWindow);
d22.setLayout(new BorderLayout());
l = new Label("DOCUMENT_MODAL");
l.setBackground(Color.BLUE);
l.setAlignment(Label.CENTER);
l.setFont(labelFont);
d22.add(l, BorderLayout.CENTER);
//第三個文檔
f3 = new Frame("Excluded Frame");
f3.setModalExclusionType(
Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
一個被設置為APPLICATION_MODAL的對話框將會阻斷在同一Java程序中的所有的頂層窗口,除了它自己的孩子窗口層次之外。如果若干applet在一浏覽器中被調用,那麼可以把它們當作或者是獨立的應用程序或者是單個的程序。這種行為的實現要依賴具體的環境而定。
注意,下面的f3不會被APPLICATION_MODAL和DOCUMENT_MODAL對話框所阻斷。
f3.setBounds(32, sh - 200 + 32, 300, 200);
f3.addWindowListener(closeWindow);
f3.setLayout(new BorderLayout());
l = new Label("EXCLUDED FRAME");
l.setBackground(Color.GREEN);
l.setAlignment(Label.CENTER);
l.setFont(labelFont);
f3.add(l, BorderLayout.CENTER);
b = new Button("I'm alive!");
f3.add(b, BorderLayout.SOUTH);
f3.setVisible(true);
// 第四個文檔
f4 = new Frame("Parent Frame");
f4.setBounds(sw - 300 + 32, sh - 200 + 32, 300, 200);
f4.addWindowListener(closeWindow);
f4.setLayout(new BorderLayout());
l = new Label("FRAME");
l.setBackground(Color.GRAY);
l.setAlignment(Label.CENTER);
l.setFont(labelFont);
b = new Button("Show file dialog");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fd4.setVisible(true);
}
});
f4.add(b, BorderLayout.SOUTH);
f4.setVisible(true);
fd4 = new FileDialog(f4, "File Dialog", FileDialog.LOAD);
為了向後兼容性起見,File對話框缺省是APPLICATION_MODA。
fd4.setBounds(sw - 400 + 32, sh - 300 + 32, 300, 200);
}
}
五、 有關AWT特性
下面是其它一些在使用新的模態模態時要了解的AWT特性。
(一) 總在最上面
當一個並不總是位於頂層的模式對話框阻斷一個總是位於頂層的窗口時,它們的相對Z-序未特別指出並且是平台依賴的。下列實例表明這個問題:
JFrame f = new JFrame(...);
f.setAlwaysOnTop(true);
f.setVisible(true);
JDialog d = new JDialog(frame, "Dialog", true);
d.setVisible(true);
這段代碼制造了一種矛盾:一方面,d必須位於f之上(因為它是一個模式對話框並且應該阻斷f)。而另一方面,f必須必須位於d之上,因為f被設置為總是位於頂層,而d不是。如此情形是未特別指出的。然而,總是有解決辦法:如果d也被設置為總是位於頂層(d.setAlwaysOnTop(true)),那麼該對話框將在任何時候位於框架窗口之上。
(二) toFront()和toBack()方法
一個模式對話框應該總是位於所有的被其阻斷的窗口之上。這樣,如果一被阻斷的窗口被送到前面,那麼它的阻斷對話框,如果有的話,也被送到前面並且位於被阻斷的窗口之上。同樣,如果一模式對話框被送到後面,那麼所有它的被阻斷的窗口被送到後面以使其位於阻斷對話框下面。
(三) 最小化,最大化和關閉被阻斷的窗口
當一個模式對話框阻斷一個窗口時,用戶可能無法最小化或最大化被阻斷的窗口。然而,實際行為並未特別指出並且是平台依賴的。在任何情況下,用戶都不能交互式地關閉被阻斷的窗口。但是它能被通過編程方式關閉-通過在被阻斷的窗口上調用setVisible(false)或dispose()方法。
(四) 激活被阻斷的窗口
當用戶選擇一個被阻斷的窗口時,它有可能連同阻斷模式對話框一起被送到前端,然後它成為當前活動窗口。然而,實際行為並未特別指出並且是平台依賴的。
(五) 隱藏模式對話框
當具有當前焦點的模式對話框被隱藏時,它的所有者未被阻斷而有可能成為活動窗口。然而,實際行為並未特別指出並且是平台依賴的。如果要被隱藏的模式對話框不具有焦點,那麼活動窗口保持不變。
(六) 安全性
為了顯示工具箱-模式對話框,需要一種特殊的AWTPermission,toolkitModality。例如,這將防止從applet中顯示的模式對話框被浏覽器或JWS(Java Web Start)的軟件所阻斷。
相同的權限需要用於從工具箱模態中排除一個窗口。例如,這將防止一個從applet中顯示的對話框被浏覽器或JWS的模式對話框所阻斷。
(七) 平台支持
有兩個java.awt.Toolkit方法允許你檢查是否當前平台支持特定的模態特征:
isModalityTypeSupported(modalityType),返回是否給定的模態類型為當前平台所支持。如果不支持模式M並且一個對話框被設置為M-modal,那麼其行為就象一個無模態對話框。
isModalExclusionTypeSupported(modalExclusionType),返回是否給定的模態exclusion類型為當前平台所支持。如果不支持exclusion類型E並且一個窗口被標記為E-excluded,那麼這沒有任何影響。
六、兼容性
默認的模態類型是應用程序模式,它被如Dialog.setModal(true),Dialog(owner,true)等API所用調用。在JDK 6以前,默認類型是工具箱-模式,但是在應用程序模態和工具箱模態之間的唯一區別在於從JWS軟件中激活applet和應用程序方面。
注意:任何Java SE平台API的增加或對其說明的改進必須經JSR 270專家組的過目和同意。