Java游戏点击Canvas后失去焦点,按键控制失效问题求助
解决Java游戏点击Canvas后按键无响应的问题
问题根源
你把KeyListener绑定在了JFrame上,当点击Canvas区域时,Canvas会获取输入焦点,JFrame失去焦点,导致后续的按键事件无法被JFrame的监听器捕获,直接表现为按键操作失效。如果点击Canvas时角色正在移动,此时释放按键的事件也无法被监听器接收,对应的XXXPressed布尔值会一直保持true,角色就会持续移动停不下来。
解决方案
方案1:将KeyListener绑定到Canvas(快速修复)
修改initFrame方法,把按键监听器绑定到Canvas(也就是当前的Game实例)上,同时确保Canvas可以接收焦点:
- 替换
initFrame中给JFrame加KeyListener的代码:
// 移除frame.addKeyListener(...),替换为: this.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { Game.this.keyPressed(e); } @Override public void keyReleased(KeyEvent e) { Game.this.keyReleased(e); } });
- 在
Game构造函数中添加焦点设置:
public Game() { sheet = new Spritesheet("/spritesheet.png"); player = new BufferedImage[2]; player[0] = sheet.getSprite(0, 0, 16, 16); player[1] = sheet.getSprite(16, 0, 16, 16); this.setPreferredSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE)); // 新增以下两行 this.setFocusable(true); // 允许Canvas接收输入焦点 this.requestFocusInWindow(); // 启动时自动获取焦点 initFrame(); image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); }
方案2:使用KeyBindings(推荐的AWT/Swing最佳实践)
KeyBindings比KeyListener更灵活,可设置为只要窗口激活就能捕获按键,不受组件焦点变化影响。示例代码如下:
首先需要导入额外的类:
import javax.swing.AbstractAction; import javax.swing.JComponent; import java.awt.event.ActionEvent;
然后在Game构造函数中添加按键绑定:
public Game() { sheet = new Spritesheet("/spritesheet.png"); player = new BufferedImage[2]; player[0] = sheet.getSprite(0, 0, 16, 16); player[1] = sheet.getSprite(16, 0, 16, 16); this.setPreferredSize(new Dimension(WIDTH*SCALE, HEIGHT*SCALE)); // 绑定W键按下 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), "upPressed"); this.getActionMap().put("upPressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { upPressed = true; playerDirectionY = -1; } }); // 绑定W键释放 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), "upReleased"); this.getActionMap().put("upReleased", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { upPressed = false; } }); // 绑定S键按下/释放 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0), "downPressed"); this.getActionMap().put("downPressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { downPressed = true; playerDirectionY = 1; } }); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), "downReleased"); this.getActionMap().put("downReleased", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { downPressed = false; } }); // 绑定A键按下/释放 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0), "leftPressed"); this.getActionMap().put("leftPressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { leftPressed = true; playerDirectionX = -1; } }); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), "leftReleased"); this.getActionMap().put("leftReleased", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { leftPressed = false; } }); // 绑定D键按下/释放 this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0), "rightPressed"); this.getActionMap().put("rightPressed", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { rightPressed = true; playerDirectionX = 1; } }); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "rightReleased"); this.getActionMap().put("rightReleased", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { rightPressed = false; } }); // 同理可添加上下方向键的绑定 initFrame(); image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); }
添加完这些绑定后,记得移除原来给JFrame添加的KeyListener代码,避免重复处理事件。
内容的提问来源于stack exchange,提问作者Gabriel
相关产品推荐
相关产品推荐

