I have a JFrame with multiple panels that accumulates in a fairly complex Swing UI. I want to add Keyboard support so that regardless of component focus a certain Key press, for example the [ENTER] key, causes a listener to react.
I tried adding a KeyListener to the JFrame but that doesn't work if another JComponent is selected which changes focus.
Is there a proper way to do this?
Registering a KeyEventDispatcher with the KeyboardFocusManager allows you to see all key events before they are sent to the focused component. You can even modify the event or prevent it from beeing delivered to the focused component:
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
new KeyEventDispatcher() {
public boolean dispatchKeyEvent(KeyEvent e) {
//Get the char which was pressed from the KeyEvent:
e.getKeyChar();
//Return 'true' if you want to discard the event.
return false;
}
});
If you just want to get the key inputs for one window / component or just for specific keys, you can use KeyBindings as kleopatra suggested. As an example on how to register to the keyboard event on Enter pressed (You may use any VK_ provided by KeyEvent, for modifiers [alt, ctrl, etc.] see InputEvent) see:
JFrame frame = new JFrame(); //e.g.
JPanel content = (JPanel)frame.getContentPane();
content.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_ENTER,0),"enterDown");
content.getActionMap().put("enterDown",new AbstractAction() {
private static final long serialVersionUID = 1l;
#Override public void actionPerformed(ActionEvent e) {
//This action is called, as the Enter-key was pressed.
}
});
The way I do it is to make the JFrame focusable and append the listener to it. Then, iterate through all the JFrame children and make everything else not focusable.
Of course this only works if you don't have text boxes or similar as they will become non editable.
Related
I have two JPanels inside another JPanel. One of them has a JTextField inside, another few JButtons. I want focus to be set on the JTextField every time user starts typing (even when one of the buttons has focus at the moment).
KeyListener won't work, because in order for it to trigger key events, the component it is registered to must be focusable AND have focus, this means you'd have to attach a KeyListener to EVERY component that might be visible on the screen, this is obviously not a practical idea.
Instead, you could use a AWTEventListener which allows you to register a listener that will notify you of all the events been processed through the event queue.
The registration process allows you to specify the events your are interested, so you don't need to constantly try and filter out events which your not interested in
For example. Now, you can automatically focus the textField when a key is triggered, but you should check to see if the event was triggered by the text field and ignore it if it was
One of the other things you would be need to do is re-dispatch the key event to the text field when it isn't focused, otherwise the field will not show the character which triggered it...
Something like...
if (Character.isLetterOrDigit(e.getKeyChar())) {
filterField.setText(null);
filterField.requestFocusInWindow();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
filterField.dispatchEvent(e);
}
});
}
as an example
You need to attach a KeyListener to all controls in your JPanel, with a reference to the JTextField you want to focus like so:
panel.addKeyListener(new KeyPressedListener(yourTextField));
button1.addKeyListener(new KeyPressedListener(yourTextField));
button2.addKeyListener(new KeyPressedListener(yourTextField));
class KeyPressedListener implements KeyListener
{
private JTextField textFieldToFocus;
public KeyPressedListener(JTextField textFieldToFocus)
{
this.textFieldToFocus = textFieldToFocus;
}
#Override
public void keyPressed(KeyEvent e) {
textFieldToFocus.requestFocus();
}
}
How can I get rid of the sound when I give focus to an uneditable JTextField or JTextPane?
Whenever I transfer focus to a JTextPane which is uneditable and hit Enter, a sound plays which is equals to the "beep" of the Toolkit class:
Toolkit.getDefaultToolkit.beet();
How can I make it play no sound?
You might be able to try the idea from this question, quoted:
The idea is to get the beep action for the text field and disabling it.
JTextField field = new JTextField();
Action action;
action = field.getActionMap().get(DefaultEditorKit.beepAction);
action.setEnabled(false);
If that does not work, you can try to add a KeyListener, that would consume the KeyEvent that causes the beep.
JTextField textField = new JTextField();
textField.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ENTER){
// will consume the event and stop it from processing normally
e.consume();
}
}
});
You can override the beep method from the Toolkit class:
public class MuteToolkit extends Toolkit {
public void beep() {
//do nothing
}
// [...] other methods
}
Then, set this class as the default toolkit:
System.setProperty("awt.toolkit", "package.MuteToolkit");
But may be not the best option, considering it disables all beeps.
I have a JFrame called gameFrame and I added a Jpanel called introPanel to it, gameFrame.add(introPanel) and I wanted to listen to the JPanel with a keylistener so I added one. When the user presses Enter, I removed the JPanel from the gameFrame and added the MainMenu theoretically, however the program doesnt listen to my keys. So i looked online and through SO and found out that I had to make the panel focusable so I did:
public IntroMenuStart() {
this.addKeyListener(this);
this.setFocusable(true);
this.requestFocusInWindow();
}
However this did not work either. What else can I do to fix this?
Each Panel is a seperate class and they all get removed from gameframe and the next panel is added.
I would prefer to do this with keylistener.
EDIT
I fixed it by including this in my code for anyone wanting to know but I'm going to be changing my code to Keybindings like the 2 answers suggsted.
public void addNotify() {
super.addNotify();
requestFocus();
}
i want to listne to the panel with a key listener so i add one. When
the user presses Enter, i remove to JPanel form the gameFrame and add
MainMenu, theoretically, however the program doesnt listen to my keys
If you search in deep in SO , you'll see that you have to use KeyBindings
KeyListener has 2 greate issues, you listen to all keys and you have to have focus.
Instead KeyBinding you bind for a key and you don't have to be in focus.
Simple Example:
AbstractAction escapeAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//code here example
((JComponent)e.getSource()).setVisible(Boolean.FALSE);
}};
String key = "ESCAPE";
KeyStroke keyStroke = KeyStroke.getKeyStroke(key);
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, key);
component.getActionMap().put(key, escapeAction);
You can use these JComponent constants
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
WHEN_FOCUSED
WHEN_IN_FOCUSED_WINDOW
I believe that JPanel is not focuseable. Instead, use the key bindings.
See How to Use Key Bindings.
This is in the constructor of a JPanel but it does not print anything when I press "h". If more code is needed, I can provide it. Thank you!
String hide = "hide";
this.getInputMap().put(KeyStroke.getKeyStroke('h'), hide);
this.getActionMap().put(hide, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("HIDDEN");
if (isHidden){
slide.setVisible(true);
}else{
slide.setVisible(false);
}
}
});
this.getInputMap()....
You are trying to add the bindings to the default InputMap, which is the InputMap when the component has focus. By default a JPanel does not have focus.
You should try using one of the other InputMaps by using the getInputMap(int) method. Or you will need to make the panel focusable and give it focus.
Read the Swing tutorial on How to Use Key Bindings for more information on the proper variables to use to specify the desired InputMap.
I'm trying to use an editable JComboBox such that upon a user typing into the editor, possible results are displayed in the list part of the combo box.
Unfortunately, I've found that upon using either addItem(item) or getModel().addItem(item), the input typed by the user is overwritten by the first value I added. I've considered storing the editor value, adding items, and then using setSelectedItem() to fix this, but I wan't to preserve the state of any selected text/ caret position, and believe this should be something more trivial, but can't for the life of me figure it out.
JComboBox box = new JComboBox();
box.setModel(new MutableComboBoxModel());
box.setEditable(true);
box.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
// Actual results are retrieved from server via HTTP
box.addItem("Demo");
// Here, the editor window the user was typing in is replaced with the value "Demo".. how to fix this?
}
});
use AutoComplete JComboBox / JTextField,
for listening in JTextComponent is there DocumentListener,
never use KeyListener for Swing JComponents, this listener is designated for AWT Components, for Swing JComponents is there KeyBindings
You need to implement your own MutableComboBoxModel since DefaultComboBoxModel is responsible for the "add item then auto select it" behavior.