如何使用分层窗格

分层窗格是一个 Swing 容器,它提供了第三个维度来定位组件:* depth ,也称为 Z order *。将组件添加到分层窗格时,可以将其深度指定为整数。数字越高,组件越接近容器内的“顶部”位置。如果组件重叠,则“较近”的组件将以较低的深度绘制在组件顶部。在相同深度处的组件之间的关系取决于它们在深度内的位置。

Note:

AWT 容器具有一个 API,可让您操作组件* Z order *。有关更多信息,请参见AWT 焦点规范

每个具有root pane的 Swing 容器(例如JFrameJAppletJDialogJInternalFrame)都会自动具有分层的窗格。大多数程序没有显式使用根窗格的分层窗格,因此本节将不进行讨论。您可以在提供概述的根窗格和具有更多详细信息的分层窗格中找到有关它的信息。本节介绍如何创建自己的分层窗格以及如何在可以使用常规 Swing 容器的任何地方使用它。

Swing 提供了两个分层的窗格类。第一个JLayeredPane是根窗格使用的类,也是本节中示例使用的类。第二个JDesktopPaneJLayeredPane子类,专门用于保持内部框架的任务。有关使用JDesktopPane的示例,请参见如何使用内部框架

这是一个应用程序的图片,该应用程序创建了一个分层窗格并将重叠的,颜色为labels的重叠放置在不同的深度:

LayeredPaneDemo 的快照

Try this::

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

  • 在窗口的下部移动鼠标。 Duke 的图像拖到绿色和红色标签的后面,但在其他三个标签的前面。

  • 使用窗口顶部的组合框更改 Duke 的深度。使用复选框设置 Duke 是否在当前深度内位于最高位置(位置 0)。

以下是LayeredPaneDemo.java中创建分层窗格的代码:

layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300, 310));
layeredPane.setBorder(BorderFactory.createTitledBorder(
                                    "Move the Mouse to Move Duke"));
layeredPane.addMouseMotionListener(new MouseMotionAdapter() {
    ...
});

该代码使用JLayeredPane的唯一构造函数(无参数构造函数)来创建分层窗格。其余代码使用从超类继承的方法为分层窗格提供首选的大小和边框,并为其添加鼠标移动侦听器。鼠标移动侦听器仅响应鼠标移动而移动 Duke 图像。尽管此处未显示代码,但是该示例将分层窗格添加到框架的内容窗格。

稍后我们将向您展示,您可以使用add方法将组件添加到分层窗格中。在将组件添加到分层窗格中时,您可以指定组件的深度,也可以指定其在其深度内的位置。演示程序中的分层窗格包含六个标签-五个彩色标签和第六个显示 Duke 图像的标签。如程序所示,组件的深度及其在该深度内的位置都可以动态更改。

本节的其余部分涵盖以下主题:

添加组件并设置组件深度

这是示例程序中的代码,该代码将彩色标签添加到分层窗格中:

for (int i = 0; i < ...number of labels...; i++) {
    JLabel label = createColoredLabel(...);
    layeredPane.add(label, new Integer(i));
    ...
}

您可以在程序的源代码中找到createColoredLabel方法的实现。它只是创建一个不透明的JLabel,并初始化为背景颜色,边框,一些文本和大小。

该示例程序使用add方法的两个参数版本。第一个参数是要添加的组件,第二个参数是Integer对象,用于指定深度。该程序使用for循环迭代变量来指定深度。实际值无关紧要。重要的是深度的相对值,以及在程序中您如何使用每个深度是一致的。

Note:

如果使用根窗格的分层窗格,请确保使用其深度约定。有关详情,请参阅分层窗格。该部分向您展示如何修改LayeredPaneDemo以使用根窗格的分层窗格。通过修改,您可以在控制面板中看到拖动的 Duke 图像与组合框的关系。

从示例程序中可以看到,如果组件重叠,则深度较大的组件位于深度较小的组件的顶部。要动态更改组件深度,请使用setLayer方法。在该示例中,用户可以通过从组合框中进行选择来更改 Duke 的图层。这是在组合框上注册的动作侦听器的actionPerformed方法:

public void actionPerformed(ActionEvent e) {
    int position = onTop.isSelected() ? 0 : 1;
    layeredPane.setLayer(dukeLabel,
                         layerList.getSelectedIndex(),
                         position);
}

此处使用的setLayer方法采用三个参数:要设置深度的组件,新深度以及该深度内的位置。 JLayeredPane具有setLayer的两个参数版本,该版本仅采用组件和新深度。该方法将组件置于其深度的底部位置。

A note of caution:

将组件添加到分层窗格时,请使用Integer指定该层。使用setLayer更改组件的图层时,请使用int。您可能会认为,如果通过add方法使用int而不是Integer,则编译器会抱怨或您的程序将抛出非法的参数异常。但是编译器什么也没说,导致常见的分层窗格问题。您可以在本节末尾使用 API 表来检查参数的类型并返回处理图层的方法的值。

设置组件在其深度内的位置

以下代码创建显示 Duke 图像的标签,然后将标签添加到分层窗格中。

