Vivek
Vivek

Reputation: 3613

Key Listener doesn't work in a JFrame when traversing from another JFrame

I have 2 classes.

One extends canvas and inside creates a jframe and add the canvas to that jframe and add another keyadapter class to receive key events. I also have main function to test the code. When running from main, the form is displayed and recieves key events too.

Now i create another class that extends jframe and implements keylistener to receive events in this form.

Once the functionality done in the second class i want to close the second form and show the first form. When showing it from the key event functions in the second class the first class key listener is not working.

Please just have a glimpse at my code and tell me how to correct my prob. Thanks for your time and valuable suggestion.

Class 1

public class Test extends Canvas {

private JFrame container;

public Test() {

    container = new JFrame("Space Invaders");
    JPanel panel = (JPanel) container.getContentPane();
    panel.setPreferredSize(new Dimension(screenSize.width, screenSize.height));
    panel.setLayout(null);
    setBounds(0, 0, screenSize.width, screenSize.height);
    panel.add(this);
    container.pack();
    container.setResizable(false);
    container.setVisible(true);

    try {

        addKeyListener(new KeyInputHandler(this));
    } catch (Exception e) {
        e.printStackTrace();
    }
    requestFocus();
}

private class KeyInputHandler extends KeyAdapter {

public void keyPressed(KeyEvent e) {
    //Some Action
}
public void keyReleased(KeyEvent e) {
    //Some Action
}
public void keyTyped(KeyEvent e) {
    //Some Action
}
}

public static void main(String args[]){
    //Running this canvas here works perfectly with all added keylisteners
}
}

Class 2

public class Sample extends JFrame implements KeyListener {

public Sample() {
    init();
    this.setSize(100, 100);
    this.setVisible(true);
    Sample.this.dispose();
            // Created a window here and doing some operation and finally redirecting
            // to the previous test window. Even now the test window works perfectly
            // with all keylisteners
    new Test();
}

public static void main(String[] args) {
    new Sample();

}

private void init() {
    addKeyListener(this);
}

@Override
public void keyTyped(KeyEvent e) {

}

@Override
public void keyPressed(KeyEvent e) {
    removeKeyListener(this);
    Sample.this.dispose();
            // But when calling the previous Test window here, the window 
            // gets displayed but the keylistener is not added to the 
            // window. No keys are detected in test window.
    new Test();
}

@Override
public void keyReleased(KeyEvent e) {

}
}

Upvotes: 1

Views: 4837

Answers (1)

David Kroukamp
David Kroukamp

Reputation: 36423

Simple dont use KeyListener/KeyAdapter that is for AWT components and has known focus issues when used with Swing.

The issues can be got around by making sure your component is focusable via setFocusable(true) and than call requestFocusInWindow() after component has been added/is visible.

Rather use KeyBindings for Swing.

For example say now we wanted to listen for D pressed and released:

public static void addKeyBindings(JComponent jc) {
    jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), "D pressed");
    jc.getActionMap().put("D pressed", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            System.out.println("D pressed");
        }
    });

    jc.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), "D released");
    jc.getActionMap().put("D released", new AbstractAction() {
        @Override
        public void actionPerformed(ActionEvent ae) {
            System.out.println("D released");
        }
    });
}

We would call this method like:

JPanel ourPanel=new JPanel();

...

addKeyBindings(ourPanel);//adds keybindings to the panel

Other suggestions on code

  • Always create and manipulate Swing components on Event Dispatch Thread, via SwingUtilities.invokeLater(Runnable r) block

  • Dont extend JFrame class unnecessarily

  • Dont implement interfaces on a class unless the class will be used for that purpose, or other classes need access to the interfaces methods.

  • As mentioned by @AndrewThompson, dont use multiple JFrames, either swap the rest for JDialog, or use CardLayout. See here for an example.

Upvotes: 5

Related Questions