首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java GUI 程式設計全攻略:Swing 元件實戰與事件驅動開發

     編輯:Java 程式設計入門

前言:從命令列邁向圖形化介面

早期程式透過 CLI (Command Line Interface) 互動,使用者需背誦指令語法。1984 年 Macintosh 推出 GUI (Graphical User Interface),以視覺化元件(視窗、按鈕、選單)大幅降低操作門檻,奠基現代軟體體驗。Java 提供 AWT (Abstract Window Toolkit) 與 Swing (JFC) 兩套 GUI 工具包,其中 Swing 以純 Java 實作、跨平台一致性高、元件豐富,成為桌面應用開發主流選擇。

9.1 GUI 架構核心:容器與元件

兩大基本建構單元

類別代表類別功能常見子類別
容器Container承載其他元件、管理佈局JFrame、JDialog、JPanel、JApplet、JScrollPane
元件Component互動單元、顯示資訊JButton、JLabel、JTextField、JCheckBox、JRadioButton、JComboBox、JList、JTable、JTree

基本開發流程

  1. 建立頂層容器(JFrame 或 JApplet)
  2. 設定佈局管理器(LayoutManager)
  3. 建立元件實例
  4. 將元件加入容器(container.add(component))
  5. 註冊事件監聽器
  6. 設定視窗屬性並顯示

匯入套件慣例


import javax.swing.*;      // Swing 元件
import java.awt.*;         // AWT 核心(Color、Font、LayoutManager、Event 等)
import java.awt.event.*;   // 事件處理介面

9.2 按鈕 — 互動的起點

JButton 常用建構子與方法

建構子/方法說明
JButton()無標籤按鈕
JButton(String text)文字標籤按鈕
JButton(Icon icon)圖示按鈕
JButton(String text, Icon icon)文字+圖示
setText(String) / getText()設定/取得按鈕文字
setForeground(Color)設定文字顏色
setEnabled(boolean)啟用/停用按鈕
addActionListener(ActionListener)註冊點擊事件監聽器

事件處理模式:匿名內部類別


JButton btn = new JButton("點我");
btn.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        Toolkit.getDefaultToolkit().beep(); // 發出蜂鳴聲
        btn.setText("已點擊 " + (++count) + " 次");
    }
});

完整範例:計數按鈕


import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class CounterButton extends JFrame {
    private int count = 0;
    private JButton btn;

    public CounterButton() {
        setTitle("按鈕計數器");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        
        btn = new JButton("點我 (0)");
        btn.addActionListener(e -> {
            count++;
            btn.setText("點我 (" + count + ")");
            if (count % 10 == 0) Toolkit.getDefaultToolkit().beep();
        });
        add(btn);
        
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(CounterButton::new);
    }
}

9.3 標籤 — 資訊展示的基本單元

JLabel 進階功能

  • 文字對齊:setHorizontalAlignment(SwingConstants.LEFT/CENTER/RIGHT)、setVerticalAlignment(SwingConstants.TOP/CENTER/BOTTOM)
  • 圖文混排:new JLabel("文字", icon, SwingConstants.CENTER),配合 setHorizontalTextPosition()、setVerticalTextPosition() 控制相對位置
  • 提示文字:setToolTipText("滑鼠懸停顯示")
  • 背景色:需先 setOpaque(true) 再 setBackground(Color)

範例:動態更新標籤


JLabel label = new JLabel("計數: 0", SwingConstants.CENTER);
label.setFont(new Font("微軟正黑體", Font.BOLD, 24));
label.setOpaque(true);
label.setBackground(Color.WHITE);
label.setForeground(Color.BLUE);
add(label);

JButton btn = new JButton("增加");
btn.addActionListener(e -> label.setText("計數: " + (++count)));
add(btn);

9.4 核取方塊與單選按鈕 — 選擇型輸入

JCheckBox:多重獨立選擇


JCheckBox cb1 = new JCheckBox("選項 A");
JCheckBox cb2 = new JCheckBox("選項 B", true); // 預設勾選
cb1.addItemListener(e -> {
    if (e.getStateChange() == ItemEvent.SELECTED) 
        System.out.println("A 被勾選");
    else 
        System.out.println("A 被取消");
});

JRadioButton + ButtonGroup:互斥單選


ButtonGroup group = new ButtonGroup();
JRadioButton rb1 = new JRadioButton("方案 1");
JRadioButton rb2 = new JRadioButton("方案 2", true); // 預設選中
JRadioButton rb3 = new JRadioButton("方案 3");
group.add(rb1); group.add(rb2); group.add(rb3);

