Swing 布局管理
最后修改于 2023 年 1 月 10 日
Java Swing 有两种组件:容器和子组件。容器将子组件分组到合适的布局中。为了创建布局,我们使用*布局管理器*。
ZetCode 提供了一本专门的 196 页 电子书,用于 Swing 布局管理过程:
Swing 布局管理器
Swing 拥有大量的布局管理器——内置和第三方。然而,大多数管理器不适用于现代 UI 的创建。
有三个布局管理器可以正确地完成这项工作
- MigLayout
- GroupLayout
- FormLayout
MigLayout
、GroupLayout
和 FormLayout
是强大、灵活的布局管理器,可以满足大多数布局要求。在本教程中,我们使用 GroupLayout
管理器来设计用户界面。
以下布局管理器已过时
- FlowLayout
- GridLayout
- CardLayout
- BoxLayout
- GridBagLayout
这些布局管理器无法满足现代 UI 的要求。
过时管理器的缺点
过时的管理器要么过于简单 (FlowLayout
, GridLayout
),要么不必要地复杂 (GridBagLayout
)。所有这些管理器都有一个基本的错误设计:*它们使用组件之间的固定间隙*。使用组件之间的刚性空间并不可移植:一旦程序在不同的屏幕分辨率上运行,用户界面就会被破坏。
过时的管理器试图通过一种称为嵌套的技术来修复它们的弱点。在嵌套中,开发人员在多个面板中使用几个不同的布局管理器。虽然可以使用嵌套创建 UI,但它给代码带来了不必要的复杂性。
过时的管理器
在本节中,我们将介绍过时的布局管理器。不推荐使用这些管理器。只有在需要维护一些遗留代码时,才花一些时间来研究它们。否则,应该拒绝使用它们。
FlowLayout 管理器
这是 Java Swing 工具包中最简单的布局管理器。它是 JPanel
组件的默认布局管理器。
它非常简单,无法用于任何实际的布局。这个管理器在许多 Java Swing 教程中都有介绍,因此,初学者试图在他们的项目中也使用它,但没有意识到它不能用于任何严肃的事情。
在计算其子组件大小时,流式布局允许每个组件采用其自然 (首选) 的大小。该管理器将组件放入一行。按照添加的顺序。如果它们不适合一行,则进入下一行。组件可以从右向左或从左向右添加。该管理器允许对齐组件。隐式地,组件居中放置,并且组件之间以及组件和容器边缘之间有 5px 的空间。
FlowLayout() FlowLayout(int align) FlowLayout(int align, int hgap, int vgap)
FlowLayout 管理器有三个构造函数可用。第一个构造函数使用隐式值创建一个管理器。居中对齐,水平和垂直间距均为 5px。其他构造函数允许指定这些参数。
package com.zetcode; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTree; import java.awt.Dimension; import java.awt.EventQueue; public class FlowLayoutEx extends JFrame { public FlowLayoutEx() { initUI(); } private void initUI() { var panel = new JPanel(); var button = new JButton("button"); panel.add(button); var tree = new JTree(); panel.add(tree); var area = new JTextArea("text area"); area.setPreferredSize(new Dimension(100, 100)); panel.add(area); add(panel); pack(); setTitle("FlowLayout example"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new FlowLayoutEx(); ex.setVisible(true); }); } }
该示例显示窗口中的一个按钮、一个树组件和一个文本区域组件。如果我们创建一个空的树组件,则该组件内部有一些默认值。
var panel = new JPanel();
JPanel
组件的隐式布局管理器是 FlowLayout
。我们不必手动设置它。
var area = new JTextArea("text area"); area.setPreferredSize(new Dimension(100, 100));
流式布局管理器为其组件设置了*首选*大小。因此,在我们的例子中,区域组件将是 100x100px。如果我们没有设置首选大小,组件将具有其文本的大小。如果没有文本,组件将根本不可见。尝试在区域组件中写入或删除一些文本。组件将相应地增长和缩小。
panel.add(area);
该组件使用 add()
放置在容器内。

