Reputation: 5495
I have a JFrame class which implements several JLabels and JTextFields and I would like to perform an action when the focus is on that JFrame and the user press F1.
I have created the following class to handle the KeyListener:
public class PropertiesKey implements KeyListener, ActionListener {
private Properties propertiesWindow;
public PropertiesKey(Properties p) {
propertiesWindow = p;
System.out.println("DEBUG PropertiesKey");
}
@Override
public void keyPressed(KeyEvent event) {
System.out.println("DEBUG keyPressed");
// F1 - Display Attribute Window
if (event.getKeyCode() == KeyEvent.VK_F1){
System.out.println("F1");
if(propertiesWindow.isVisible()) {
propertiesWindow.setVisible(false);
} else {
propertiesWindow.setVisible(true);
}
}
System.out.println("KEY PRESSED " + event);
}
@Override
public void keyReleased(KeyEvent event) {
System.out.println("KEY RELEASED " + event);
}
@Override
public void keyTyped(KeyEvent event) {
System.out.println("KEY TYPED " + event);
}
@Override
public void actionPerformed(ActionEvent event) {
System.out.println("ACTION PERFORMED " + event);
}
}
Which I attach to the main JFrame class using:
this.addKeyListener(new PropertiesKey(this));
I am sure that this is actually attached as I see the System.out message when the Key listener class is created.
While this strategy works in another area of the program where I have an AWT Frame, I can not manage to get the KeyEvent propagated to my listener in this specific case of a JFrame populated with JTextFields.
I suspect that the TextFields -the focus on the JFrame will be always captured by one TextField- might be intercepting the event and not propagating it.
If that is the case, how can I fix it? Which strategy/pattern is used to capture Key events at JFrame level?
It it is not the case, how could I further troubleshoot this issue?
Upvotes: 0
Views: 2296
Reputation: 324147
You should not be using a KeyListener. Key events can only be generated for the component that has focus. The frame will not have direct focus, only a component added to the frame.
Instead you should be using Key Bindings
. With key bindings you can map a Keystroke
to an Action
even when a component doesn't have focus. Make sure you use the appropriate InputMap
.
In this case if you want to add a general handler for the frame you would add the key bindings to the "root pane" of the frame.
Read the section from the Swing tutorial on How to Use Key Bindings for more information.
Also read the section on How to Use Actions for the benefits of using an Action
.
Upvotes: 1
Reputation: 10153
KeyListener
only works (in most cases) for components that are currently focused. It also won't work if you add it to a container of the focused component in case when that component consumes key events. Registering a keyboard action would be a better way to approach window-wide hotkeys.
Here is a small example of how it could be done:
public class FrameHotkey
{
public static void main ( final String[] args )
{
SwingUtilities.invokeLater ( new Runnable ()
{
@Override
public void run ()
{
final JFrame frame = new JFrame ();
frame.setLayout ( new FlowLayout ( FlowLayout.CENTER, 15, 15 ) );
frame.add ( new JLabel ( "Field 1:" ) );
frame.add ( new JTextField ( "Field 1", 15 ) );
frame.add ( new JLabel ( "Field 2:" ) );
frame.add ( new JTextField ( "Field 2", 15 ) );
// Hotkey for the F1 in window
frame.getRootPane ().registerKeyboardAction ( new ActionListener ()
{
@Override
public void actionPerformed ( final ActionEvent e )
{
JOptionPane.showMessageDialog ( frame, "F1 have been pressed!" );
}
}, KeyStroke.getKeyStroke ( KeyEvent.VK_F1, 0 ), JComponent.WHEN_IN_FOCUSED_WINDOW );
frame.setDefaultCloseOperation ( WindowConstants.EXIT_ON_CLOSE );
frame.pack ();
frame.setLocationRelativeTo ( null );
frame.setVisible ( true );
}
} );
}
}
Note that I'm registering hotkey on JRootPane
of the JFrame
here, but it generally does't matter because condition is JComponent.WHEN_IN_FOCUSED_WINDOW
- meaning that you can register it on any component in the window and as long as the window is focused in the system you will receive the action event.
Alternatively, if your application has a JMenuBar
with menu items that would perform the actions you desire - you can specify accelerators for those menu items and they will handle the action upon specified hotkey on their own.
I also recommend reading the article from Swing tutorial camickr offered in the other answer.
Upvotes: 0