鍵盤交互是桌面應用的核心體驗之一。Swing 通過 KeyListener 接口與 KeyEvent 類提供完整的鍵盤事件模型。本文詳細講解三類鍵盤事件、關鍵 API、焦點前置條件、以及一個完整的「字母統計顯示」實戰案例。
| 方法 | 觸發時機 | 典型用途 |
|---|---|---|
keyPressed(KeyEvent e) | 鍵按下瞬間 | 遊戲移動、快捷鍵、組合鍵檢測 |
keyReleased(KeyEvent e) | 鍵釋放瞬間 | 輸入完成提交、鬆開停止移動 |
keyTyped(KeyEvent e) | 鍵按下並釋放(產生字符) | 文本輸入過濾、字符統計、只允許數字 |
關鍵區別:keyTyped 只有可打印字符才觸發(如字母、數字、符號),Shift、Ctrl、方向鍵等不觸發;前兩者對所有鍵均觸發。
getKeyCode():返回虛擬鍵碼(KeyEvent.VK_A、VK_ENTER、VK_LEFT…),用於 pressed/released。getKeyChar():返回實際字符(Unicode),用於 typed。getModifiersEx():返回修飾鍵掩碼(SHIFT_DOWN_MASK、CTRL_DOWN_MASK…),檢測組合鍵。isActionKey():判斷是否為功能鍵(F1-F12、Home、End…)。只有擁有焦點的組件才能接收鍵盤事件。
setFocusable(true):使組件可獲取焦點(JButton 默認 true,JLabel 默認 false)。requestFocusInWindow():請求焦點(啟動後調用)。FocusListener:監聽焦點獲得/失去,動態改變外觀提示用戶。需求:窗口包含一個按鈕(事件源)和文本區。點擊按鈕獲取焦點後,輸入字母在文本區顯示,每 10 個換行,字母間空格分隔。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class KeyEventDemo extends JFrame implements KeyListener {
private JTextArea textArea = new JTextArea(10, 30);
private JButton btn = new JButton("點擊獲取焦點,然後輸入字母");
private int count = 0;
public KeyEventDemo() {
setTitle("鍵盤事件演示");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
btn.addKeyListener(this); // 註冊監聽
btn.setFocusable(true);
add(btn, BorderLayout.NORTH);
add(new JScrollPane(textArea), BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
}
@Override
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
// 只處理 A-Z
if (code >= KeyEvent.VK_A && code <= KeyEvent.VK_Z) {
char ch = (char) code;
textArea.append(ch + " ");
if (++count % 10 == 0) textArea.append("\n");
}
}
@Override public void keyReleased(KeyEvent e) {} // 不處理
@Override public void keyTyped(KeyEvent e) {} // 不處理
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new KeyEventDemo().setVisible(true));
}
}
KeyListener,不依賴焦點,支援全局/組件級映射。
InputMap im = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
im.put(KeyStroke.getKeyStroke("ctrl S"), "saveAction");
comp.getActionMap().put("saveAction", new AbstractAction() {
public void actionPerformed(ActionEvent e) { save(); }
});
JTextField/JTextArea 內建 DocumentFilter、InputVerifier,比鍵盤事件更適合輸入限制。e.consume() 阻止事件向父容器傳播(如屏蔽某些快捷鍵)。KeyEvent.VK_* 常量,避免硬編碼數字。| 現象 | 原因 | 解決 |
|---|---|---|
| 鍵盤事件完全不觸發 | 組件不可聚焦或未獲取焦點 | setFocusable(true); requestFocusInWindow(); |
| 方向鍵不觸發 keyTyped | 非字符鍵不產生 typed 事件 | 用 keyPressed 配合 VK_LEFT 等常量 |
| 組合鍵檢測失效 | 直接判斷 getKeyCode() == VK_CONTROL | 用 (e.getModifiersEx() & CTRL_DOWN_MASK) != 0 |
| 重複觸發/卡鍵 | 長按產生多次 pressed | 在邏輯層去抖,或用 released 觸發一次性動作 |
鍵盤事件看似簡單,但焦點、事件類型選擇、跨平台一致性是三大隱形坑。建議:文本輸入用 DocumentFilter,遊戲/快捷鍵用 Key Binding,臨時攔截用 KeyListener,分場景選擇最適 API。