问题: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
相关产品推荐
相关产品推荐

