基本的 Swing 组件
最后修改日期:2024 年 7 月 10 日
Swing 组件是应用程序的基本构建块。Swing 拥有各种各样的组件,包括按钮、复选框、滑块和列表框。
在本 Swing 教程中,我们将介绍 JButton、JLabel、JTextField 和 JPasswordField。
JButton
JButton 是一个推送按钮的实现。如果用户点击它,它用于触发一个动作。
显示文本和图标
JButton 可以显示文本、图标或两者都显示。
package com.zetcode;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import java.awt.EventQueue;
public class ImageIconButtonEx extends JFrame {
public ImageIconButtonEx() {
initUI();
}
private void initUI() {
var saveIcon = new ImageIcon("src/resources/save.png");
var homeIcon = new ImageIcon("src/resources/home.png");
var quitBtn = new JButton("Quit");
var saveBtn = new JButton(saveIcon);
var homeBtn = new JButton("Home", homeIcon);
createLayout(quitBtn, saveBtn, homeBtn);
setTitle("JButtons");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2])
);
gl.setVerticalGroup(gl.createParallelGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2])
);
gl.linkSize(arg[0], arg[1], arg[2]);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
var ex = new ImageIconButtonEx();
ex.setVisible(true);
});
}
}
该示例显示了三个按钮:一个显示文本,一个显示图标,一个同时显示文本和图标。
var saveIcon = new ImageIcon("src/main/resources/save.png");
许多组件都可以用图标装饰;为此,我们使用 ImageIcon 类。
var quitBtn = new JButton("Quit");
此 JButton 构造函数接受一个文本作为参数。
var saveBtn = new JButton(saveIcon);
在此 JButton 构造函数中,我们传递一个图标。
JButton homeBtn = new JButton("Home", homeIcon);
此按钮显示文本和图标。
gl.linkSize(arg[0], arg[1], arg[2]);
使用 GroupLayout 的 linkSize 方法,我们使按钮具有相同的大小。
带有助记符的 JButton
一个 助记符 是一把按键,当与外观和感觉的无鼠标修饰符(通常是 Alt)组合时,如果焦点包含在此按钮的祖先窗口中的某个位置,则将激活此按钮。
package com.zetcode;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class ButtonMnemonicEx extends JFrame implements ActionListener {
public ButtonMnemonicEx() {
initUI();
}
private void initUI() {
var showBtn = new JButton("Show");
showBtn.addActionListener(this);
showBtn.setMnemonic(KeyEvent.VK_S);
createLayout(showBtn);
setTitle("JButton");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
.addGap(250)
);
gl.setVerticalGroup(gl.createParallelGroup()
.addComponent(arg[0])
.addGap(150)
);
pack();
}
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this, "Button clicked",
"Information", JOptionPane.INFORMATION_MESSAGE);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
var ex = new ButtonMnemonicEx();
ex.setVisible(true);
});
}
}
在此示例中,可以使用鼠标单击或 Alt + S 键盘快捷键激活该按钮。
public class ButtonMnemonicEx extends JFrame implements ActionListener
ButtonMnemonicEx 类实现了 ActionListener;它必须重写 actionPerformed 方法,我们在其中放置在按钮被激活后执行的代码。
var showBtn = new JButton("Show");
showBtn.addActionListener(this);
创建了一个新的 JButton。我们使用 addActionListener 方法向按钮添加一个动作监听器。
showBtn.setMnemonic(KeyEvent.VK_S);
setMnemonic 设置助记键;'S' 字符带有下划线。
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(this, "Button clicked",
"Information", JOptionPane.INFORMATION_MESSAGE);
}
当按钮被激活时,无论是通过鼠标单击还是通过快捷方式,都会使用 JOptionPane.showMessageDialog 显示一个消息对话框。
JLabel
JLabel 是一个用于显示文本、图像或两者的简单组件。它不会对输入事件做出反应。
显示文本
以下示例显示文本。
package com.zetcode;
import javax.swing.GroupLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
public class LabelEx extends JFrame {
public LabelEx() {
initUI();
}
private void initUI() {
var lyrics = """
<html>It's way too late to think of<br>
Someone I would call now<br>
And neon signs got tired<br>
Red eye flights help the stars out<br>
I'm safe in a corner<br>
Just hours before me<br>
<br>
I'm waking with the roaches<br>
The world has surrendered<br>
I'm dating ancient ghosts<br>
The ones I made friends with<br>
The comfort of fireflies<br>
Long gone before daylight<br>
<br>
And if I had one wishful field tonight<br>
I'd ask for the sun to never rise<br>
If God leant his voice for me to speak<br>
I'd say go to bed, world<br>
<br>
I've always been too late<br>
To see what's before me<br>
And I know nothing sweeter than<br>
Champaign from last New Years<br>
Sweet music in my ears<br>
And a night full of no fears<br>
<br>
But if I had one wishful field tonight<br>
I'd ask for the sun to never rise<br>
If God passed a mic to me to speak<br>
I'd say stay in bed, world<br>
Sleep in peace</html>""";
var label = new JLabel(lyrics);
label.setFont(new Font("Serif", Font.PLAIN, 14));
label.setForeground(new Color(50, 50, 25));
createLayout(label);
setTitle("No Sleep");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
);
gl.setVerticalGroup(gl.createParallelGroup()
.addComponent(arg[0])
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
var ex = new LabelEx();
ex.setVisible(true);
});
}
}
在我们的示例中,我们显示了 Cardigans 的一首歌词。我们可以在 JLabel 组件中使用 HTML 标签。我们使用 <br> 标签来分隔行。
var label = new JLabel(lyrics);
label.setFont(new Font("Serif", Font.PLAIN, 14));
在这里,我们创建一个标签组件。我们选择一个普通的 Serif 字体,并将其高度设置为 14px。
pack();
pack 方法调整窗口大小,以便在其首选大小中显示标签组件。
显示图标
JLabel 可用于显示图像。
package com.zetcode;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.EventQueue;
public class LabelEx2 extends JFrame {
public LabelEx2() {
initUI();
}
private void initUI() {
var lbl1 = new JLabel(new ImageIcon("src/resources/cpu.png"));
var lbl2 = new JLabel(new ImageIcon("src/resources/drive.png"));
var lbl3 = new JLabel(new ImageIcon("src/resources/laptop.png"));
var lbl4 = new JLabel(new ImageIcon("src/resources/player.png"));
createLayout(lbl1, lbl2, lbl3, lbl4);
setTitle("Icons");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2])
.addComponent(arg[3])
);
gl.setVerticalGroup(gl.createParallelGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2])
.addComponent(arg[3])
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
var ex = new LabelEx2();
ex.setVisible(true);
});
}
}
在示例中,我们使用 JLabel 组件显示四个图标。
var lbl1 = new JLabel(new ImageIcon("src/main/resources/cpu.png"));
JLabel 接受 ImageIcon 作为参数。图标是固定大小的图像。ImageIcon 从 GIF、JPEG 或 PNG 图像绘制图标。
JTextField
JTextField 是一个文本组件,允许编辑单行非格式化的文本。
package com.zetcode;
import javax.swing.GroupLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import java.awt.EventQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JTextFieldEx extends JFrame {
private JLabel lbl;
public JTextFieldEx() {
initUI();
}
private void initUI() {
var field = new JTextField(15);
lbl = new JLabel();
field.getDocument().addDocumentListener(new MyDocumentListener());
createLayout(field, lbl);
setTitle("JTextField");
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private class MyDocumentListener implements DocumentListener {
private String text;
@Override
public void insertUpdate(DocumentEvent e) {
updateLabel(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
updateLabel(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
}
private void updateLabel(DocumentEvent e) {
var doc = e.getDocument();
int len = doc.getLength();
try {
text = doc.getText(0, len);
} catch (BadLocationException ex) {
Logger.getLogger(JTextFieldEx.class.getName()).log(
Level.WARNING, "Bad location", ex);
}
lbl.setText(text);
}
}
private void createLayout(JComponent... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateContainerGaps(true);
gl.setAutoCreateGaps(true);
gl.setHorizontalGroup(gl.createParallelGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addGap(250)
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(arg[0], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(arg[1])
.addGap(150)
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
var ex = new JTextFieldEx();
ex.setVisible(true);
});
}
}
在示例中,输入到 JTextField 中的文本会立即显示在标签组件中。
var field = new JTextField(15);
创建了新的 JTextField。参数是列数。请注意,此值不会设置字段中允许的字符数;该值用于计算字段的首选宽度。
field.getDocument().addDocumentListener(new MyDocumentListener());
我们向 JTextField 添加一个文档监听器。getDocument 方法获取与编辑器关联的模型。每个 Swing 组件都有一个模型,它管理其状态或数据。
@Override
public void insertUpdate(DocumentEvent e) {
updateLabel(e);
}
@Override
public void removeUpdate(DocumentEvent e) {
updateLabel(e);
}
insertUpdate 和 removeUpdate 方法调用 updateLabel 方法,该方法将文本从文本字段复制并将其设置为标签组件中。
@Override
public void changedUpdate(DocumentEvent e) {
}
我们对 changeUpdate 方法不感兴趣。此事件仅在样式文档中生成。
private void updateLabel(DocumentEvent e) {
var doc = e.getDocument();
int len = doc.getLength();
try {
text = doc.getText(0, len);
} catch (BadLocationException ex) {
Logger.getLogger(JTextFieldEx.class.getName()).log(
Level.WARNING, "Bad location", ex);
}
lbl.setText(text);
}
文档事件的 getDocument 方法用于获取正在观察的文本字段的文档。我们使用文档的 getLength 方法获取字符数。该值用于使用文档的 getText 方法复制文本。最后,文本使用标签的 setText 方法设置到标签。
gl.setVerticalGroup(gl.createSequentialGroup()
.addComponent(arg[0], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(arg[1])
.addGap(150)
);
我们不希望 JTextField 在垂直方向增长;因此,我们将它的最大值设置为垂直方向的 GroupLayout.PREFERRED_SIZE。
JPasswordField
JPasswordField 是 JTextField 的一个子类,它不显示用户键入的字符。
package com.zetcode;
import javax.swing.AbstractAction;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.Arrays;
import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
public class PasswordEx extends JFrame {
private JTextField loginField;
private JPasswordField passField;
public PasswordEx() {
initUI();
}
private void initUI() {
var lbl1 = new JLabel("Login");
var lbl2 = new JLabel("Password");
loginField = new JTextField(15);
passField = new JPasswordField(15);
var submitButton = new JButton("Submit");
submitButton.addActionListener(new SubmitAction());
createLayout(lbl1, loginField, lbl2, passField, submitButton);
setTitle("Login");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private class SubmitAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
doSubmitAction();
}
private void doSubmitAction() {
var login = loginField.getText();
var passwd = passField.getPassword();
if (!login.isEmpty() && passwd.length != 0) {
System.out.format("User %s entered %s password%n",
login, String.valueOf(passwd));
}
Arrays.fill(passwd, '0');
}
}
private void createLayout(Component... arg) {
var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);
gl.setAutoCreateGaps(true);
gl.setAutoCreateContainerGaps(true);
gl.setHorizontalGroup(gl.createSequentialGroup()
.addGap(50)
.addGroup(gl.createParallelGroup()
.addComponent(arg[0])
.addComponent(arg[1])
.addComponent(arg[2])
.addComponent(arg[3])
.addComponent(arg[4]))
.addGap(50)
);
gl.setVerticalGroup(gl.createSequentialGroup()
.addGap(50)
.addGroup(gl.createSequentialGroup()
.addComponent(arg[0])
.addComponent(arg[1], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addComponent(arg[2])
.addComponent(arg[3], GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(UNRELATED)
.addComponent(arg[4]))
.addGap(50)
);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
var ex = new PasswordEx();
ex.setVisible(true);
});
}
}
该示例具有一个文本字段、一个密码字段和一个按钮。该按钮打印用户输入的数据。
passField = new JPasswordField (15);
创建了 JPasswordField 的一个实例。
var passwd = passField.getPassword();
作为安全预防措施,密码字段将其值存储为字符数组,而不是字符串。字符数组由 getPassword 方法返回。旧的 getText 方法已被弃用。
Arrays.fill(passwd , '0');
在完成密码处理后,建议将数组的元素设置为零。
在本 Java Swing 教程中,我们介绍了基本的 Swing 组件,包括 JButton、JLabel、JTextField 和 JPasswordField。