// 監聽選擇變化
ItemListener listener = e -> {
    if (e.getStateChange() == ItemEvent.SELECTED) {
        JRadioButton src = (JRadioButton) e.getSource();
        System.out.println("目前選擇: " + src.getText());
    }
};
rb1.addItemListener(listener);
rb2.addItemListener(listener);
rb3.addItemListener(listener);

完整範例:顏色選擇器


public class ColorChooser extends JFrame {
    private JButton previewBtn;
    private ButtonGroup colorGroup;

    public ColorChooser() {
        setTitle("顏色選擇示範");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        
        // 預覽按鈕
        previewBtn = new JButton("預覽區域");
        previewBtn.setFont(new Font("微軟正黑體", Font.BOLD, 18));
        previewBtn.setBackground(Color.LIGHT_GRAY);
        previewBtn.setOpaque(true);
        previewBtn.setBorderPainted(false);
        add(previewBtn, BorderLayout.CENTER);
        
        // 選項面板
        JPanel panel = new JPanel(new GridLayout(0, 1));
        colorGroup = new ButtonGroup();
        String[] colors = {"紅色", "綠色", "藍色", "黑色"};
        Color[] colorVals = {Color.RED, Color.GREEN, Color.BLUE, Color.BLACK};
        
        for (int i = 0; i < colors.length; i++) {
            JRadioButton rb = new JRadioButton(colors[i], i == 3); // 預設黑色
            final Color c = colorVals[i];
            rb.addActionListener(e -> {
                previewBtn.setBackground(c);
                previewBtn.setForeground(getContrastColor(c)); // 文字對比色
            });
            colorGroup.add(rb);
            panel.add(rb);
        }
        add(panel, BorderLayout.EAST);
        
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }
    
    // 簡易對比色計算
    private Color getContrastColor(Color bg) {
        int luminance = (int)(0.299*bg.getRed() + 0.587*bg.getGreen() + 0.114*bg.getBlue());
        return luminance > 186 ? Color.BLACK : Color.WHITE;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(ColorChooser::new);
    }
}

9.5 列表方塊與下拉選單 — 多項目選擇

JList:可見多行、支援多選


String[] items = {"台北", "新北", "桃園", "台中", "台南", "高雄"};
JList list = new JList<>(items);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); // 或 MULTIPLE_INTERVAL_SELECTION
list.setVisibleRowCount(4);
JScrollPane scrollPane = new JScrollPane(list);
add(scrollPane);

list.addListSelectionListener(e -> {
    if (!e.getValueIsAdjusting()) {
        System.out.println("選擇: " + list.getSelectedValue());
    }
});

JComboBox:下拉式、節省空間


String[] fonts = {"微軟正黑體", "新細明體", "標楷體", "Arial", "Times New Roman"};
JComboBox combo = new JComboBox<>(fonts);
combo.setEditable(true); // 允許使用者自行輸入
combo.addActionListener(e -> {
    String selected = (String) combo.getSelectedItem();
    System.out.println("選擇字型: " + selected);
});
add(combo);

動態增刪列表項目


DefaultListModel model = new DefaultListModel<>();
model.addElement("項目 1");
model.addElement("項目 2");
JList dynamicList = new JList<>(model);

JButton addBtn = new JButton("新增");
addBtn.addActionListener(e -> model.addElement("項目 " + (model.getSize() + 1)));

JButton delBtn = new JButton("刪除最後");
delBtn.addActionListener(e -> {
    if (model.getSize() > 0) model.remove(model.getSize() - 1);
});

9.6 文字輸入元件 — 從單行到多行

三大文字元件比較

類別用途關鍵特性
JTextField單行文字輸入設定欄寬 new JTextField(20)、setHorizontalAlignment()、addActionListener (Enter 鍵觸發)
JPasswordField密碼輸入(遮罩顯示)getPassword() 回傳 char[]、setEchoChar('*') 自訂遮罩字元
JTextArea多行文字編輯/顯示設定行/列 new JTextArea(5, 30)、setLineWrap(true) 自動換行、setWrapStyleWord(true) 單字換行、搭配 JScrollPane

完整範例:登入表單


public class LoginForm extends JFrame {
    public LoginForm() {
        setTitle("使用者登入");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 5, 5, 5);
        gbc.fill = GridBagConstraints.HORIZONTAL;
        
        // 帳號
        gbc.gridx = 0; gbc.gridy = 0; add(new JLabel("帳號:"), gbc);
        gbc.gridx = 1; 
        JTextField userField = new JTextField(15);
        add(userField, gbc);
        
        // 密碼
        gbc.gridx = 0; gbc.gridy = 1; add(new JLabel("密碼:"), gbc);
        gbc.gridx = 1;
        JPasswordField pwdField = new JPasswordField(15);
        add(pwdField, gbc);
        
