You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java游戏点击Canvas后失去焦点,按键控制失效问题求助

解决Java游戏点击Canvas后按键无响应的问题

问题根源

你把KeyListener绑定在了JFrame上,当点击Canvas区域时,Canvas会获取输入焦点,JFrame失去焦点,导致后续的按键事件无法被JFrame的监听器捕获,直接表现为按键操作失效。如果点击Canvas时角色正在移动,此时释放按键的事件也无法被监听器接收,对应的XXXPressed布尔值会一直保持true,角色就会持续移动停不下来。

解决方案

方案1:将KeyListener绑定到Canvas(快速修复)

修改initFrame方法,把按键监听器绑定到Canvas(也就是当前的Game实例)上,同时确保Canvas可以接收焦点:

  1. 替换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);
    }
});
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 04:54:55