全面剖析Java 6中新型模态对话框API
作者:朱先忠编译
一、引言
对话框,是指一个最顶层的拥有标题和边框的弹出窗口,典型地应用于用户进行某种形式的输入操作。在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展示了这样的一个例子。
四、 与所有者窗口一同工作
如前面所提及的,当一个窗口或一个对话框是另一个窗口的父亲时,就称为该父组件"拥有"它的孩子。在此,当在新的模态模式下与所有者窗口一同工作时,你应该注意几件事情:
·创建一个没有所有者的文档-模式对话框。在这种情况中,因为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; //……省略,详见所附源码文件 |
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); |
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); } } |
JFrame f = new JFrame(...); f.setAlwaysOnTop(true); f.setVisible(true); JDialog d = new JDialog(frame, "Dialog", true); d.setVisible(true); |