Search code examples
javaswingawtkeypress

How to fix KeyAdapter no longer working once window loses focus and gains it again


I'm making a simple game in Java for a school project, and I'm using a JFrame in conjunction with a class extending KeyAdapter in order to listen for inputs. However, once I change focus to another window (such as Snipping Tool) then back to the game window, the KeyAdapter no longer seems to recognise key presses.

I've tried running this code once compiled, and in Eclipse, experiencing the same issue both times. I'm running Windows 10, if that makes a difference, and using version 4.11.0 of Eclipse with the latest Java package available. I have not used JFrames, etc. in the past and have found very little content describing their use in a beginner-friendly fashion.

import java.awt.*;
import javax.swing.*;

public class GWindow extends Canvas {
    public GWindow() {
        frame = new JFrame("Dungeon120");
        frame.addKeyListener(new GInputs());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(1000, 650);
        frame.add(this);
        frame.pack();
        frame.setVisible(true);
        this.setBackground(Color.black);
    }
}
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class GInputs extends KeyAdapter {
    public void keyPressed(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.VK_SPACE) {
            Main.MainGame.TurnQueue.nextGUnit();
            System.out.println("SPACE KEY PRESSED");
        }
    }
}

When the program is first run, it runs as expected, with space presses printing "SPACE KEY PRESSED" and executing the nextGUnit() method. However, nothing happens once any key press once the program loses and regains focus.


Solution

  • You should try to avoid mixing AWT components with Swing components (in your case, Canvas with JFrame). Moving on, in my past experience, using KeyListener/KeyAdapter with JFrame and JPanel does not work. Instead, to listen for inputs, you can position an offscreen JTextField to listen for keys for you.

    import java.awt.*;
    import javax.swing.*;
    
    public class GWindow extends Canvas {
        public GWindow() {
            frame = new JFrame("Dungeon120");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setSize(1000, 650);
            frame.add(this);
            JTextField listener = new JTextField();
            listener.addKeyListener(new GInputs());
            //add it to the frame using any layout of your choice
            this.add(listener);
            frame.pack();
            frame.setVisible(true);
            //set focus to JTextField
            listener.requestFocusInWindow();
            this.setBackground(Color.black);
        }
    }
    

    If you are not familiar with layouts, here is a code excerpt:

    import javax.swing.*;
    import java.awt.*;
    
    public class GWindow extends Canvas {
        public GWindow() {
            JFrame frame = new JFrame("Dungeon120");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            //set layout to SpringLayout
            SpringLayout layout = new SpringLayout();
            frame.setLayout(new SpringLayout());
            this.setSize(1000, 650);
            frame.add(this);
            JTextField listener = new JTextField();
            listener.addKeyListener(new GInputs());
            //position it offscreen
            layout.putConstraint(SpringLayout.SOUTH, listener, 0, SpringLayout.NORTH, frame.getContentPane());
            frame.add(listener);
            frame.pack();
            frame.setVisible(true);
            //set focus to JTextField
            listener.requestFocusInWindow();
            this.setBackground(Color.black);
        }
    }
    

    Also, try to make your JFrame outside of the GWindow constructor.