        // 登入按鈕
        gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2;
        JButton loginBtn = new JButton("登入");
        loginBtn.addActionListener(e -> {
            String user = userField.getText();
            String pwd = new String(pwdField.getPassword());
            if ("admin".equals(user) && "1234".equals(pwd)) {
                JOptionPane.showMessageDialog(this, "歡迎, " + user + "!");
            } else {
                JOptionPane.showMessageDialog(this, "帳號或密碼錯誤", "錯誤", JOptionPane.ERROR_MESSAGE);
            }
        });
        add(loginBtn, gbc);
        
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(LoginForm::new);
    }
}

JTextArea 進階:自動換行與滾動


JTextArea textArea = new JTextArea("請輸入內容...", 10, 40);
textArea.setFont(new Font("Monospaced", Font.PLAIN, 14));
textArea.setLineWrap(true);           // 自動換行
textArea.setWrapStyleWord(true);      // 依單字換行,不斷詞
textArea.setTabSize(4);               // Tab 寬度

JScrollPane scroll = new JScrollPane(textArea);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
add(scroll, BorderLayout.CENTER);

// 清除按鈕
JButton clearBtn = new JButton("清除");
clearBtn.addActionListener(e -> textArea.setText(""));
add(clearBtn, BorderLayout.SOUTH);

9.7 佈局管理器 — 掌控元件排版

Swing 採用委派模式:容器持有 LayoutManager,負責子元件大小與位置。

佈局管理器特性適用場景
FlowLayout左到右、上到下流式排列簡單工具列、按鈕群組
BorderLayout五大區域:NORTH, SOUTH, EAST, WEST, CENTER主視窗整體架構
GridLayout規則網格,等寬等高計算機鍵盤、表單欄位
GridBagLayout彈性網格,元件可跨行跨列複雜表單、精細對齊
BoxLayout單行/單欄堆疊,支援彈性空間工具列、側邊欄
CardLayout疊卡切換,同一時間顯示一張精靈嚮導、分頁介面

GridBagLayout 實用範例


JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(4, 4, 4, 4);
gbc.anchor = GridBagConstraints.WEST;

gbc.gridx = 0; gbc.gridy = 0; panel.add(new JLabel("姓名:"), gbc);
gbc.gridx = 1; gbc.weightx = 1.0; gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(new JTextField(15), gbc);

gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0; gbc.fill = GridBagConstraints.NONE;
panel.add(new JLabel("性別:"), gbc);
gbc.gridx = 1;
JComboBox gender = new JComboBox<>(new String[]{"男", "女", "其他"});
panel.add(gender, gbc);

gbc.gridx = 0; gbc.gridy = 2; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER;
JButton submit = new JButton("送出");
panel.add(submit, gbc);

9.8 事件處理深度解析

事件模型三要素

  1. 事件源:產生事件的元件(按鈕、文字框、滑鼠等)
  2. 事件物件:封裝事件資訊(ActionEvent、MouseEvent、KeyEvent 等)
  3. 事件監聽器:實作特定介面(ActionListener、MouseListener 等)的處理類別

常見事件監聽器對照表

事件類型監聽器介面事件類別觸發時機
動作ActionListenerActionEvent按鈕點擊、菜單選擇、Enter 鍵
滑鼠MouseListener/MouseMotionListenerMouseEvent點擊、進入/離開、拖曳、移動
鍵盤KeyListenerKeyEvent按下、釋放、輸入字元
項目狀態ItemListenerItemEvent核取方塊、單選按鈕、下拉選單選擇變化
列表選擇ListSelectionListenerListSelectionEventJList、JTable 選取變化
視窗WindowListenerWindowEvent開啟、關閉、最小化、啟動/停用
焦點FocusListenerFocusEvent元件獲得/失去焦點

Lambda 表達式簡化監聽器 (Java 8+)


// 傳統匿名類別
btn.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) { doSomething(); }
});

// Lambda 簡寫 (適用於函數式介面)
btn.addActionListener(e -> doSomething());
btn.addActionListener(e -> {
    int count = getCount();
    updateUI(count);
});

// 方法參考
btn.addActionListener(this::handleClick);

9.9 綜合實戰:通訊錄管理介面


public class AddressBookUI extends JFrame {
    private DefaultListModel listModel = new DefaultListModel<>();
    private JList contactList = new JList<>(listModel);
    private JTextField nameField = new JTextField(15);
    private JTextField phoneField = new JTextField(15);
    private JTextField emailField = new JTextField(15);

    public AddressBookUI() {
        setTitle("通訊錄管理");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout(10, 10));
        
