如何使用根窗格

通常,您不会直接创建JRootPane对象。而是实例化JInternalFrame或顶级 Swing 容器之一(例如JAppletJDialogJFrame)时,会得到JRootPane(无论是否想要!)。

使用顶层容器告诉您使用根窗格的基本知识—获取内容窗格,设置其布局 管理 器以及向其中添加 Swing 组件。本节介绍有关根窗格的更多信息,包括组成根窗格的组件以及如何使用它们。

根窗格 管理 其他四个窗格:分层窗格,菜单栏,内容窗格和玻璃窗格。

如上图所示,根窗格包含四个部分:

  • 玻璃窗格

    • 隐藏,默认情况下。如果使玻璃窗格可见,则就像在根窗格的所有其他部分上的一块玻璃一样。它是完全透明的,除非您实现玻璃窗格的paintComponent方法,以便它可以执行某些操作,并且它可以拦截根窗格的 Importing 事件。在next section中,您将看到一个使用玻璃窗格的示例。
  • 分层窗格

    • 用于放置其内容,该内容由内容窗格和可选菜单栏组成。也可以按指定的 ZSequences 容纳其他组件。有关信息,请参阅分层窗格
  • 内容窗格

    • 根窗格的可见组件的容器,但菜单栏除外。有关使用内容窗格的信息,请参见使用顶层容器
  • 可选菜单栏

    • 根窗格的容器菜单的主目录。如果容器具有菜单栏,则通常使用容器的setJMenuBar方法将菜单栏放置在适当的位置。有关使用菜单和菜单栏的更多信息,请参见如何使用菜单

玻璃窗格

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

这是演示玻璃窗格功能的应用程序的图片。它包含一个复选框,可让您设置玻璃窗格是否“可见”-它是否可以获取事件并在屏幕上进行绘制。当玻璃窗格可见时,它将阻止所有 Importing 事件到达内容窗格中的组件。它还在上次检测到鼠标按下事件的位置绘制了一个红点。

GlassPaneDemo 的快照

Try this:

  • 单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 GlassPaneDemo。另外,要自己编译和运行示例,请查阅example index

  • 点击按钮 1.
    该按钮的外观更改为显示已被单击。

  • 单击复选框,使玻璃窗格变为“可见”,然后再次单击按钮 1.
    由于玻璃窗格会拦截所有鼠标事件,因此无法单击该按钮。玻璃窗格在释放鼠标的位置绘制了一个红色的圆圈。

  • 再次单击复选框,以隐藏玻璃窗格。
    当玻璃窗格在复选框上检测到事件时,会将其转发到复选框。否则,该复选框将不响应点击。

GlassPaneDemo.java中的以下代码显示和隐藏玻璃窗格。该程序恰好创建自己的玻璃窗格。但是,如果玻璃窗格不做任何绘画,则该程序可能只是将侦听器附加到默认玻璃窗格,如getGlassPane返回。

myGlassPane = new MyGlassPane(...);
changeButton.addItemListener(myGlassPane);
frame.setGlassPane(myGlassPane);
...
class MyGlassPane extends JComponent
                  implements ItemListener {
    ...
    //React to change button clicks.
    public void itemStateChanged(ItemEvent e) {
        setVisible(e.getStateChange() == ItemEvent.SELECTED);
    }
...
}

下一个代码段实现了玻璃窗格的鼠标事件处理。如果在复选框上发生鼠标事件,则玻璃窗格将重新分发该事件,以便复选框接收该事件。

...//In the implementation of the glass pane's mouse listener:
public void mouseMoved(MouseEvent e) {
    redispatchMouseEvent(e, false);
}