GridLayout
GridLayout
布局管理器将组件布置在一个矩形网格中。容器被分成大小相等的矩形。每个矩形放置一个组件。
GridLayout
非常简单,不能用于任何实际的布局。
package com.zetcode; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import java.awt.EventQueue; import java.awt.GridLayout; public class GridLayoutEx extends JFrame { public GridLayoutEx() { initUI(); } private void initUI() { var panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); panel.setLayout(new GridLayout(5, 4, 5, 5)); String[] buttons = { "Cls", "Bck", "", "Close", "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }; for (int i = 0; i < buttons.length; i++) { if (i == 2) { panel.add(new JLabel(buttons[i])); } else { panel.add(new JButton(buttons[i])); } } add(panel); setTitle("GridLayout"); setSize(350, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new GridLayoutEx(); ex.setVisible(true); }); } }
该示例显示了一个简单计算器工具的框架。我们将 19 个按钮和一个标签放入管理器中。请注意,每个按钮的大小都相同。
panel.setLayout(new GridLayout(5, 4, 5, 5));
在这里,我们为面板组件设置了网格布局管理器。布局管理器接受四个参数。行数、列数以及组件之间的水平和垂直间隙。

BorderLayout
BorderLayout
是一个简单的布局管理器,在某些布局中可能很有用。它是 JFrame
、JWindow
、JDialog
、JInternalFrame
和 JApplet
的默认布局管理器。它有一个严重的限制——它以像素为单位设置其子组件之间的间隙,从而创建刚性布局。这导致 UI 不可移植,因此不推荐使用。
BorderLayout
将空间划分为五个区域:北、西、南、东和中心。每个区域只能有一个组件。如果我们需要将更多组件放入一个区域,我们必须在那里放置一个带有我们选择的管理器 的面板。N、W、S、E 区域中的组件获得它们的*首选*大小。中心的组件占据剩余的整个空间。
如果子组件彼此靠得太近,它看起来不太好。我们必须在它们之间留一些空间。Swing 工具包中的每个组件都可以在其边缘周围添加边框。要创建边框,我们既可以创建一个新的 EmptyBorder
类实例,也可以使用 BorderFactory
。
package com.zetcode; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Insets; public class BorderLayoutEx extends JFrame { public BorderLayoutEx() { initUI(); } private void initUI() { var bottomPanel = new JPanel(new BorderLayout()); var topPanel = new JPanel(); topPanel.setBackground(Color.gray); topPanel.setPreferredSize(new Dimension(250, 150)); bottomPanel.add(topPanel); bottomPanel.setBorder(new EmptyBorder(new Insets(20, 20, 20, 20))); add(bottomPanel); pack(); setTitle("BorderLayout"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new BorderLayoutEx(); ex.setVisible(true); }); } }
该示例将显示一个灰色的面板及其周围的边框。
var bottomPanel = new JPanel(new BorderLayout()); var topPanel = new JPanel();
我们将一个面板放置到另一个面板中。底面板具有 BorderLayout
管理器。
bottomPanel.add(topPanel);
在这里,我们将顶部面板放置到底部面板组件中。更准确地说,我们将其放置在其 BorderLayout
管理器的中心区域中。
bottomPanel.setBorder(new EmptyBorder(new Insets(20, 20, 20, 20)));
在这里,我们在底面板周围创建了一个 20px 的边框。边框值如下:顶部、左侧、底部和右侧。请注意,创建固定内边距(空格)是不可移植的。

下一个示例显示了 BorderLayout
管理器的典型用法。
package com.zetcode; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JTextArea; import javax.swing.JToolBar; import javax.swing.border.EmptyBorder; import java.awt.BorderLayout; import java.awt.EventQueue; import java.awt.Insets; public class BorderLayoutEx2 extends JFrame { public BorderLayoutEx2() { initUI(); } private void initUI() { var menubar = new JMenuBar(); var fileMenu = new JMenu("File"); menubar.add(fileMenu); setJMenuBar(menubar); var toolbar = new JToolBar(); toolbar.setFloatable(false); var exitIcon = new ImageIcon("src/resources/exit.png"); var exitBtn = new JButton(exitIcon); exitBtn.setBorder(new EmptyBorder(0, 0, 0, 0)); toolbar.add(exitBtn); add(toolbar, BorderLayout.NORTH); var vertical = new JToolBar(JToolBar.VERTICAL); vertical.setFloatable(false); vertical.setMargin(new Insets(10, 5, 5, 5)); var driveIcon = new ImageIcon("src/resources/drive.png"); var compIcon = new ImageIcon("src/resources/computer.png"); var printIcon = new ImageIcon("src/resources/printer.png"); var driveBtn = new JButton(driveIcon); driveBtn.setBorder(new EmptyBorder(3, 0, 3, 0)); var compBtn = new JButton(compIcon); compBtn.setBorder(new EmptyBorder(3, 0, 3, 0)); var printBtn = new JButton(printIcon); printBtn.setBorder(new EmptyBorder(3, 0, 3, 0)); vertical.add(driveBtn); vertical.add(compBtn); vertical.add(printBtn); add(vertical, BorderLayout.WEST); add(new JTextArea(), BorderLayout.CENTER); var statusbar = new JLabel(" Statusbar"); add(statusbar, BorderLayout.SOUTH); setSize(400, 350); setTitle("BorderLayout"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new BorderLayoutEx2(); ex.setVisible(true); }); } }
该示例显示了一个典型的应用程序框架。我们显示了一个垂直和水平工具栏、一个状态栏和一个中心组件(一个文本区域)。
BorderLayout
是 JFrame
容器的默认布局管理器。所以我们不必显式地设置它。
add(toolbar, BorderLayout.NORTH);
我们将工具栏放置在布局的上方。
var driveBtn = new JButton(driveIcon); driveBtn.setBorder(new EmptyBorder(3, 0, 3, 0));
为了在按钮周围留出一些空白空间,我们必须使用 EmptyBorder
。这为按钮的顶部和底部添加了一些固定空间。当我们添加固定空间时,UI 不可移植。在 1280x720 屏幕上,3 像素的空间可能看起来很好,但在 1920x1200 像素的屏幕上,它是不合适的。
add(vertical, BorderLayout.WEST);
我们将垂直工具栏放置在西侧。
add(new JTextArea(), BorderLayout.CENTER);
我们将文本区域放置在中心。
add(statusbar, BorderLayout.SOUTH);
状态栏进入南侧区域。

