Reputation: 2019
I've a frame with a text field, a table and two buttons!
I would like to add a KeyListener to listen when a modifier key is pressed and released, in order to change the text of the Ok button and let user select different methods. The listener should work regardless which component is in focus.
For example:
Right now I've found this solution, but it seems a bit cluncky and not elegant at all.
public class MyFrame extends JFrame {
private boolean shiftPressed;
private MyDispatcher keyDispatcher;
public MyFrame () {
super();
initGUI();
shiftPressed = false;
KeyboardFocusManager currentManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
keyDispatcher = new MyDispatcher();
currentManager.addKeyEventDispatcher(keyDispatcher);
//...remain of code
}
@Override
public void dispose() {
// I remove the dispatcher when the frame is closed;
KeyboardFocusManager currentManager = KeyboardFocusManager
.getCurrentKeyboardFocusManager();
currentManager.removeKeyEventDispatcher(keyDispatcher);
super.dispose();
}
private class MyDispatcher implements KeyEventDispatcher {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getModifiers() == KeyEvent.SHIFT_MASK) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("Shift Pressed");
shiftPressed = true;
btnOk.setText("Ok and Close");
}
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
if(!e.isShiftDown() && shiftPressed) {
System.out.println("Shift Released");
btnOk.setText("Ok");
}
}
return false;
}
}
}
Any suggestion on how to improve the code?
Upvotes: 1
Views: 137
Reputation: 17971
I would like to add a KeyListener to listen when a modifier key is pressed and released, in order to change the text of the Ok button and let user select different methods. The listener should work regardless which component is in focus.
IMHO using Key bindings is a more flexible and reliable approach that brings with these benefits:
WHEN_FOCUSED
, WHEN_IN_FOCUSED_WINDOW
, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
Having said that, we can create KeyStrokes
using appropriate masks like follows:
KeyStroke shiftKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SHIFT, KeyEvent.SHIFT_DOWN_MASK);
And we can even specify that the key stroke will be triggered on a key release event:
KeyStroke shiftReleasedKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_SHIFT, 0, true);
Note: 0
means no modifiers and true
is a flag indicating the key stroke represents a key release event. See the API for more details.
Now, what I'd do is create a wrapper JPanel
that contains all my components and attach actions using WHEN_IN_FOCUSED_WINDOW
condition:
JPanel wrapperPanel = new JPanel();
...
InputMap wrapperPanelInputMap = wrapperPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
wrapperPanelInputMap.put(shiftKeyStroke, "pressedAction");
wrapperPanelInputMap.put(shiftReleasedKeyStroke, "releasedAction");
...
wrapperPanel.getActionMap().put("pressedAction", pressedAction);
wrapperPanel.getActionMap().put("releasedAction", releasedAction);
Where both pressedAction
and releasedAction
are different Actions that can do whatever you want (i.e.: change button's text).
The only minor disadvantage is the LOC needed to set key bindings. However you can avoid boilerplate code by creating a reusable panel that can accept Actions for the different modifiers (SHIFT, ALT, CTRL, etc.) and set key bindings internally in that class.
Upvotes: 2
Reputation: 11327
Your approach is ok but it uses a global focus manager. It's a little bit dangerous and can provoke some errors when you for example forget to dispose your frame. Much better is to add your listener to all focusable components in the frame.
private static void addToAll(Component aStart, KeyListener aListener) {
if (aStart.isFocusable()) {
aStart.addKeyListener(aListener);
}
if (aStart instanceof Container) {
Container container = (Container) aStart;
for (Component comp : container.getComponents()) {
addToAll(comp, aListener);
}
}
}
To use it:
public MyFrame () {
super();
initGUI();
shiftPressed = false;
keyDispatcher = new MyDispatcher();
addToAll(this.getContentPane(), keyDispatcher);
//...remain of code
}
Disadvantage of this approach: when you modify your GUI dynamically (add/remove components after your GUI is created and listener is added to all components) you need to remove the listener from all components and add it again.
Upvotes: 1