本教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
有关所有JDK版本的新功能、增强功能以及删除或弃用选项的信息,请参阅JDK发行说明。
分层窗格是一个Swing容器,为组件提供了第三个维度:深度,也称为Z顺序。当向分层窗格中添加组件时,您需要指定其深度为一个整数。数字越大,组件离容器的“顶部”位置越近。如果组件重叠,那些“更近”的组件会在较低深度的组件上面绘制。在相同深度的组件之间的关系取决于它们在深度内的位置。
每个拥有根窗格的Swing容器 —— 如JFrame
、JApplet
、JDialog
或JInternalFrame
—— 都自动拥有一个分层窗格。大多数程序不会显式地使用根窗格的分层窗格,因此本节不会讨论它。您可以在根窗格中找到有关它的信息,它提供了一个概述,以及在分层窗格中提供了更详细的信息。本节将告诉您如何创建自己的分层窗格,并在任何可以使用常规Swing容器的地方使用它。
Swing提供了两个分层窗格类。第一个是JLayeredPane
,这是根窗格使用的类,也是本节示例中使用的类。第二个是JDesktopPane
,它是一个JLayeredPane
子类,专门用于容纳内部窗格。有关使用JDesktopPane
的示例,请参阅如何使用内部窗格。
下面是一个应用程序的图片,它创建了一个分层窗格,并在不同深度上放置了重叠的、带颜色的标签:
以下是创建层次面板的代码,来自LayeredPaneDemo.java
:
layeredPane = new JLayeredPane(); layeredPane.setPreferredSize(new Dimension(300, 310)); layeredPane.setBorder(BorderFactory.createTitledBorder( "移动鼠标以移动Duke")); layeredPane.addMouseMotionListener(new MouseMotionAdapter() { ... });
代码使用JLayeredPane
的唯一构造函数(无参构造函数)创建层次面板。其余的代码使用从父类继承的方法给层次面板设置了首选大小和边框,并添加了一个鼠标移动监听器。鼠标移动监听器只是根据鼠标移动来移动Duke的图像。尽管我们没有在这里显示代码,但示例将层次面板添加到了框架的内容面板中。
稍后我们将向您展示,您可以使用add
方法将组件添加到层次面板中。在向层次面板添加组件时,您需要指定组件的深度,以及可选的深度内位置。演示程序中的层次面板包含了六个标签,其中包括五个带颜色的标签和一个显示Duke图像的标签。正如程序演示的那样,组件的深度和深度内的位置都可以动态改变。
本节的其余部分涵盖以下主题:
这是示例程序中将彩色标签添加到分层面板的代码:
for (int i = 0; i < ...标签数量...; i++) { JLabel label = createColoredLabel(...); layeredPane.add(label, new Integer(i)); ... }
你可以在程序的源代码中找到createColoredLabel
方法的实现。它只是创建一个带有背景颜色、边框、文本和大小的不透明JLabel
。
示例程序使用了add
方法的两个参数版本。第一个参数是要添加的组件,第二个参数是一个Integer
对象,指定深度。该程序使用for
循环迭代变量来指定深度。实际的值并不重要,重要的是深度的相对值以及在程序中使用每个深度时的一致性。
如果你使用根窗格的分层面板,请确保使用它的深度约定。有关详细信息,请参见分层面板。该部分向你展示如何修改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
方法。该方法将组件放置在其深度的底部位置。
在将组件添加到分层面板时,您需要用一个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.java中找到此程序的代码。您可以运行LayeredPaneDemo2(下载JDK 7或更高版本)。如果您想编译示例,请查阅示例索引获取所有必要的文件列表。
许多程序使用中间容器(如面板)和它们的布局管理器在同一层上布置组件,但使用绝对定位在不同层上布置组件。有关绝对定位的更多信息,请参见没有布局管理器(绝对定位)。
以下表列出了常用的JLayeredPane
构造方法和方法。您最有可能在JLayeredPane
对象上调用的其他方法是从其父类继承的方法,例如setBorder
,setPreferredSize
等等。有关常用继承方法的表格,请参见JComponent API。
使用分层面板的API可以分为以下几个类别:
方法或构造函数 | 用途 |
---|---|
JLayeredPane() | 创建一个分层面板。 |
JLayeredPane getLayeredPane() (在 JApplet 、JDialog 、JFrame 和JInternalFrame 中) |
获取应用程序、对话框、框架或内部框架中的自动分层面板。 |
方法 | 目的 |
---|---|
void add(Component) void add(Component, Object) void add(Component, Object, int) |
将指定的组件添加到分层窗格。当存在第二个参数时,它是一个整数,表示层级。当存在第三个参数时,它表示组件在其层级中的位置。如果使用此方法的一个参数版本,则将组件添加到第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() |
计算当前正在使用的最高或最低层级。 |
方法 | 目的 |
---|---|
void setPosition(Component, int) int getPosition(Component) |
设置或获取指定组件在其层中的位置。 |
void moveToFront(Component) void moveToBack(Component) |
将指定组件移动到其层的前面或后面。 |
此表格显示了使用 JLayeredPane
的示例以及这些示例的描述。
示例 | 描述位置 | 备注 |
---|---|---|
LayeredPaneDemo |
本节 | 演示了 JLayeredPane 的图层和同一层位置。 |
LayeredPaneDemo2 |
本节 | 使用布局管理器来帮助布局分层窗格中的组件。 |
RootLayeredPaneDemo |
分层窗格 | 使用根窗格的分层窗格修改了 LayeredPaneDemo 。 |
InternalFrameDemo |
使用内部框架 | 使用 JDesktopFrame 来管理内部框架。 |