CardLayout
CardLayout
是一个简单的布局管理器,它将每个组件视为一张卡片。容器是这些卡片的堆叠。一次只显示一个组件;其余的被隐藏。添加到容器的第一个组件在容器最初显示时默认可见。此管理器的实际用途有限。它可用于创建向导或选项卡窗格。
以下示例使用 CardLayout 管理器创建一个图像库。我们使用了四张克拉斯纳霍尔卡城堡的图片(2012 年火灾之前)。
package com.zetcode; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.EventQueue; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class CardLayoutEx extends JFrame { private ImageIcon horka1; private ImageIcon horka2; private ImageIcon horka3; private ImageIcon horka4; private ImageIcon previ; private ImageIcon nexti; private JPanel mainPanel; private CardLayout cardLayout; public CardLayoutEx() { initUI(); } private void initUI() { mainPanel = new JPanel(); mainPanel.setBackground(new Color(50, 50, 50)); mainPanel.setBorder( BorderFactory.createEmptyBorder(5, 5, 5, 5) ); cardLayout = new CardLayout(); mainPanel.setLayout(cardLayout); horka1 = new ImageIcon("src/resources/horka1.jpg"); horka2 = new ImageIcon("src/resources/horka2.jpg"); horka3 = new ImageIcon("src/resources/horka3.jpg"); horka4 = new ImageIcon("src/resources/horka4.jpg"); previ = new ImageIcon("src/resources/previous.png"); nexti = new ImageIcon("src/resources/next.png"); var label1 = new JLabel(horka1); var label2 = new JLabel(horka2); var label3 = new JLabel(horka3); var label4 = new JLabel(horka4); mainPanel.add(label1); mainPanel.add(label2); mainPanel.add(label3); mainPanel.add(label4); add(mainPanel); var prevButton = new JButton(previ); prevButton.addActionListener((e) -> cardLayout.previous(mainPanel)); var nextButton = new JButton(nexti); nextButton.addActionListener((e) -> cardLayout.next(mainPanel)); var btnPanel = new JPanel(); btnPanel.setBackground(new Color(50, 50, 50)); btnPanel.add(prevButton); btnPanel.add(nextButton); add(btnPanel, BorderLayout.SOUTH); pack(); setTitle("Gallery"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new CardLayoutEx(); ex.setVisible(true); }); } }
我们创建了两个按钮来浏览图片。
mainPanel = new JPanel(); mainPanel.setBackground(new Color(50, 50, 50)); mainPanel.setBorder( BorderFactory.createEmptyBorder(5, 5, 5, 5) );
我们创建了主面板组件。我们将它的颜色设置为深灰色。我们在面板周围放置 5px,这样它的子组件就不会离窗口的边框太近。
cardLayout = new CardLayout(); mainPanel.setLayout(cardLayout);
创建 CardLayout
管理器并将其设置为主面板。
mainPanel.add(label1); mainPanel.add(label2); mainPanel.add(label3); mainPanel.add(label4);
显示图像的标签组件被添加到面板中。
var prevButton = new JButton(previ); prevButton.addActionListener((e) -> cardLayout.previous(mainPanel));
单击“上一个”按钮将调用管理器的 previous()
方法。它翻转到指定容器的上一张卡片。
add(mainPanel);
我们将主面板添加到框架组件的边框布局的中心区域。如果我们没有明确指定放置组件的位置,则将其添加到中心区域。
var btnPanel = new JPanel(); btnPanel.setBackground(new Color(50, 50, 50)); btnPanel.add(prevButton); btnPanel.add(nextButton);
按钮被添加到按钮面板。
add(btnPanel, BorderLayout.SOUTH);
最后,带有按钮的面板被放置到 BorderLayout
管理器的南侧区域。

