文档

Java™ 教程
隐藏目录
使用根窗格
路径: 使用 Swing 创建 GUI
课程: 使用 Swing 组件
章节: 如何使用各种组件

如何使用根面板

一般来说,您不直接创建JRootPane对象。相反,当您实例化JInternalFrame或其中一个顶级Swing容器(例如JAppletJDialogJFrame)时,您会获得一个JRootPane对象(不管您是否需要)。

使用顶级容器介绍了使用根面板的基础知识,包括获取内容面板、设置其布局管理器以及向其添加Swing组件。本节将更详细地介绍根面板,包括组成根面板的组件以及您如何使用它们。

根面板管理四个其他面板:分层面板、菜单栏、内容面板和玻璃面板。

如上图所示,根面板由四个部分组成:

玻璃面板
默认情况下是隐藏的。如果您使玻璃面板可见,则它就像是放在根面板的所有其他部分上的一片玻璃。它完全透明,除非您实现了玻璃面板的paintComponent方法,使其执行某些操作,并且它可以拦截根面板的输入事件。在下一节中,您将看到使用玻璃面板的示例。
分层面板
用于定位其内容,其中包括内容面板和可选的菜单栏。还可以按照指定的Z顺序容纳其他组件。有关详细信息,请参见分层面板
内容面板
是根面板可见组件的容器,不包括菜单栏。有关使用内容面板的信息,请参见使用顶级容器
可选的菜单栏
是根面板容器的菜单的位置。如果容器有菜单栏,通常使用容器的setJMenuBar方法将菜单栏放置在适当的位置。有关使用菜单和菜单栏的更多信息,请参见如何使用菜单

玻璃面板

当您希望能够捕获事件或在已包含一个或多个组件的区域上进行绘制时,玻璃面板很有用。例如,您可以通过使玻璃面板拦截事件来禁用多组件区域的鼠标事件。或者,您可以使用玻璃面板在多个组件上显示图像。

下面是一个演示玻璃面板功能的应用程序的图片。它包含一个复选框,可以设置玻璃面板是否“可见” - 是否可以接收事件并在屏幕上绘制自己。当玻璃面板可见时,它会阻止所有输入事件传递到内容面板中的组件。它还会在最后检测到鼠标按下事件的位置绘制一个红点。

GlassPaneDemo的快照

试一试: 
  1. 点击“启动”按钮以使用Java™ Web Start运行GlassPaneDemo(下载JDK 7或更高版本)。或者,如果要自己编译和运行示例,请参考示例索引启动GlassPaneDemo示例
  2. 点击按钮1。
    按钮的外观会变化,以显示已被点击。
  3. 选中复选框,使玻璃板“可见”,然后再次点击按钮1。
    按钮不会被点击,因为玻璃板会拦截所有鼠标事件。玻璃板在鼠标释放的位置绘制一个红色圆圈。
  4. 再次点击复选框,使玻璃板隐藏。
    当玻璃板检测到复选框上方的事件时,它会将其转发给复选框。否则,复选框将不会响应点击事件。

以下代码来自GlassPaneDemo.java,它显示和隐藏玻璃板。此程序创建自己的玻璃板。但是,如果玻璃板不进行任何绘制,程序可以简单地将侦听器附加到默认玻璃板,即通过getGlassPane返回的玻璃板。

myGlassPane = new MyGlassPane(...);
changeButton.addItemListener(myGlassPane);
frame.setGlassPane(myGlassPane);
...
class MyGlassPane extends JComponent
                  implements ItemListener {
    ...
    //响应更改按钮点击事件。
    public void itemStateChanged(ItemEvent e) {
        setVisible(e.getStateChange() == ItemEvent.SELECTED);
    }
...
}

下一个代码片段实现了玻璃板的鼠标事件处理。如果鼠标事件发生在复选框上方,玻璃板会重新分发该事件,以便复选框接收到它。

... //在玻璃板的鼠标监听器的实现中:
public void mouseMoved(MouseEvent e) {
    redispatchMouseEvent(e, false);
}

... /* mouseDragged, mouseClicked, mouseEntered,
    * mouseExited和mousePressed方法的实现与mouseMoved相同。 */...

public void mouseReleased(MouseEvent e) {
    redispatchMouseEvent(e, true);
}

private void redispatchMouseEvent(MouseEvent e,
                                  boolean repaint) {
    Point glassPanePoint = e.getPoint();
    Container container = contentPane;
    Point containerPoint = SwingUtilities.convertPoint(
                                    glassPane,
                                    glassPanePoint,
                                    contentPane);

    if (containerPoint.y < 0) { //不在内容窗格中
        //可能有特殊代码来处理菜单栏或非系统窗口装饰上的鼠标事件,
        //例如Java外观提供的装饰。
    } else {
        //鼠标事件可能发生在内容窗格上。
        //确定准确位于哪个组件上。
        Component component =
            SwingUtilities.getDeepestComponentAt(
                                    container,
                                    containerPoint.x,
                                    containerPoint.y);

        if ((component != null)
            && (component.equals(liveButton))) {
            //将事件转发到复选框上。
            Point componentPoint = SwingUtilities.convertPoint(
                                        glassPane,
                                        glassPanePoint,
                                        component);
            component.dispatchEvent(new MouseEvent(component,
                                                 e.getID(),
                                                 e.getWhen(),
                                                 e.getModifiers(),
                                                 componentPoint.x,
                                                 componentPoint.y,
                                                 e.getClickCount(),
                                                 e.isPopupTrigger()));
        }
    }

    //如果需要,更新玻璃板。
    if (repaint) {
        glassPane.setPoint(glassPanePoint);
        glassPane.repaint();
    }
}