.../* The mouseDragged, mouseClicked, mouseEntered,
    * mouseExited, and mousePressed methods have the same
    * implementation as 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) { //we're not in the content pane
        //Could have special code to handle mouse events over
        //the menu bar or non-system window decorations, such as
        //the ones provided by the Java look and feel.
    } else {
        //The mouse event is probably over the content pane.
        //Find out exactly which component it's over.
        Component component =
            SwingUtilities.getDeepestComponentAt(
                                    container,
                                    containerPoint.x,
                                    containerPoint.y);

        if ((component != null)
            && (component.equals(liveButton))) {
            //Forward events over the check box.
            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()));
        }
    }

    //Update the glass pane if requested.
    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常量:

Layer NameValueDescription
FRAME_CONTENT_LAYERnew Integer(-30000)根窗格在此深度将菜单栏和内容窗格添加到其分层窗格中。
DEFAULT_LAYERnew Integer(0)如果在将组件添加到分层窗格中时未指定组件的深度,则分层窗格会将其置于该深度。
PALETTE_LAYERnew Integer(100)该层对于 Float 工具栏和调色板很有用。
MODAL_LAYERnew Integer(200)模态内部框架对话框将属于此层。
POPUP_LAYERnew Integer(300)弹出窗口位于此层,因为它们需要出现在几乎所有内容的上方。
DRAG_LAYERnew Integer(400)旨在在拖动组件时使用。组件在放下后应返回其常规层。

这是 RootLayeredPaneDemo 的图片,该图片是 LayeredPaneDemo使用根窗格的分层窗格,而不是创建新的分层窗格。

修改 LayeredPaneDemo 以使用根窗格的分层窗格

Try this:

  • 单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行 RootLayeredPaneDemo。另外,要自己编译和运行示例,请查阅example index

  • 在窗口中四处移动光标,以便 Duke 在其他组件的顶部移动。
    请注意,当光标位于非标签组件的顶部时(无论是在内容窗格中还是在 Java 外观提供的标题栏中),Duke 的移动都将暂时停止。这是因为鼠标移动事件进入包含层次结构中最深的组件,并且对鼠标事件感兴趣。移动 Duke 的鼠标移动侦听器已注册在分层窗格中,并且该窗格中的大多数组件(标签除外)都具有鼠标移动侦听器。当鼠标移到分层窗格中的某个感兴趣的组件上时,分层窗格不会获取事件,而感兴趣的组件则会catch该事件。

  • 确保选中“在图层中的最高位置”复选框,然后将 Duke 的图层更改为“黄色”(-30000)。
    和以前一样,他出现在其他组件的顶部,洋红色(0)和青色(301)矩形除外。

  • 将 Duke 保留在 Yellow 层中,单击复选框以将 Duke 发送到-30000 层的背面。
    Duke 之所以消失,是因为内容窗格及其中的所有组件现在都在他上方。

  • 将 Duke 的图层更改为 Cyan(301),向下移动 Duke 一点,以便他站在 Yellow 矩形的顶部边缘,然后按 Space 弹出组合框的下拉列表。
    如果外观将下拉列表实现为轻量级弹出窗口,则 Duke 会出现在下拉列表的顶部。

根窗格 API

后面的表列出了用于使用根窗格,玻璃窗格和内容窗格的 API。有关使用内容窗格的更多信息,请转到使用顶层容器。以下是本节中的表格:

其他地方介绍了使用根窗格其他部分的 API:

使用根窗格

MethodPurpose
JRootPane getRootPane()

(在JAppletJDialogJFrameJInternalFrameJWindow中)
获取 Servlets,对话框,框架,内部框架或窗口的根窗格。
静态 JRootPane getRootPane(Component)
(在SwingUtilities中)
如果组件包含一个根窗格,请返回该根窗格。否则,返回包含组件的根窗格(如果有)。
JRootPane getRootPane()
(在JComponent中)
JComponent调用SwingUtilities getRootPane方法。
void setDefaultButton(JButton)
JButton getDefaultButton()
设置或获取哪个按钮(如果有)是根窗格中的默认按钮。特定于外观的操作(例如按 Enter)将导致执行按钮的操作。

设置或获取根窗格的内容
除非另有说明,否则以下方法在JAppletJDialogJFrameJInternalFrameJRootPaneJWindow中定义.

MethodPurpose
void setGlassPane(Component)

Component getGlassPane()
设置或获取玻璃窗格。
void setLayeredPane(JLayeredPane)
Container getLayeredPane()
设置或获取分层窗格。
void setContentPane(Container)
Container getContentPane()
设置或获取内容窗格。
void setJMenuBar(JMenuBar)
JMenuBar getJMenuBar()
(未在JWindow中定义)
设置或获取菜单栏。

使用根窗格的示例

每个 Swing 程序都有一个根窗格,但是很少有直接引用它。下表中的示例说明了如何使用JRootPane或玻璃窗格的功能。另请参阅以下列表:

ExampleWhere DescribedNotes
GlassPaneDemoThis section使用绘制一点并重新分发事件的玻璃窗格。
RootLayeredPaneDemoThis section调整 LayeredPaneDemo 以使用根窗格的分层窗格。
ListDialog如何使用清单设置JDialog的默认按钮。
FrameDemo2如何制作框架设置JFrame的默认按钮。