java - 单击 Canvas 后 KeyListener 停止工作

标签 java swing keylistener

我有简单的KeyHandler

public class KeyHandler implements KeyListener {
    private final Set<Integer> keyEvents = new LinkedHashSet<>();
    public Set<Integer> pressedKeys() {
        return keyEvents;
    }

    @Override
    public void keyPressed(KeyEvent e) {
        keyEvents.add(e.getKeyCode());
    }

    @Override
    public void keyReleased(KeyEvent e) {
        keyEvents.remove(e.getKeyCode());
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }
}

添加到JFrame
frame.addKeyListener(keyListener);

休息可能无关紧要,但我将相同的实例传递给KeyBinder

public class KeyBinder {
    private final Map<Integer, GameEvent> bindings = new HashMap<>();
    private final KeyHandler keyHandler;

    public KeyBinder(KeyHandler keyHandler) {
        this.keyHandler = keyHandler;
        bindings.put(KeyEvent.VK_A, MoveEvent.MOVE_LEFT);
        bindings.put(KeyEvent.VK_D, MoveEvent.MOVE_RIGHT);
        bindings.put(KeyEvent.VK_S, MoveEvent.MOVE_DOWN);
        bindings.put(KeyEvent.VK_W, MoveEvent.MOVE_UP);
    }

    public List<GameEvent> mapEvents() {
        final var events = new ArrayList<GameEvent>();
        keyHandler.pressedKeys().forEach(key -> events.add(bindings.get(key)));
        return events;
    }
}

然后,我将这些 GameEvents 传递给 Player 实例以使他移动

@Override
public void update(java.util.List<GameEvent> events) {
    events.forEach(this::handleEvent);
}

private void handleEvent(GameEvent event) {
    if (event instanceof MoveEvent) {
        updatePosition((MoveEvent) event);
    }
}

private void updatePosition(MoveEvent event) {
    switch (event) {
        case MOVE_UP -> position = new Position(position.getX(), position.getY() - speed);
        case MOVE_DOWN -> position = new Position(position.getX(), position.getY() + speed);
        case MOVE_RIGHT -> position = new Position(position.getX() + speed, position.getY());
        case MOVE_LEFT -> position = new Position(position.getX() - speed, position.getY());
    }
}

如果我运行应用程序的单个实例,它工作得非常好。但由于这是一个在线游戏,我想同时运行 2 个实例以进行测试。不幸的是,当我启动第二个实例时,我的 KeyHandler 在任何一个实例中都不再工作,它只是不记录任何 KeyEvents。我知道我的应用程序仍在运行,因为我的服务器应用程序不断从两个客户端接收数据。

编辑:

我刚刚意识到还有其他问题。一旦我单击 Canvas ,KeyHandler 就会停止工作...我什至不需要应用程序的第二个实例。这是我用来绘制东西的 JFrameCanvas 的代码...

 public class Display {
        private JFrame frame;
        private Canvas canvas;
    
        public Display(KeyListener keyListener) {
            initFrame(keyListener);
            initCanvas();
            frame.add(canvas);
            frame.pack();
        }
    
        private void initFrame(KeyListener keyListener) {
            frame = new JFrame(GameConfig.TITLE);
            frame.setSize(GameConfig.SCREEN_WIDTH, GameConfig.SCREEN_HEIGHT);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setResizable(false);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            frame.addKeyListener(keyListener);
        }
    
        private void initCanvas() {
            canvas = new Canvas();
            canvas.setPreferredSize(new Dimension(GameConfig.SCREEN_WIDTH, GameConfig.SCREEN_HEIGHT));
            canvas.setMaximumSize(new Dimension(GameConfig.SCREEN_WIDTH, GameConfig.SCREEN_HEIGHT));
            canvas.setMinimumSize(new Dimension(GameConfig.SCREEN_WIDTH, GameConfig.SCREEN_HEIGHT));
        }
    
        public Canvas getCanvas() {
            return canvas;
        }
    }

最佳答案

您的按键监听器未监听任何按键事件的原因是因为按键监听器仅在组件获得焦点时才起作用。似乎当您单击 Canvas 时, Canvas 现在是焦点的所有者,因此框架不再获得焦点。

一般来说,我建议为此使用键绑定(bind)。即使您的组件未获得焦点,键绑定(bind)也可以监听键事件。但如果你想继续使用 KeyListeners,这里有两个解决方案:

Add a FocusListener to the frame.

    frame.addFocusListener(new FocusListener() {
            public void focusGained(FocusEvent e) {}
            public void focusLost(FocusEvent e) {frame.requestFocus();}
    });

Disable the focusability of canvas.

    canvas.setFocusable(false);

不推荐使用这些解决方案,但它应该适合您的情况。

关于java - 单击 Canvas 后 KeyListener 停止工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60237917/

相关文章:

java - "AL lib: alc_cleanup: 1 device not closed"是什么意思?

Java JButton 只有图像?

java - 如何在 JTable 中选择行或列?

java - 从 JPanel 添加/删除

java - 为什么这不会产生歧义?

java - 将后端服务的一行数据放入ListView中

java - 使用它的缓存键取消来自管理器的请求不起作用

java - 来自命令行的 Weka

没有打开窗口的Java Keylistener?

java - 我在添加关键监听器时犯了什么错误?