        // 左側:聯絡人列表
        contactList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        contactList.setCellRenderer(new ContactCellRenderer());
        JScrollPane listScroll = new JScrollPane(contactList);
        listScroll.setPreferredSize(new Dimension(250, 400));
        add(listScroll, BorderLayout.WEST);
        
        // 右側:編輯表單
        JPanel formPanel = new JPanel(new GridBagLayout());
        formPanel.setBorder(BorderFactory.createTitledBorder("聯絡人資料"));
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.insets = new Insets(5, 5, 5, 5);
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        
        addFormRow(formPanel, gbc, 0, "姓名:", nameField);
        addFormRow(formPanel, gbc, 1, "電話:", phoneField);
        addFormRow(formPanel, gbc, 2, "Email:", emailField);
        
        // 按鈕列
        gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 2; gbc.anchor = GridBagConstraints.CENTER;
        JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
        JButton addBtn = new JButton("新增");
        JButton updateBtn = new JButton("更新");
        JButton delBtn = new JButton("刪除");
        JButton clearBtn = new JButton("清空");
        btnPanel.add(addBtn); btnPanel.add(updateBtn); btnPanel.add(delBtn); btnPanel.add(clearBtn);
        formPanel.add(btnPanel, gbc);
        
        add(formPanel, BorderLayout.CENTER);
        
        // 事件綁定
        addBtn.addActionListener(e -> addContact());
        updateBtn.addActionListener(e -> updateContact());
        delBtn.addActionListener(e -> deleteContact());
        clearBtn.addActionListener(e -> clearForm());
        contactList.addListSelectionListener(e -> {
            if (!e.getValueIsAdjusting()) loadSelectedContact();
        });
        
        // 測試資料
        listModel.addElement(new Contact("張三", "0912-345678", "zhang@example.com"));
        listModel.addElement(new Contact("李四", "0923-456789", "li@example.com"));
        
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }
    
    private void addFormRow(JPanel panel, GridBagConstraints gbc, int row, String label, JComponent field) {
        gbc.gridx = 0; gbc.gridy = row; gbc.weightx = 0; panel.add(new JLabel(label), gbc);
        gbc.gridx = 1; gbc.weightx = 1.0; panel.add(field, gbc);
    }
    
    private void addContact() {
        String name = nameField.getText().trim();
        if (name.isEmpty()) return;
        listModel.addElement(new Contact(name, phoneField.getText().trim(), emailField.getText().trim()));
        clearForm();
    }
    
    private void updateContact() {
        int idx = contactList.getSelectedIndex();
        if (idx >= 0) {
            listModel.set(idx, new Contact(
                nameField.getText().trim(),
                phoneField.getText().trim(),
                emailField.getText().trim()
            ));
        }
    }
    
    private void deleteContact() {
        int idx = contactList.getSelectedIndex();
        if (idx >= 0) listModel.remove(idx);
        clearForm();
    }
    
    private void clearForm() {
        nameField.setText(""); phoneField.setText(""); emailField.setText("");
        contactList.clearSelection();
    }
    
    private void loadSelectedContact() {
        Contact c = contactList.getSelectedValue();
        if (c != null) {
            nameField.setText(c.name);
            phoneField.setText(c.phone);
            emailField.setText(c.email);
        }
    }
    
    // 內部資料類別
    static class Contact {
        String name, phone, email;
        Contact(String n, String p, String e) { name = n; phone = p; email = e; }
        public String toString() { return name + " (" + phone + ")"; }
    }
    
    // 自訂列表渲染器
    static class ContactCellRenderer extends DefaultListCellRenderer {
        public Component getListCellRendererComponent(JList list, Object value, int index, 
                boolean isSelected, boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value instanceof Contact) {
                Contact c = (Contact) value;
                setText(c.name);
                setToolTipText(c.phone + " | " + c.email);
            }
            return this;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(AddressBookUI::new);
    }
}

最佳實踐總結

  1. EDT 事件派發執行緒:所有 GUI 操作必須在 EDT 執行,使用 SwingUtilities.invokeLater() 或 invokeAndWait()
  2. 長時間任務:使用 SwingWorker 背景執行,避免凍結介面
  3. 元件重用:封裝常用表單面板為自訂類別,提升開發效率
  4. 外觀一致性:統一字型、色系、間距,建立專案風格指南
  5. 無障礙支援:設定 setToolTipText、鍵盤導航順序 (setFocusTraversalPolicy)

本章完整涵蓋 Swing 核心元件、事件模型、佈局管理與實戰技巧。掌握這些基礎後,可進階學習 JavaFX 現代 UI 框架、客製化元件開發、資料綁定架構等主題,構建專業級桌面應用程式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved