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 中的布局管理。