Reputation: 361
I have two classes that implement KeyListener and FocusListener interfaces. Neither works, but if I don't add the JButton by commenting or removing this: add(whiteJButton)
, then they do work. Could someone explain to me why this happens? Thanks in advance.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class JFrames {
public static void main(String[] args) {
JFrame myJFrame = new JFrame("MyJFrame");
myJFrame.setBounds(400, 400, 500, 500);
myJFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyJPanel myJPanel = new MyJPanel();
myJFrame.add(myJPanel);
// Doesn't work
myJFrame.addKeyListener(new MyKeyListener());
// Doesn't work
myJFrame.addFocusListener(new MyFocusListener());
myJFrame.setVisible(true);
}
static class MyJPanel extends JPanel {
public MyJPanel() {
JButton whiteJButton = new JButton("WHITE");
add(whiteJButton);
whiteJButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setBackground(Color.WHITE);
}
});
}
}
}
class MyKeyListener implements KeyListener {
@Override
public void keyTyped(KeyEvent e) {
System.out.println("keyTyped: " + e.getKeyChar());
}
@Override
public void keyPressed(KeyEvent e) {
System.out.println("keyPressed: " + e.getKeyChar());
}
@Override
public void keyReleased(KeyEvent e) {
System.out.println("keyReleased: " + e.getKeyChar());
}
}
class MyFocusListener implements FocusListener {
@Override
public void focusGained(FocusEvent e) {
System.out.println("focusGained");
}
@Override
public void focusLost(FocusEvent e) {
System.out.println("focusLost");
}
}
Upvotes: 1
Views: 727
Reputation: 285403
The problem is that the button steals focus from the JFrame, preventing the focus listener from working (focus is already gone). Some solutions if you absolutely need to use a KeyListener are kludges, including making the JButton not focusable: whiteJButton.setFocusable(false);
, but if you do this, you need to do this for all components added. And yes you can request focus as the other answer suggests, but it should be requestFocusInWindw()
, not requestFocus()
(many similar questions explain why this is so). And if you do this and the components are still focusable, then the whole thing breaks down if a component gains focus -- not good.
Better (as per comments) is to use Key Bindings which don't require focus to work if you use the correct InputMap. Note that key bindings is how Swing itself traps keystrokes for components, so using this would follow with the Swing general structure and contracts. The problem with Key Bindings is that you have to bind each key that you wish to trap, but you can use for loops to assist with this.
Another solution is to use a KeyEventDispatcher to the keyboard focus manager:
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(new KeyEventDispatcher() {
@Override
public boolean dispatchKeyEvent(KeyEvent e) {
// code goes here
return false;
}
});
Example using Key Bindings:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;
@SuppressWarnings("serial")
public class KeyboardFun extends JPanel {
private InputMap inputMap = getInputMap(WHEN_IN_FOCUSED_WINDOW);
private ActionMap actionMap = getActionMap();
public KeyboardFun() {
setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
setLayout(new GridLayout(0, 8, 3, 3));
for (char c = 'A'; c <= 'Z'; c++) {
final String text = String.valueOf(c);
JButton button = new JButton(text);
button.addActionListener(e -> {System.out.println("Key pressed: " + text);});
add(button);
setBinding(c, button);
}
}
private void setBinding(char c, final JButton button) {
KeyStroke keyStroke = KeyStroke.getKeyStroke(Character.toLowerCase(c));
inputMap.put(keyStroke, keyStroke.toString());
actionMap.put(keyStroke.toString(), new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
button.doClick();
}
});
}
private static void createAndShowGui() {
KeyboardFun mainPanel = new KeyboardFun();
JFrame frame = new JFrame("KeyboardFun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Upvotes: 2
Reputation: 363
You must focus the object that you add listener, try add listener to "myJFrame". You can't focus JPanel.
Your JPanel is probably isn't focusable at start and your JButton gets Focus. So you can also add this codes to "myJPanel" :
setFocusable(true);
requestFocusInWindow();
Upvotes: 0