final ImageIcon icon = createImageIcon("images/dukeWaveRed.gif");
...
dukeLabel = new JLabel(icon);
...
dukeLabel.setBounds(15, 225,
                    icon.getIconWidth(),
                    icon.getIconHeight());
...
layeredPane.add(dukeLabel, new Integer(2), 0);

这段代码使用add方法的三参数版本。第三个参数指定 Duke 标签在其深度内的位置,该位置确定组件与相同深度处其他组件的关系。

在-1 和(* n -1)之间用int指定位置,其中 n 是深度处的分量数。与层编号不同,位置编号越小,组件在其深度内的位置越高。使用-1 与使用 n *-1 相同;它指示最底部的位置。使用 0 表示组件应位于其深度内的最高位置。如下图所示,除-1 以外,位置编号越小表示深度内的位置越高。

位置如何影响分层

组件在其层中的位置可以动态更改。在示例中,您可以使用复选框确定 Duke 标签是否在其深度的顶部。这是在复选框上注册的动作侦听器的actionPerformed方法:

public void actionPerformed(ActionEvent e) {
    if (onTop.isSelected())
        layeredPane.moveToFront(dukeLabel);
    else
        layeredPane.moveToBack(dukeLabel);
}

当用户选中复选框时,moveToFront方法将 Duke 移到前面(位置 0)。并且当用户取消选择复选框时,Duke 使用moveToBack方法移到了后面。您还可以使用setPosition方法或setLayer的三参数版本来更改组件的位置。

在分层窗格中布置组件

默认情况下,分层窗格没有布局 管理 器。这意味着您通常必须编写代码来确定放置在分层窗格中的组件的位置和大小。

该示例使用setBounds方法设置每个标签的大小和位置:

dukeLabel.setBounds(15, 225,
                    icon.getIconWidth(),
                    icon.getIconHeight());
...
label.setBounds(origin.x, origin.y, 140, 140);

当用户移动鼠标时,程序将调用setPosition来更改 Duke 的位置:

dukeLabel.setLocation(e.getX()-XFUDGE, e.getY()-YFUDGE);

尽管默认情况下分层窗格没有布局 管理 器,但是您仍然可以将布局 管理 器分配给分层窗格。 Java 平台提供的所有布局 管理 器都将组件排列在一起,就好像它们都在一层上一样。这是先前演示的一个版本,该版本将分层窗格的布局 管理 器设置为GridLayout实例,使用该布局 管理 器来布局六个彩色标签。

LayeredPaneDemo2 的快照

您可以在LayeredPaneDemo2.java中找到该程序的代码。您可以run LayeredPaneDemo2(下载 JDK 7 或更高版本)。如果要编译示例,请查阅example index以获取所有必需文件的列表。

许多程序使用中间容器(例如面板)及其布局 管理 器在同一层上布置组件,但使用绝对定位在不同层上布置组件。有关绝对定位的更多信息,请参见在没有布局 管理 器的情况下进行操作(绝对定位)

分层窗格 API

下表列出了常用的JLayeredPane构造函数和方法。您最有可能在JLayeredPane对象上调用的其他方法是从其超类继承的方法,例如setBordersetPreferredSize等。有关常用继承方法的表,请参见JComponent API

使用分层窗格的 API 分为以下几类:

创建或获取分层窗格

方法或构造函数Purpose
JLayeredPane()创建一个分层的窗格。
JLayeredPane getLayeredPane()

(在JAppletJDialogJFrameJInternalFrame中)
在 applet,对话框,框架或内部框架中获取自动分层的窗格。

Layering Components

MethodPurpose
void add(Component)

void add(组件,对象)
无效 add(Component,Object,int)
将指定的组件添加到分层窗格中。第二个参数(如果存在)是Integer,指示该图层。第三个参数(如果存在)指示组件在其层中的位置。如果您使用此方法的单参数版本,则该组件将添加到第 0 层。如果您使用此方法的一或两个参数版本,则该组件将放置在当前位于同一层的所有其他组件下方。
void setLayer(Component,int)
void setLayer(Component,int,int)
更改组件的图层。第二个参数指示层。第三个参数(如果存在)指示组件在其层中的位置。
int getLayer(Component)
int getLayer(JComponent)
获取指定组件的图层。
int getComponentCountInLayer(int)获取指定层中的组件数。通过此方法返回的值对于计算位置值很有用。
Component[] getComponentsInLayer(int)获取指定层中所有组件的数组。
int highestLayer()
int lowestLayer()
计算当前正在使用的最高或最低层。

设置组件的层内位置

MethodPurpose
void setPosition(Component,int)

int getPosition(Component)
设置或获取指定组件在其层中的位置。
void moveToFront(Component)
void moveToBack(Component)
将指定的组件移动到其层的前面或后面。

使用分层窗格的示例

下表显示了使用JLayeredPane的示例,并在其中描述了这些示例。

ExampleWhere DescribedNotes
LayeredPaneDemoThis section说明JLayeredPane的层和层内位置。
LayeredPaneDemo2This section使用布局 管理 器来帮助在分层窗格中布置组件。
RootLayeredPaneDemo分层窗格LayeredPaneDemo版本已修改为使用根窗格的分层窗格。
InternalFrameDemo如何使用内部框架使用JDesktopFrame管理 内部框架。