BoxLayout
BoxLayout
管理器是一个简单的布局管理器,它以列或行的形式组织组件。它可以通过嵌套创建相当复杂的布局。但是,这增加了布局创建的复杂性,并使用了额外的资源,特别是许多其他 JPanel
组件。BoxLayout
只能创建固定空间;因此,它的布局不可移植。
BoxLayout
具有以下构造函数
BoxLayout(Container target, int axis)
构造函数创建一个布局管理器,该管理器将沿给定的轴布置组件。与其他布局管理器不同,BoxLayout
将容器实例作为构造函数中的第一个参数。第二个参数确定管理器的方向。要创建一个水平框,我们可以使用 LINE_AXIS
常量。要创建一个垂直框,我们可以使用 PAGE_AXIS
常量。
框布局管理器通常与 Box
类一起使用。这个类创建了几个不可见的组件,这些组件会影响最终的布局。
- glue
- strut
- rigid area
假设我们想将两个按钮放到窗口的右下角。
package com.zetcode; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import java.awt.Dimension; import java.awt.EventQueue; public class BoxLayoutButtonsEx extends JFrame { public BoxLayoutButtonsEx() { initUI(); } private void initUI() { var basePanel = new JPanel(); basePanel.setLayout(new BoxLayout(basePanel, BoxLayout.Y_AXIS)); add(basePanel); basePanel.add(Box.createVerticalGlue()); var bottomPanel = new JPanel(); bottomPanel.setAlignmentX(1f); bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS)); var okBtn = new JButton("OK"); var closeBtn = new JButton("Close"); bottomPanel.add(okBtn); bottomPanel.add(Box.createRigidArea(new Dimension(5, 0))); bottomPanel.add(closeBtn); bottomPanel.add(Box.createRigidArea(new Dimension(15, 0))); basePanel.add(bottomPanel); basePanel.add(Box.createRigidArea(new Dimension(0, 15))); setTitle("Two Buttons"); setSize(300, 150); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new BoxLayoutButtonsEx(); ex.setVisible(true); }); } }
以下绘图说明了该示例。

我们创建了两个面板。底面板具有垂直框布局。底面板有一个水平框布局。我们将底面板放入基面板。底面板右对齐。窗口顶部和底面板之间的空间是可扩展的。这是通过垂直胶水实现的。
basePanel.setLayout(new BoxLayout(basePanel, BoxLayout.Y_AXIS));
在这里,我们使用垂直 BoxLayout
创建一个基面板。
var bottomPanel = new JPanel(); bottomPanel.setAlignmentX(1f); bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
底面板右对齐。这是通过 setAlignmentX()
方法完成的。面板具有水平布局。
bottomPanel.add(Box.createRigidArea(new Dimension(5, 0)));
我们在按钮之间放置一些刚性空间。
basePanel.add(bottomPanel);
在这里,我们将带有水平框布局的底面板放入垂直基面板。
basePanel.add(Box.createRigidArea(new Dimension(0, 15)));
我们还在底面板和窗口边框之间留出了一些空间。

当我们使用 BoxLayout
管理器时,我们可以在组件之间设置一个刚性区域。
package com.zetcode; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Insets; public class BoxLayoutRigidAreaEx extends JFrame { public BoxLayoutRigidAreaEx() { initUI(); } private void initUI() { var basePanel = new JPanel(); basePanel.setLayout(new BoxLayout(basePanel, BoxLayout.Y_AXIS)); basePanel.setBorder(new EmptyBorder(new Insets(40, 60, 40, 60))); basePanel.add(new JButton("Button")); basePanel.add(Box.createRigidArea(new Dimension(0, 5))); basePanel.add(new JButton("Button")); basePanel.add(Box.createRigidArea(new Dimension(0, 5))); basePanel.add(new JButton("Button")); basePanel.add(Box.createRigidArea(new Dimension(0, 5))); basePanel.add(new JButton("Button")); add(basePanel); pack(); setTitle("RigidArea"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new BoxLayoutRigidAreaEx(); ex.setVisible(true); }); } }
在此示例中,我们显示了四个按钮。默认情况下,按钮之间没有空间。要在它们之间放置一些空间,我们添加一些刚性区域。
basePanel.setLayout(new BoxLayout(basePanel, BoxLayout.Y_AXIS));
我们为面板使用垂直 BoxLayout
管理器。
basePanel.add(new JButton("Button")); basePanel.add(Box.createRigidArea(new Dimension(0, 5))); basePanel.add(new JButton("Button"));
我们添加按钮并在它们之间使用 Box.createRigidArea()
创建一个刚性区域。

