user9871751
user9871751

Reputation:

Why does the KeyListener only work when did`t pressed the button?

I wanted to make a KeyListener to stop the programm when I press the ESC key. But it only works when I did nothing else (pressed the button). I`m sorry if it is something super obvious but I cant find the mistake.

package basics;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame; 


public class Graphic extends JFrame implements ActionListener, KeyListener {

 private JButton button;

    public Graphic() {
        button = new JButton();
        button.addActionListener(this);
        button.setIcon(new ImageIcon("Image.jpg"));

        this.getContentPane().add(button);
    }
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button){
            //some code 
        }
   } 

    public static void main(String[] args) {
        JFrame bec = new Graphic();
        bec.setDefaultCloseOperation(Graphic.EXIT_ON_CLOSE);
        bec.setSize(1731, 563);
        bec.setVisible(true);
        bec.setTitle("title");
        bec.requestFocus();
        bec.addKeyListener(new Graphic());
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_ESCAPE){
            System.exit(0);
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

} 

Upvotes: 0

Views: 256

Answers (4)

rodridevops
rodridevops

Reputation: 1997

KeyListener suffers from issues related to focusability and with other controls in the GUI. A simple solution would be to use the Actions API. Under this approach, the program simply specifies, for a given component, the “binding” or “mapping” between any key of interest and the Action (command) object to be invoked when that key is pressed (or released). Key bindings are associated with a specific GUI component.

In this case, a proper solution could be:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.KeyStroke; 


public class Graphic extends JFrame implements ActionListener {

private JButton button;

    public Graphic() {
        getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
                KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "Cancel"); //$NON-NLS-1$
            getRootPane().getActionMap().put("Cancel", new AbstractAction(){ //$NON-NLS-1$
                public void actionPerformed(ActionEvent e)
                {
                    dispose();
                }
            });

        button = new JButton();
        button.addActionListener(this);
        button.setIcon(new ImageIcon("Image.jpg"));

        this.getContentPane().add(button);

    }
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == button){
            //some code 
        }
   } 

    public static void main(String[] args) {
        JFrame bec = new Graphic();
        bec.setDefaultCloseOperation(Graphic.EXIT_ON_CLOSE);
        bec.setSize(1731, 563);
        bec.setVisible(true);
        bec.setTitle("title");
        bec.requestFocus();
    }

} 

Upvotes: 1

Andrew Thompson
Andrew Thompson

Reputation: 168845

But it only works when I did nothing else (pressed the button).

No it doesn't work (at all). Have a look at this code:

public static void main(String[] args) {
    JFrame bec = new Graphic();
    // ..
    bec.addKeyListener(new Graphic());
}

The key listener is added to a 2nd instance of Graphic that is never displayed.

Another reason it would not work: Because a KeyListener (even when added to the correct instance) requires that the component to which it's added is both focusable (a JFrame by default is not) and has the input focus (which that frame will never have, given it is not focusable).

Solution: For Swing, we typically use key bindings rather than the lower level KeyListener. A key binging offers ways to specify on what conditions it will be invoked, some of which do not require the component to have focus.

Upvotes: 0

Oleg Cherednik
Oleg Cherednik

Reputation: 18253

You problem is to catch KeyListener across all components. You can do it like this:

public Graphic() {
    // ...
    KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> {
        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
            dispose();
            return true;
        }
        return false;
    });
}

When you add this, your application will be closed exactly after you press ESC button. If you want to block it e.g. if some of you components have focus (e.g. JTextField and you want to do specific action), then you have to focus component in this listener and avoid invoking dispose().

Upvotes: 0

KeterDev
KeterDev

Reputation: 7

Are you sure there's no TextArea's or other focusable things? They can get focus, and key events will pass to them instead of listener.

UPD: Sorry, didn't see that you have nothing but button there.

Upvotes: 0

Related Questions