如何在actionPerformed方法中检测键盘输入或传递键盘输入结果至该方法?
解决方法
核心思路是通过键盘监听器记录输入状态,在Timer触发的actionPerformed方法中读取这些状态来控制逻辑,而不是直接在actionPerformed内检测键盘输入。下面是两种可行的实现方案:
方案一:使用KeyBindings(推荐,避免焦点问题)
KeyBindings比KeyListener更可靠,它不依赖组件获取焦点,适合游戏这类需要全局响应键盘输入的场景。我们可以给myJPanel绑定方向键,通过按键事件修改速度变量,再在actionPerformed中更新位置:
修改后的myJPanel类代码:
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class myJPanel extends JPanel implements ActionListener { final int PANEL_WIDTH = 500; final int PANEL_HEIGHT = 500; final int SNAKE_WIDTH = 25; final int SNAKE_HEIGHT = 25; Timer timer; int xVelocity = 0; int yVelocity = 0; int xPosition = 250; int yPosition = 250; myJPanel(){ this.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT)); this.setBackground(Color.BLACK); // 绑定方向键 setupKeyBindings(); timer = new Timer(100, this); // 调整间隔让移动更平滑 timer.start(); } private void setupKeyBindings() { // 向上键 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("UP"), "moveUp"); this.getActionMap().put("moveUp", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { xVelocity = 0; yVelocity = -5; // 控制移动步长 } }); // 向下键 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("DOWN"), "moveDown"); this.getActionMap().put("moveDown", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { xVelocity = 0; yVelocity = 5; } }); // 向左键 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("LEFT"), "moveLeft"); this.getActionMap().put("moveLeft", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { xVelocity = -5; yVelocity = 0; } }); // 向右键 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("RIGHT"), "moveRight"); this.getActionMap().put("moveRight", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { xVelocity = 5; yVelocity = 0; } }); } @Override protected void paintComponent(Graphics g) { // Swing推荐重写paintComponent而非paint super.paintComponent(g); Graphics2D g2D = (Graphics2D) g; g2D.setColor(Color.GREEN); g2D.fillRect(xPosition, yPosition, SNAKE_WIDTH, SNAKE_HEIGHT); } @Override public void actionPerformed(ActionEvent e) { // 更新蛇的位置 xPosition += xVelocity; yPosition += yVelocity; // 边界检测(防止蛇移出面板) xPosition = Math.max(0, Math.min(PANEL_WIDTH - SNAKE_WIDTH, xPosition)); yPosition = Math.max(0, Math.min(PANEL_HEIGHT - SNAKE_HEIGHT, yPosition)); repaint(); } }
方案二:使用KeyListener(需注意焦点)
如果选择KeyListener,需要确保myJPanel能获取焦点,同时重写keyPressed方法记录键盘输入:
修改后的myJPanel类代码:
import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; public class myJPanel extends JPanel implements ActionListener, KeyListener { final int PANEL_WIDTH = 500; final int PANEL_HEIGHT = 500; final int SNAKE_WIDTH = 25; final int SNAKE_HEIGHT = 25; Timer timer; int xVelocity = 0; int yVelocity = 0; int xPosition = 250; int yPosition = 250; myJPanel(){ this.setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT)); this.setBackground(Color.BLACK); this.setFocusable(true); // 必须设置为可获取焦点 this.addKeyListener(this); timer = new Timer(100, this); timer.start(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2D = (Graphics2D) g; g2D.setColor(Color.GREEN); g2D.fillRect(xPosition, yPosition, SNAKE_WIDTH, SNAKE_HEIGHT); } @Override public void actionPerformed(ActionEvent e) { xPosition += xVelocity; yPosition += yVelocity; // 边界检测 xPosition = Math.max(0, Math.min(PANEL_WIDTH - SNAKE_WIDTH, xPosition)); yPosition = Math.max(0, Math.min(PANEL_HEIGHT - SNAKE_HEIGHT, yPosition)); repaint(); } @Override public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); switch (key) { case KeyEvent.VK_UP: xVelocity = 0; yVelocity = -5; break; case KeyEvent.VK_DOWN: xVelocity = 0; yVelocity = 5; break; case KeyEvent.VK_LEFT: xVelocity = -5; yVelocity = 0; break; case KeyEvent.VK_RIGHT: xVelocity = 5; yVelocity = 0; break; } } @Override public void keyTyped(KeyEvent e) {} @Override public void keyReleased(KeyEvent e) {} }
关键注意事项
- 不要在
actionPerformed内直接检测键盘输入:Timer的定时事件和键盘输入事件属于不同的事件队列,直接检测会导致逻辑冲突。 - 用
paintComponent替代paint:这是Swing自定义绘制的最佳实践,能正确处理组件背景和边界渲染。 - 调整移动参数:通过修改Timer的间隔(如100毫秒)和速度变量(如±5),可以控制蛇的移动平滑度和速度。
内容的提问来源于stack exchange,提问作者Tim Asher
相关产品推荐
相关产品推荐