每日提示
下一个示例创建了一个每日提示窗口对话框。我们使用了各种布局管理器的组合。
package com.zetcode; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JTextPane; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.event.KeyEvent; public class TipOfDayEx extends JDialog { public TipOfDayEx() { initUI(); } private void initUI() { var basePanel = new JPanel(); basePanel.setLayout(new BoxLayout(basePanel, BoxLayout.Y_AXIS)); add(basePanel); var topPanel = new JPanel(new BorderLayout(0, 0)); topPanel.setMaximumSize(new Dimension(450, 0)); var hint = new JLabel("Productivity Hints"); hint.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 0)); topPanel.add(hint); var icon = new ImageIcon("src/resources/coffee2.png"); var label = new JLabel(icon); label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); topPanel.add(label, BorderLayout.EAST); var separator = new JSeparator(); separator.setForeground(Color.gray); topPanel.add(separator, BorderLayout.SOUTH); basePanel.add(topPanel); var textPanel = new JPanel(new BorderLayout()); textPanel.setBorder(BorderFactory.createEmptyBorder(15, 25, 15, 25)); var pane = new JTextPane(); pane.setContentType("text/html"); var text = "<p><b>Closing windows using the mouse wheel</b></p>" + "<p>Clicking with the mouse wheel on an editor tab closes the window. " + "This method works also with dockable windows or Log window tabs.</p>"; pane.setText(text); pane.setEditable(false); textPanel.add(pane); basePanel.add(textPanel); var boxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 0)); var box = new JCheckBox("Show Tips at startup"); box.setMnemonic(KeyEvent.VK_S); boxPanel.add(box); basePanel.add(boxPanel); var bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); var tipBtn = new JButton("Next Tip"); tipBtn.setMnemonic(KeyEvent.VK_N); var closeBtn = new JButton("Close"); closeBtn.setMnemonic(KeyEvent.VK_C); bottomPanel.add(tipBtn); bottomPanel.add(closeBtn); basePanel.add(bottomPanel); bottomPanel.setMaximumSize(new Dimension(450, 0)); setTitle("Tip of the Day"); setSize(new Dimension(450, 350)); setResizable(false); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new TipOfDayEx(); ex.setVisible(true); }); } }
该示例使用混合的布局管理器。简单地说,我们将四个面板放入垂直组织的基面板中。
var basePanel = new JPanel(); basePanel.setLayout(new BoxLayout(basePanel, BoxLayout.Y_AXIS)); add(basePanel);
这是最底部的面板。它有一个垂直框布局管理器。基面板被添加到默认的 JDialog
组件中。此组件默认具有边框布局管理器。
var topPanel = new JPanel(new BorderLayout(0, 0));
topPanel 面板有一个边框布局管理器。我们将在其中放入三个组件。两个标签和一个分隔符。
topPanel.setMaximumSize(new Dimension(450, 0));
如果我们希望面板不大于其组件,则必须设置其最大大小。忽略零值。管理器计算必要的高度。
var textPanel = new JPanel(new BorderLayout()); ... textPanel.add(pane);
文本窗格组件被添加到边框布局管理器的中心区域。它占据了所有剩余的空间。确切地说,正如我们希望的那样。
var boxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 0));
复选框显示在 boxPanel 面板中。它左对齐。流式布局管理器具有 20px 的水平间隙。其他组件有 25px。为什么会这样?这是因为流式布局管理器也会在组件和边缘之间留出一些空间。
var bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); ... bottomPanel.setMaximumSize(new Dimension(450, 0));
底面板显示两个按钮。它有一个右对齐的流式布局管理器。为了在对话框的右边缘显示按钮,面板必须从头到尾水平延伸。

没有管理器
可以不使用布局管理器。在少数情况下,我们可能不需要布局管理器。(也许将几个图像放置在一些不规则的位置。)但在大多数情况下,为了创建真正可移植的复杂应用程序,我们需要布局管理器。
如果没有布局管理器,我们使用绝对值来定位组件。
package com.zetcode; import javax.swing.JButton; import javax.swing.JFrame; import java.awt.EventQueue; public class AbsoluteLayoutEx extends JFrame { public AbsoluteLayoutEx() { initUI(); } private void initUI() { setLayout(null); var okBtn = new JButton("OK"); okBtn.setBounds(50, 50, 80, 25); var closeBtn = new JButton("Close"); closeBtn.setBounds(150, 50, 80, 25); add(okBtn); add(closeBtn); setTitle("Absolute positioning"); setSize(300, 250); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new AbsoluteLayoutEx(); ex.setVisible(true); }); } }
这个简单的例子显示了两个按钮。
setLayout(null);
我们通过向 setLayout()
方法提供 null
来使用绝对定位。(JFrame
组件有一个默认的布局管理器,即 BorderLayout
。)
okBtn.setBounds(50, 50, 80, 25);
setBounds()
方法定位“确定”按钮。参数是组件的 x 和 y 坐标以及宽度和高度。

在本章中,我们提到了 Swing 中的布局管理。