Swing 是 Java 經典 GUI 框架,雖然現代開發多用 JavaFX/Web,但理解 Swing 組件模型、事件機制、佈局管理對掌握桌面開發基礎仍有價值。本文通過一個完整的登錄界面案例,涵蓋 JFrame、JPanel、JLabel、JTextField、JPasswordField、JComboBox、JButton、JOptionPane 核心組件用法,以及絕對佈局、事件監聽、鍵盤快捷鍵、輸入驗證、圖片加載完整流程。
package com.example.swing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class LoginFrame extends JFrame implements ActionListener {
// 組件聲明
private JPanel backgroundPanel;
private JButton btnLogin, btnCancel;
private JLabel lblTitle, lblUser, lblPwd, lblRole;
private JLabel lblImage;
private JComboBox cmbRole;
private JTextField txtUser;
private JPasswordField txtPwd;
private ImageIcon bgIcon;
private static final String[] ROLES = {"管理員", "其他"};
private static final String VALID_USER = "123";
private static final String VALID_PWD = "123";
public LoginFrame() {
initComponents();
layoutComponents();
registerListeners();
frameSettings();
}
private void initComponents() {
// 標題
lblTitle = new JLabel("網 站 內 容 管 理 系 統");
lblTitle.setFont(new Font("楷體", Font.BOLD, 20));
lblTitle.setForeground(Color.BLACK);
// 標籤
lblUser = new JLabel("用戶名:");
lblPwd = new JLabel("密 碼:");
lblRole = new JLabel("身 份:");
for (JLabel lbl : new JLabel[]{lblUser, lblPwd, lblRole}) {
lbl.setFont(new Font("楷體", Font.PLAIN, 15));
lbl.setForeground(Color.BLACK);
}
// 輸入組件
txtUser = new JTextField();
txtUser.setForeground(Color.BLACK);
txtPwd = new JPasswordField();
txtPwd.setEchoChar('*');
txtPwd.setForeground(Color.BLACK);
cmbRole = new JComboBox<>(ROLES);
cmbRole.setForeground(Color.BLACK);
// 按鈕 + 快捷鍵
btnLogin = new JButton("確定 (Alt+Y)");
btnLogin.setMnemonic(KeyEvent.VK_Y);
btnLogin.setForeground(Color.BLACK);
btnCancel = new JButton("取消 (Alt+N)");
btnCancel.setMnemonic(KeyEvent.VK_N);
btnCancel.setForeground(Color.BLACK);
// 背景圖片面板
backgroundPanel = new JPanel(null); // 面板內部也用絕對佈局
bgIcon = new ImageIcon(getClass().getResource("/images/login_bg.jpg")); // 建議放 resources/images
lblImage = new JLabel(bgIcon);
backgroundPanel.add(lblImage);
}
private void layoutComponents() {
setLayout(null); // 窗口絕對佈局
// 背景面板鋪滿
backgroundPanel.setBounds(0, 0, 560, 400);
lblImage.setBounds(0, 0, 560, 400);
add(backgroundPanel);
// 組件座標 (x, y, width, height)
lblTitle.setBounds(115, 20, 420, 50);
lblUser.setBounds(130, 90, 100, 50);
txtUser.setBounds(210, 100, 180, 25);
lblPwd.setBounds(130, 150, 100, 50);
txtPwd.setBounds(210, 160, 180, 25);
lblRole.setBounds(130, 200, 100, 50);
cmbRole.setBounds(210, 210, 100, 25);
btnLogin.setBounds(120, 270, 120, 35);
btnCancel.setBounds(320, 270, 120, 35);
// 添加到窗口 (背景面板在最底層)
add(lblTitle);
add(lblUser);
add(txtUser);
add(lblPwd);
add(txtPwd);
add(lblRole);
add(cmbRole);
add(btnLogin);
add(btnCancel);
}
private void registerListeners() {
btnLogin.addActionListener(this);
btnCancel.addActionListener(this);
}
private void frameSettings() {
setTitle("歡迎登陸網站內容管理系統");
setSize(560, 400);
setResizable(false);
setLocationRelativeTo(null); // 居中
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btnLogin) {
handleLogin();
} else if (e.getSource() == btnCancel) {
System.exit(0);
}
}
private void handleLogin() {
String username = txtUser.getText().trim();
String password = new String(txtPwd.getPassword()); // 獲取密碼字符數組轉字符串
if (username.isEmpty()) {
JOptionPane.showMessageDialog(this, "請輸入用戶名!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
if (password.isEmpty()) {
JOptionPane.showMessageDialog(this, "請輸入密碼!", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
if (VALID_USER.equals(username) && VALID_PWD.equals(password)) {
JOptionPane.showMessageDialog(this, "恭喜你,登陸成功!", "成功", JOptionPane.INFORMATION_MESSAGE);
dispose(); // 關閉登錄窗口
// TODO: 打開主業務窗口 new MainFrame().setVisible(true);
} else {
JOptionPane.showMessageDialog(this, "用戶名或密碼錯誤!", "錯誤", JOptionPane.ERROR_MESSAGE);
txtPwd.setText(""); // 清空密碼框
}
}
public static void main(String[] args) {
// EDT 線程安全啟動
SwingUtilities.invokeLater(() -> new LoginFrame());
}
}
setLayout(null) 絕對佈局,手動 setBounds(x,y,w,h),適合固定尺寸登錄頁。JPanel(null) 同樣絕對佈局,JLabel 裝載 ImageIcon 鋪滿背景。BorderLayout + GridBagLayout 或 MigLayout 第三方庫,自適應縮放。ActionListener 接口,actionPerformed 統一分發。e.getSource() 區分事件源。btnLogin.addActionListener(e -> handleLogin()); 更簡潔。
btnLogin.setMnemonic(KeyEvent.VK_Y); // Alt+Y 觸發
btnCancel.setMnemonic(KeyEvent.VK_N); // Alt+N 觸發
配合按鈕文本顯示「確定 (Alt+Y)」,提升鍵盤用戶體驗。
JPasswordField pwdField = new JPasswordField();
pwdField.setEchoChar('*'); // 掩碼字符
char[] pwdChars = pwdField.getPassword(); // 返回 char[] 而非 String,安全性更高
String pwd = new String(pwdChars); // 用完及時清空:Arrays.fill(pwdChars, '0');
getClass().getResource("/images/xxx.jpg") 從 classpath 讀取 (需放入 src/main/resources/images)。new ImageIcon("path/to/file.jpg") 讀取外部文件,部署靈活但路徑易錯。JOptionPane.showMessageDialog 四種圖標:ERROR_MESSAGE, WARNING_MESSAGE, INFORMATION_MESSAGE, QUESTION_MESSAGE。txtPwd.setText(""),防止肩部偷窺。Preferences API 或加密配置文件存儲 Token。ResourceBundle 管理多語言文本。Swing 登錄界面雖小,卻包含了 GUI 開發的核心要素:組件樹構建、佈局策略、事件模型、資源管理、輸入驗證、線程安全。掌握這些基石,無論未來轉向 JavaFX、Electron 還是 Web 前端,都能觸類旁通。建議動手跑通代碼,再嘗試加入「記住密碼」複選框、登錄成功跳轉主頁等功能,在實戰中內化知識。