ZetCode

基本的 Swing 组件

最后修改日期:2024 年 7 月 10 日

Swing 组件是应用程序的基本构建块。Swing 拥有各种各样的组件,包括按钮、复选框、滑块和列表框。

在本 Swing 教程中,我们将介绍 JButtonJLabelJTextFieldJPasswordField

JButton

JButton 是一个推送按钮的实现。如果用户点击它,它用于触发一个动作。

显示文本和图标

JButton 可以显示文本、图标或两者都显示。

com/zetcode/ImageIconButtonEx.java
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]);

使用 GroupLayoutlinkSize 方法,我们使按钮具有相同的大小。

JButtons
图:JButtons

带有助记符的 JButton

一个 助记符 是一把按键,当与外观和感觉的无鼠标修饰符(通常是 Alt)组合时,如果焦点包含在此按钮的祖先窗口中的某个位置,则将激活此按钮。

com/zetcode/ButtonMnemonicEx.java
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 是一个用于显示文本、图像或两者的简单组件。它不会对输入事件做出反应。

显示文本

以下示例显示文本。

com/zetcode/LabelEx.java
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
图:JLabel

显示图标

JLabel 可用于显示图像。

com/zetcode/LabelEx2.java
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 图像绘制图标。

Displaying icons
图:显示图标

JTextField

JTextField 是一个文本组件,允许编辑单行非格式化的文本。

com/zetcode/JTextFieldEx.java
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);
}

insertUpdateremoveUpdate 方法调用 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

JTextField
图:JTextField

JPasswordField

JPasswordFieldJTextField 的一个子类,它不显示用户键入的字符。

com/zetcode/PasswordEx.java
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');

在完成密码处理后,建议将数组的元素设置为零。

JPasswordField
图:JPasswordField

在本 Java Swing 教程中,我们介绍了基本的 Swing 组件,包括 JButtonJLabelJTextFieldJPasswordField