使用Swing中的高层容器与使用高层AWT容器不同。对于AWT容器,Frame,Window,Dialog以及Applet,我们可以将组件直接添加到容器,并且我们只有一个位置来放置这些组件。在Swing世界中,高层容器,JFrame,JWindow,JDialog以及JApplet,加上JInternalFrame容器,依赖JRootPane。我们并不能将组件直接添加到容器,而只能将这些组件添加到root pane(根面板)的一部分。然后由根面板来管理这些组件。
为什么添加这个间接层呢?无论我们是否相信,这样做是为了事情的简化。根面板在层中管理其组件,从而如工具提示文本这样的元素总是显示在组件上面,而且我们不必担心拖拽某个组件在其他组件周围运动。
JInternalFrame并没有相对应的AWT组件,他也提供了一些额外的功能用于处理被放置在桌面(在JDesktopPane中)中的情况。JInternalFrame可以用作在Swing程序创建多文档界面(MDI)程序的基础。在我们的程序中我们可以管理一系列的内部框架,并且他们绝不会超出我们的主程序容器。
下面我们开始探讨新的JRootPane类,他管理所有的高层容器。
JRootPane担当高层Swing容器的容器代理。因为容器只存放一个JRootPane,当我们由高层容器中添加或是移除组件时,我们并没有直接修改容器中的组件,而是间接的由JRootPane实例添加或是移除组件。事实上,高层容器担当代理的角色,由JRootPane完成所有的工作。
JRootPane容器依赖其内联类RootLayout进行布局管理,并且管理存储JRootPane的高层容器的所有空间。在JRootPane中只有两个组件:一个JLayeredPane以及一个玻璃嵌板(Component)。前面的玻璃嵌板可以是任意组件,而且是不可见的。玻璃嵌板保证类似工具提示文本这样的元素显示在其他的Swing之前。后面是JLayeredPane,在其上部包含一个可的选的JMenuBar,在其下面的另一层中包含一个内容面析(Container)。通常我们将组件放在JRootPane中就是放置在内容面板中。图8-1有助于我们理解RootLayout是如何布局组件的。
注意:JLayerPane也仅是一个Swing容器。他可以包含任意的组件并且具有一些特定的布局特性。JRootPane面板中所用的JlayeredPane只包含一个JMenuBar以及一个Container作为其内容面板。内容面板有其自己的布局管理器,默认情况下为BorderLayout。
尽管JRootPane具有一个公开的无参数的构造函数,但是通常我们并不会亲自创建JRootPane。相反,实现了RootPaneContainer接口的类创建JRootPane。然后,我们由该组件通过RootPaneContainer接口来获取根面板,我们会在稍后进行描述。
如表8-1所示,JRootPane有11个属性。大多数情况下,当我们为高层容器获取或是设置一个这样的属性时,例如JFrame,容器只是简单的将请求传递给其JRootPane。
JRootPane的玻璃嵌板必须是透明的。因为玻璃嵌板会占据JLayeredPane前面的整个区域,一个不透明的玻璃嵌板会将其菜单栏与内容面板渲染为不可见。而且,因为玻璃嵌板与内容面板共享相同的边界,当设置optimizedDrawingEnabled属性时会返回玻璃嵌板的可见性。
Table 8-1. JRootPane属性
属性名 | 数据类型 | 访问性 |
accessibleContext | AccessibleContext | 只读 |
contentPane | Container | 读写 |
defaultButton | JButton | 读写绑定 |
glassPane | Component | 读写 |
jMenuBar | JMenuBar | 读写 |
layeredPane | JLayeredPane | 读写 |
optimizedDrawingEnabled | boolean | 只读 |
UI | RootPaneUI | 读写 |
UIClassID | String | 只读 |
validateRoot | boolean | 只读 |
windowDecorationStyle | int | 读写绑定 |
windowDecorationStyle属性用来描述包含JRootPane窗口的窗口装饰(边框,标题,关闭窗口的按钮)。他可以设置为下列的JRootPane类常量:
使用windowDecorationStyle设置后的实际效果要依据于当前的观感。这只是一个小提示。默认情况,这个设置为NONE。如果这个设置不为NONE,使用true值来调用JDialog或JFrame的setUndecorated()方法,并且当前观感的getSupportsWindowDecorations()方法报告true,那么则由观感,而不是窗口管理器,来提供窗口装饰。这可以使得使用高层窗口的程序看起来并不是来自于用户所用的工作平台,而是来自于我们自己的一半,但是仍然可以提供通知,最大化,最小化以及关闭按钮。
对于Metal观感(以及Ocean主题),getSupportsWindowDecorations()报告true。其他系统提供的观感类型报告false。图8-2演示了由Metal观感所提供的带有窗口装饰的框架样子。
生成图8-2的程序源码显示在列表8-1中。
package swingstudy.ch08; import java.awt.EventQueue; import javax.swing.JFrame; import javax.swing.JRootPane; public class AdornSample { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Runnable runner = new Runnable() { public void run() { JFrame frame = new JFrame("Adornment Example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setUndecorated(true); frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME); frame.setSize(300, 100); frame.setVisible(true); } }; EventQueue.invokeLater(runner); } }
表8-2显示了JRootPane的12UIResource相关的属性。这些中的大多数属性与配置窗体装饰风格时所用的默认边框有关。
JRootPane UIResource元素
属性字符串 | 对象类型 |
RootPane.actionMap | ActionMap |
RootPane.ancestroInputMap | InputMap |
RootPane.colorChooserDialogBorder | Border |
RootPane.defaultButtonWindowKeyBindings | Object[] |
RootPane.errorDialogBorder | Border |
RootPane.fileChooserDialogBorder | Border |
RootPane.frameBorder | Border |
RootPane.informationDialogBorder | Border |
RootPane.plainDialogBorder | Border |
RootPane.questionDialogBorder | Border |
RootPane.warningDialogBorder | Border |
RootPnaeUI | String |
RootPaneContainer接口定义了用于访问JRootPane中的各种面板以及访问JRootPane本身的setter/getter方法。
public interface RootPaneContainer { // Properties public Container getContentPane(); public void setContentPane(Container contentPane); public Component getGlassPane(); public void setGlassPane(Component glassPane); public JLayeredPane getLayeredPane(); public void setLayeredPane(JLayeredPane layeredPane); public JRootPane getRootPane(); }
在预定义的Swing组件之中,JFrame, JWindow, JDialog,JApplet以及JInternalFrame类实现了RootPaneContainer接口。对于大部分来说,这些实现简单的将请求传递给高层容器的JRootPane实现。下面的代码是RootPaneContainer的玻璃嵌板实现:
public Component getGlassPane() { return getRootPane().getGlassPane(); } public void setGlassPane(Component glassPane) { getRootPane().setGlassPane(glassPane); }
JLayeredPane是JRootPane的主要组件容器。JLayeredPane管理其内部的组件的Z顺序或层。这可以保证在某些任务的情况下,例如创建工具提示文本,弹出菜单与拖拽,正确的组件可以创建在其他的组件之上。我们可以使用系统定义的层次,或者是我们可以创建自己的层次。
尽管JLayeredPane容器并没有布局管理器,但是并没有什么可以阻止我们设置容器的layout属性。
创建JLayeredPane
与JRootPane类似,我们从不亲自创建JLayeredPane类的实例。当为实现了RootPaneContainer的预定义类创建一个默认的JRootPane时,JRootPane为其主要的组件区域创建一个JLayeredPane,并添加一个初始化的内容面板。
在层中添加组件
每一个所添加的组件的层设置管理JLayeredPane中组件的Z顺序。层设置越高,则组件绘制离顶层组件就越近。当我们向JLayeredPane中添加组件时我们可以使用布局管理的限制来设置层。
Integer layer = new Integer(20);
aLayeredPane.add(aComponent, layer);
我们也可以在向JLayeredPane添加组件之前调用public void setLayer(Component comp, int layer)或public void setLayer(Component comp, int layer, int position)方法。
aLayeredPane.setLayer(aComponent, 10); aLayeredPane.add(aComponent);
JLayeredPane类预定义了六个特殊值常量。另外,我们还可以使用public int currentLayer()方法来获得最顶部的当前层,使用public int lowestLayer()方法获得最底层。表8-3列出六个预定义的层常量。
JLayeredPane层常量
常量 | 描述 |
FRAME_CONTEND_LAYER | 层-30000用于存储菜单栏以及内容面板;通常并不为开发者所用。 |
DEFAULT_LAYER | 零层用于通常的组件层。 |
PALETTE_LAYER | 层100用于存储浮动工具栏以及类似的组件 |
MODAL_LAYER | 层200用于存储显示在默认层,调色板之上以及弹出菜单之下的弹出对话框 |
POPUP_LAYER | 层300用于存储弹出菜单以及工具提示文本 |
DRAG_LAYER | 层400用于存储保持在顶部的拖动对象 |
尽管我们可以为层次使用自己的常量,但是使用时要小心,因为系统会在需要时使用预定义的常量。如果我们的常量不正确,组件就不会如我们希望的那样工作。
图8-3可视化的显示了不同层是如何放置的。
使用内容层与位置
JLayeredPane中的组件同时具有层与位置。当某一层只有一个组件时,其位于位置零。当在相同的层有多个组件时,后添加的组件具有更高的位置数字。位置设置越低,显示距离顶部组件越近。(这与层的行为相反。)图8-4显示在相同层上四个组件的位置。
要重新安排一层上的组件,我们可以使用public void moveToBack(Component component)或是public void moveToFront(Component component)方法。当我们将一个组件移到前面时,他到达该层的位置0。当我们一个组件移动到后时,他到达该层的最大位置处。我们也可以使用public void setPosition(Component component, int position)方法来手动设置位置。位置-1自动为具有最高位置的底层(如图8-4)。
JLayeredPane属性
表8-4显示了JLayeredPane的两个属性。optimizedDrawingEnabled属性决定了JlayeredPane中的组件是否可以重叠。默认情况下,这个设置为true,在JRootPane的标准用法中,JMenuBar与内容面板不可以重叠。然而,JLayeredPane自动验证属性设置来反映面板内容的当前状态。
JLayeredPane属性
属性名 | 数据类型 | 访问性 |
accessibleContext | AccessibleContext | 只读 |
optimizedDrawingEnabled | boolean | 只读 |