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

问题:KeyListener的keyPressed和keyReleased方法未触发,已尝试setFocusable(true)

问题解决:KeyListener未触发的原因及修复方案

核心原因

你把KeyListener绑定在了JFrame上,但实际焦点会落在添加的Canvas组件上,JFrame本身几乎不会获得焦点,导致监听器收不到按键事件。另外你的Game类中running变量初始为false,run方法的循环根本不会执行,就算按键事件触发了也看不到输出。

快速修复方案(基于KeyListener)

修改Window.java的构造函数,将KeyListener绑定到Canvas上,并确保Canvas获取焦点:

public Window(String title, Dimension resolution, Canvas display) {
    frame = new JFrame(title);
    Dimension decoratedResolution = new Dimension(resolution.width + 16, resolution.height + 38);

    frame.setPreferredSize(decoratedResolution);
    frame.setMinimumSize(decoratedResolution);
    frame.setMaximumSize(decoratedResolution);
    frame.setResizable(false);

    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // 给Canvas绑定监听器,而非JFrame
    display.setFocusable(true); // 允许Canvas获取焦点
    display.addKeyListener(new KeyListener() {
        @Override
        public void keyTyped(KeyEvent e) {}

        @Override
        public void keyPressed(KeyEvent e) {
            char character = e.getKeyChar();
            if (!keysDown.contains(character))
                keysDown.add(character);
        }

        @Override
        public void keyReleased(KeyEvent e) {
            char character = e.getKeyChar();
            keysDown.remove((Object)character);
        }
    });

    frame.add(display);
    frame.setVisible(true);
    display.requestFocusInWindow(); // 窗口显示后让Canvas主动请求焦点
}

同时修复Game.java的start方法,启动线程并设置running为true:

public void start() {
    init();
    running = true;
    thread = new Thread(this);
    thread.start();
}

推荐方案:使用KeyBindings(更稳定)

KeyListener依赖组件焦点,KeyBindings可以在窗口处于前台时就触发事件,适用性更广。修改Window.java如下:

public Window(String title, Dimension resolution, Canvas display) {
    frame = new JFrame(title);
    Dimension decoratedResolution = new Dimension(resolution.width + 16, resolution.height + 38);

    frame.setPreferredSize(decoratedResolution);
    frame.setMinimumSize(decoratedResolution);
    frame.setMaximumSize(decoratedResolution);
    frame.setResizable(false);

    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    display.setFocusable(true);

    // 按键按下时的处理逻辑
    Action keyPressedAction = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            String keyCmd = e.getActionCommand();
            char character = keyCmd.charAt(0);
            if (!keysDown.contains(character)) {
                keysDown.add(character);
            }
        }
    };

    // 按键释放时的处理逻辑
    Action keyReleasedAction = new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent e) {
            String keyCmd = e.getActionCommand();
            char character = keyCmd.charAt(0);
            keysDown.remove((Object)character);
        }
    };

    // 绑定常用按键(可按需添加更多)
    bindKey(display, "D_pressed", KeyEvent.VK_D, InputEvent.KEY_PRESSED, keyPressedAction);
    bindKey(display, "D_released", KeyEvent.VK_D, InputEvent.KEY_RELEASED, keyReleasedAction);
    bindKey(display, "A_pressed", KeyEvent.VK_A, InputEvent.KEY_PRESSED, keyPressedAction);
    bindKey(display, "A_released", KeyEvent.VK_A, InputEvent.KEY_RELEASED, keyReleasedAction);

    frame.add(display);
    frame.setVisible(true);
    display.requestFocusInWindow();
}

// 辅助方法:简化按键绑定流程
private void bindKey(Component comp, String actionName, int keyCode, int eventType, Action action) {
    InputMap inputMap = comp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
    KeyStroke stroke = KeyStroke.getKeyStroke(keyCode, 0, eventType == InputEvent.KEY_RELEASED);
    inputMap.put(stroke, actionName);
    comp.getActionMap().put(actionName, action);
}

同样需要修复Game.java的start方法启动线程。

内容的提问来源于stack exchange,提问作者RandomHumanPerson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 01:21:06