这是MyGlassPane中实现绘制的代码。

protected void paintComponent(Graphics g) {
    if (point != null) {
        g.setColor(Color.red);
        g.fillOval(point.x - 10, point.y - 10, 20, 20);
    }
}

分层窗格

分层窗格是一个带有深度的容器,使得重叠的组件可以相互覆盖。有关分层窗格的一般信息在如何使用分层窗格中。本节讨论了根窗格如何使用分层窗格的细节。

每个根窗格都将菜单栏和内容窗格放在JLayeredPane的实例中。层次结构提供的Z排序功能使得可以在其他组件上方显示弹出菜单等行为。

您可以选择将组件放在根窗格的层次结构中。如果这样做,您应该注意某些深度被定义为用于特定功能,并且应按照预期使用这些深度。否则,您的组件可能无法与其他组件协同工作。下面是一个显示功能层次结构及其关系的图表:

由JLayeredPane定义的层次结构

下表描述了每个层次结构的预期用途,并列出与每个层次结构对应的JLayeredPane常量:

层次名称 描述
FRAME_CONTENT_LAYER new Integer(-30000) 根窗格将菜单栏和内容窗格添加到此深度的层次结构中。
DEFAULT_LAYER new Integer(0) 如果在将组件添加到层次结构时未指定其深度,层次结构会将其放在此深度。
PALETTE_LAYER new Integer(100) 此层次结构适用于浮动工具栏和调色板。
MODAL_LAYER new Integer(200) 模态内部框对话框属于此层次结构。
POPUP_LAYER new Integer(300) 弹出窗口位于此层次结构中,因为它们需要出现在几乎所有组件之上。
DRAG_LAYER new Integer(400) 在拖动组件时使用的层次结构。组件在放下时应返回其常规层次。

这是RootLayeredPaneDemo的图片,它是LayeredPaneDemo的一个版本,使用了根窗格的层次结构,而不是创建一个新的层次结构。

修改后的LayeredPaneDemo使用根窗格的层次结构

请尝试以下操作: 
  1. 点击“启动”按钮使用Java™ Web Start来运行RootLayeredPaneDemo(下载JDK 7或更高版本)。或者,如果要自己编译和运行示例,请参考示例索引启动RootLayeredPaneDemo
  2. 在窗口中移动鼠标光标,使Duke移动到其他组件的上方。
    请注意,当鼠标光标位于非标签组件上方(无论是在内容窗格中还是在Java外观提供的标题栏中),Duke的移动会暂时停止。这是因为鼠标移动事件会发送到包含层次结构中最深且对鼠标事件感兴趣的组件。移动Duke的鼠标移动侦听器注册在分层窗格上,该分层窗格中的大多数组件(标签除外)恰好具有鼠标移动侦听器。当鼠标移动到分层窗格中的感兴趣组件上时,分层窗格不会接收到事件,而是由感兴趣的组件接收到事件。
  3. 确保“层中的顶部位置”复选框已被选中,将Duke的层级更改为黄色(-30000)。
    与之前一样,除了品红(0)和青色(301)的矩形外,Duke出现在其他组件的上方。
  4. 保持Duke在黄色层级中,点击复选框将Duke发送到-30000层级的底部。
    Duke消失了,因为内容窗格及其中的所有组件现在都在他的上方。
  5. 将Duke的层级更改为青色(301),将Duke稍微向下移动,使他站在黄色矩形的顶边上,然后按空格键来弹出组合框的下拉列表。
    如果外观实现将下拉列表作为轻量级弹出窗口,Duke会显示在下拉列表的上方。

根窗格 API

下表列出了使用根窗格、玻璃窗格和内容窗格的 API。有关使用内容窗格的更多信息,请参阅使用顶层容器。本节中的表格如下:

其他根窗格部分的 API 在其他地方描述:

使用根窗格
方法 目的
JRootPane getRootPane()
(在 JApplet, JDialog, JFrame, JInternalFrameJWindow 中)
获取 applet、dialog、frame、internal frame 或 window 的根窗格。
static JRootPane getRootPane(Component)
(在 SwingUtilities 中)
如果组件包含根窗格,则返回该根窗格。否则,返回包含该组件的根窗格(如果有)。
JRootPane getRootPane()
(在 JComponent 中)
调用 JComponentSwingUtilities getRootPane 方法。
void setDefaultButton(JButton)
JButton getDefaultButton()
设置或获取根窗格中的默认按钮(如果有)。外观相关的操作,如按下 Enter 键,会执行按钮的动作。
设置或获取根窗格的内容
以下方法在JAppletJDialogJFrameJInternalFrameJRootPaneJWindow中定义,除非另有说明。
方法 目的
void setGlassPane(Component)
Component getGlassPane()
设置或获取玻璃窗格。
void setLayeredPane(JLayeredPane)
Container getLayeredPane()
设置或获取分层窗格。
void setContentPane(Container)
Container getContentPane()
设置或获取内容窗格。
void setJMenuBar(JMenuBar)
JMenuBar getJMenuBar()
(在JWindow中未定义)
设置或获取菜单栏。

使用根窗格的示例

每个Swing程序都有一个根窗格,但很少直接引用它。下面的示例演示了如何使用JRootPane或玻璃窗格的特性。还可以参考以下列表:

示例 所述位置 注释
GlassPaneDemo 本节 使用绘制和重新分派事件的玻璃窗格。
RootLayeredPaneDemo 本节 将LayeredPaneDemo适配为使用根窗格的分层窗格。
ListDialog 如何使用列表 设置JDialog的默认按钮。
FrameDemo2 如何制作窗体 设置JFrame的默认按钮。

上一页:如何使用进度条
下一页:如何使用滚动窗格