My use case is I have some JButtons that fire actions to an ActionListener. I also use keystrokes to fire some of the same action commands to the AcionListener. When the keystroke shortcut fires an action that is also done by one of the buttons I want the button to look as though it was pressed but not to fire the event.
So I have looked into the AbstractButton API and I tried some of the methods there like setSelected but it didn't have the desired effect. Finally I have looked at the method doCLick to see if I could use remove the action firing part but this also doesn't work
367 public void doClick(int pressTime) {
368 Dimension size = getSize();
369 model.setArmed(true);
370 model.setPressed(true);
371 paintImmediately(new Rectangle(0,0, size.width, size.height));
372 try {
373 Thread.currentThread().sleep(pressTime);
374 } catch(InterruptedException ie) {
375 }
376 model.setPressed(false);
377 model.setArmed(false);
378 }
I had thought of removing all the listeners. Running the doClick and then adding them again but I thought something more elegant should be available.
A SSCE would be
public class Test {
public static void main(String[] args) throws InterruptedException{
JFrame jf = new JFrame();
jf.setLocationRelativeTo(null);
JButton jb = new JButton("Test Button");
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("I don't want this to fire");
}
});
jf.getContentPane().add(jb);
jf.pack();
jf.setVisible(true);
Thread.sleep(1000);
clickWithoutFiringAction(jb);
}
public static void clickWithoutFiringAction(JButton button){
Dimension size = button.getSize();
ButtonModel model = button.getModel();
//I tried changing these combinations but I could not get the desired effect
model.setArmed(true);
model.setPressed(true);
button.paintImmediately(new Rectangle(0,0, size.width, size.height));
try {
Thread.sleep(68);
} catch(InterruptedException ie) {
}
model.setPressed(false);
model.setArmed(false);
}
}
JButton uses DefaultButtonModel which has setPressed(boolean) function for generating action performed event using fireActionPerformed(ActionEvent e) function. You will need to extend this model and provide custom implementation for setPressed(boolean b) function for avoiding action event firing. Please refer to the source code of this model class for more details.
class CustomModel extends DefaultButtonModel
{
#Override
public void setPressed(boolean b){
if((isPressed() == b) || !isEnabled()) {
return;
}
if (b) {
stateMask |= PRESSED;
} else {
stateMask &= ~PRESSED;
}
fireStateChanged();
}
}
now, you can set the model: jButton.setModel(new CustomModel());
When you want an action listener to be used by left clicking on a JButton, and you want to perform the action without left clicking on a JBUtton, you write a separate public method that performs the action.
Like you did with the clickWithoutFiringAction method.
So now, all your action listener has to do is perform the separate public method.
final JButton jb = new JButton("Test Button");
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
clickWithoutFiringAction(jb);
}
});
Heh I didn't read carefully, like Sage wrote you must provide custom implementation of setPressed().
Your example works just fine. You don't see the effect because delay between model.setPressed(true); and model.setPressed(false); is to short, just 68 milisecounds. Increase value in Thread.sleep(); to couple seconds and you will see the effect.
model.setArmed(true);
model.setPressed(true);
button.paintImmediately(new Rectangle(0,0, size.width, size.height));
try {
Thread.sleep(68); // to short delay.
} catch(InterruptedException ie) {
}
model.setPressed(false);
model.setArmed(false);
Related
I am trying to create a "passive view" idea where user actions fire the listener but application it self does not.
Consider a case where I need to listen for ComponentResized events. When the user resizes the window I do something. But one button press calls setSize() method of this component as well. When the setSize is called from the program, I do not want the listener to be fired. But when it comes from user action I want.
public class Example extends JFrame {
static boolean stopResizing = false;
public Example() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
ComponentAdapter listener = new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
if (stopResizing == true)
return;
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
stopResizing = true;
setSize(getSize().width + 15, getSize().height);
stopResizing = false;
});
add(changeSizeButton);
pack();
setLocationByPlatform(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Example().setVisible(true);
});
}
}
In the example above, when the window gets resized, "resized" is printed. But when the button is pressed, it is printed as well. I know that I can remove and re-add the component listener, but can this be avoided? In "big" views where multiple listeners are registered, this will be pain.
As I can see by following the call hierarchy of setSize, the event is posted in the EventQueue. That's why the boolean flag does not work. The flag becomes true before the event is fired. So, maybe the question can be derived to "can I manipulate (how safe/trust-able could that be) the EventQueue?". Manipulating it, by adding the stopResizing = false before the fire event is posted.
Another option maybe is to create a static method that iterates all listeners, removes them, runs a Runnable (containing the setSize) and then the method adds them back on. But as far as I know, removing all listeners from a component brutally, will remove Swing's internal listeners as well and components will have unexpected behavior. Maybe there is a way to separate the custom (added by me) listeners from Swing's without keeping references?
I tried to add the stopResizing = false into a invokeLater call, but it does not work either.
Keep in mind this is not about ComponentListeners only. It is for any kind of listeners, for any kind of components. Thus, I want to it to "generalize" it.
UPDATE
Even if I remove the listener, the "resized" is printed.
public class Example extends JFrame {
public Example() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
ComponentAdapter listener = new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
removeComponentListener(listener);
setSize(getSize().width + 15, getSize().height);
addComponentListener(listener);
});
add(changeSizeButton);
pack();
setLocationByPlatform(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Example().setVisible(true);
});
}
}
The reason I want to avoid invokeLater is because the code could implicitly look like:
removeComponentListener(listener);
setSize(getSize().width + 15, getSize().height); //I dont want to fire the listener
addComponentListener(listener);
setSize(getSize().width + 15, getSize().height); // I Want to fire the listener
After spending 2 days reading multiple times EventQueue class, I think I solved it. The solution seems to be a SecondaryLoop while a background thread waits until all events are dispatched (that includes the component event in our case).
public class Example extends JFrame {
public Example() {
super("");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
ComponentAdapter listener = new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
removeComponentListener(listener);
setSize(new Dimension(getSize().width + 1, getSize().height));
waitUntilAllEventsAreDispatched();
addComponentListener(listener);
setSize(new Dimension(getSize().width + 1, getSize().height)); //I want here to print
});
add(changeSizeButton);
pack();
setLocationByPlatform(true);
}
private EventQueue eventQueue() {
return Toolkit.getDefaultToolkit().getSystemEventQueue();
}
private void waitUntilAllEventsAreDispatched() {
SecondaryLoop secondaryLoop = eventQueue().createSecondaryLoop();
new Thread(() -> {
while (eventQueue().peekEvent() != null)
;
secondaryLoop.exit();
}).start();
secondaryLoop.enter();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new Example().setVisible(true);
});
}
}
This example will print "RESIZED" only one time.
It also works with a boolean flag, as expected:
ComponentAdapter listener = new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
if (allListenersDisabled)
return;
System.out.println("RESIZED");
}
};
addComponentListener(listener);
JButton changeSizeButton = new JButton("Change size");
changeSizeButton.addActionListener(e -> {
allListenersDisabled = true;
setSize(new Dimension(getSize().width + 1, getSize().height));
waitUntilAllEventsAreDispatched();
allListenersDisabled = false;
setSize(new Dimension(getSize().width + 1, getSize().height)); //I want here to print
});
I tried to add the stopResizing = false into a invokeLater call, but it does not work either.
It does work. I do not know how did you try, but this works:
changeSizeButton.addActionListener(e -> {
stopResizing = true;
setSize(getSize().width + 15, getSize().height);
SwingUtilities.invokeLater(() -> {
stopResizing = false;
});
});
One solution is to remove the event listener before calling setSize(). Then add it back after.
I'm trying to implement a KeyListener for my JFrame. On the constructor, I'm using this code:
System.out.println("test");
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) { System.out.println( "tester"); }
public void keyReleased(KeyEvent e) { System.out.println("2test2"); }
public void keyTyped(KeyEvent e) { System.out.println("3test3"); }
});
When I run it, the test message comes up in my console. However, when I press a key, I don't get any of the other messages, as if the KeyListener was not even there.
I was thinking that it could be because the focus is not on the JFrame
and so they KeyListener doesn't receive any events. But, I'm pretty sure it is.
Is there something that I am missing?
If you don't want to register a listener on every component,
you could add your own KeyEventDispatcher to the KeyboardFocusManager:
public class MyFrame extends JFrame {
private class MyDispatcher implements KeyEventDispatcher {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("tester");
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
System.out.println("2test2");
} else if (e.getID() == KeyEvent.KEY_TYPED) {
System.out.println("3test3");
}
return false;
}
}
public MyFrame() {
add(new JTextField());
System.out.println("test");
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
manager.addKeyEventDispatcher(new MyDispatcher());
}
public static void main(String[] args) {
MyFrame f = new MyFrame();
f.pack();
f.setVisible(true);
}
}
You must add your keyListener to every component that you need. Only the component with the focus will send these events. For instance, if you have only one TextBox in your JFrame, that TextBox has the focus. So you must add a KeyListener to this component as well.
The process is the same:
myComponent.addKeyListener(new KeyListener ...);
Note: Some components aren't focusable like JLabel.
For setting them to focusable you need to:
myComponent.setFocusable(true);
InputMaps and ActionMaps were designed to capture the key events for the component, it and all of its sub-components, or the entire window. This is controlled through the parameter in JComponent.getInputMap(). See How to Use Key Bindings for documentation.
The beauty of this design is that one can pick and choose which key strokes are important to monitor and have different actions fired based on those key strokes.
This code will call dispose() on a JFrame when the escape key is hit anywhere in the window. JFrame doesn't derive from JComponent so you have to use another component in the JFrame to create the key binding. The content pane might be such a component.
InputMap inputMap;
ActionMap actionMap;
AbstractAction action;
JComponent component;
inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
actionMap = component.getActionMap();
action = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent e)
{
dispose();
}
};
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "dispose");
actionMap.put("dispose", action);
I got the same problem until i read that the real problem is about FOCUS the your JFrame has already added Listeners but tour frame is never on Focus because you got a lot of components inside your JFrame that also are focusable so try:
JFrame.setFocusable(true);
Good Luck
KeyListener is low level and applies only to a single component. Despite attempts to make it more usable JFrame creates a number of component components, the most obvious being the content pane. JComboBox UI is also often implemented in a similar manner.
It's worth noting the mouse events work in a strange way slightly different to key events.
For details on what you should do, see my answer on Application wide keyboard shortcut - Java Swing.
Deion (and anyone else asking a similar question), you could use Peter's code above but instead of printing to standard output, you test for the key code PRESSED, RELEASED, or TYPED.
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
if (e.getKeyCode() == KeyEvent.VK_F4) {
dispose();
}
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
if (e.getKeyCode() == KeyEvent.VK_F4) {
dispose();
}
} else if (e.getID() == KeyEvent.KEY_TYPED) {
if (e.getKeyCode() == KeyEvent.VK_F4) {
dispose();
}
}
return false;
}
in order to capture key events of ALL text fields in a JFrame,
one can employ a key event post processor.
Here is a working example, after you add the obvious includes.
public class KeyListenerF1Demo extends JFrame implements KeyEventPostProcessor {
public static final long serialVersionUID = 1L;
public KeyListenerF1Demo() {
setTitle(getClass().getName());
// Define two labels and two text fields all in a row.
setLayout(new FlowLayout());
JLabel label1 = new JLabel("Text1");
label1.setName("Label1");
add(label1);
JTextField text1 = new JTextField(10);
text1.setName("Text1");
add(text1);
JLabel label2 = new JLabel("Text2");
label2.setName("Label2");
add(label2);
JTextField text2 = new JTextField(10);
text2.setName("Text2");
add(text2);
// Register a key event post processor.
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventPostProcessor(this);
}
public static void main(String[] args) {
JFrame f = new KeyListenerF1Demo();
f.setName("MyFrame");
f.pack();
f.setVisible(true);
}
#Override
public boolean postProcessKeyEvent(KeyEvent ke) {
// Check for function key F1 pressed.
if (ke.getID() == KeyEvent.KEY_PRESSED
&& ke.getKeyCode() == KeyEvent.VK_F1) {
// Get top level ancestor of focused element.
Component c = ke.getComponent();
while (null != c.getParent())
c = c.getParent();
// Output some help.
System.out.println("Help for " + c.getName() + "."
+ ke.getComponent().getName());
// Tell keyboard focus manager that event has been fully handled.
return true;
}
// Let keyboard focus manager handle the event further.
return false;
}
}
This should help
yourJFrame.setFocusable(true);
yourJFrame.addKeyListener(new java.awt.event.KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
System.out.println("you typed a key");
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("you pressed a key");
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("you released a key");
}
});
Hmm.. what class is your constructor for? Probably some class extending JFrame? The window focus should be at the window, of course but I don't think that's the problem.
I expanded your code, tried to run it and it worked - the key presses resulted as print output. (run with Ubuntu through Eclipse):
public class MyFrame extends JFrame {
public MyFrame() {
System.out.println("test");
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
System.out.println("tester");
}
public void keyReleased(KeyEvent e) {
System.out.println("2test2");
}
public void keyTyped(KeyEvent e) {
System.out.println("3test3");
}
});
}
public static void main(String[] args) {
MyFrame f = new MyFrame();
f.pack();
f.setVisible(true);
}
}
I have been having the same problem. I followed Bruno's advice to you and found that adding a KeyListener just to the "first" button in the JFrame (ie, on the top left) did the trick. But I agree with you it is kind of an unsettling solution. So I fiddled around and discovered a neater way to fix it. Just add the line
myChildOfJFrame.requestFocusInWindow();
to your main method, after you've created your instance of your subclass of JFrame and set it visible.
lol .... all you have to do is make sure that
addKeyListener(this);
is placed correctly in your code.
You could have custom JComponents set their parent JFrame focusable.
Just add a constructor and pass in the JFrame. Then make a call to setFocusable() in paintComponent.
This way the JFrame will always receive KeyEvents regardless of whether other components are pressed.
I've implemented right mouse click for open menu listener on my main Jframe, it works fine except one problem. One out of 5 (give or take) clicks it not responding, this can be very annoying for the user. Here is my code:
contentPane = new JPanel();
contentPane.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON3)
{
//Do Stuff
}
}
});
Can you please help me
You won't get clicks from sub-components of contentPane.
I think your problem is that you have added things to your panel. When the user clicks at regions occupied by a sub-component, that sub-component get's the click event.
Quick fix: I would recommend you to add the same mouse listener to all sub-components.
You are not "clicking"
A click is when the mouse is pressed and release really quickly. If you are not careful you might get events for (for instance) "pressed, moved, released" instead of "clicked".
Quick fix: use mouseReleased event instead.
Use this Code instead:
private MouseAdapter listener = new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
if (downer) {
downer = false;
if (new Rectangle(e.getComponent().getLocationOnScreen(), e.getComponent().getSize())
.contains(e.getLocationOnScreen())) {
downer = false;
// CODE
new Thread(new Runnable(){
public void run(){
//Your Listener code
}
}).start();
/// COde
}
}
}
boolean downer = false;
public void mousePressed(java.awt.event.MouseEvent e) {
downer = true;
}
};
This code only reacts if you press on the component and release on the component AND starts a new Thread for the custom task. This should work allways, because the AWT Thread isnt blocked with long calculations.
private void OptionsActionPerformed(java.awt.event.ActionEvent evt)
{
// After clicking on button X, I want 4 other buttons to show up
// in a sequential order
ButtonTrue();
}
public void ButtonTrue()
{
Audio_Options.setVisible(true);
letsSleep();
Control_Options.setVisible(true);
letsSleep();
Display_Options.setVisible(true);
letsSleep();
Network_Options.setVisible(true);
}
public void letsSleep()
{
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
Logger.getLogger(MainMenu.class.getName()).log(Level.SEVERE, null, ex);
}
}
I have 4 buttons. I want them to appear in a sequential order such as :
Button1 - 10seconds - Button2 - 10 seconds - Button3 - 10seconds - Button 4
Problem: Whenever I call the function "ButtonTrue()", they all appear together after waiting 30 seconds. What can cause this problem to occur?
don't use Thread.sleep(int) for Swing JComponent, because blocking current EDT
you have look at Swing Timer
You should use different Threads for this:
javax.swing.Timer timer = new Timer(10000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//...Update the progress bar...
Control_Options.setVisible(true);
timer.stop();
}
});
timer.start();
Your buttons have to be final to be in scope for the anonymous ActionListener.
I think the problem is that all setVisble invocations are within one thread, which isn't EventDispatchThread. You could try calling:
if(EventQueue.isDispatchThread()) {
ButtonTrue();
} else {
EventQueue.invokeAndWait(new Runnable() {
ButtonTrue();
});
}
I'm trying to implement a KeyListener for my JFrame. On the constructor, I'm using this code:
System.out.println("test");
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) { System.out.println( "tester"); }
public void keyReleased(KeyEvent e) { System.out.println("2test2"); }
public void keyTyped(KeyEvent e) { System.out.println("3test3"); }
});
When I run it, the test message comes up in my console. However, when I press a key, I don't get any of the other messages, as if the KeyListener was not even there.
I was thinking that it could be because the focus is not on the JFrame
and so they KeyListener doesn't receive any events. But, I'm pretty sure it is.
Is there something that I am missing?
If you don't want to register a listener on every component,
you could add your own KeyEventDispatcher to the KeyboardFocusManager:
public class MyFrame extends JFrame {
private class MyDispatcher implements KeyEventDispatcher {
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
System.out.println("tester");
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
System.out.println("2test2");
} else if (e.getID() == KeyEvent.KEY_TYPED) {
System.out.println("3test3");
}
return false;
}
}
public MyFrame() {
add(new JTextField());
System.out.println("test");
KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
manager.addKeyEventDispatcher(new MyDispatcher());
}
public static void main(String[] args) {
MyFrame f = new MyFrame();
f.pack();
f.setVisible(true);
}
}
You must add your keyListener to every component that you need. Only the component with the focus will send these events. For instance, if you have only one TextBox in your JFrame, that TextBox has the focus. So you must add a KeyListener to this component as well.
The process is the same:
myComponent.addKeyListener(new KeyListener ...);
Note: Some components aren't focusable like JLabel.
For setting them to focusable you need to:
myComponent.setFocusable(true);
InputMaps and ActionMaps were designed to capture the key events for the component, it and all of its sub-components, or the entire window. This is controlled through the parameter in JComponent.getInputMap(). See How to Use Key Bindings for documentation.
The beauty of this design is that one can pick and choose which key strokes are important to monitor and have different actions fired based on those key strokes.
This code will call dispose() on a JFrame when the escape key is hit anywhere in the window. JFrame doesn't derive from JComponent so you have to use another component in the JFrame to create the key binding. The content pane might be such a component.
InputMap inputMap;
ActionMap actionMap;
AbstractAction action;
JComponent component;
inputMap = component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
actionMap = component.getActionMap();
action = new AbstractAction()
{
#Override
public void actionPerformed(ActionEvent e)
{
dispose();
}
};
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "dispose");
actionMap.put("dispose", action);
I got the same problem until i read that the real problem is about FOCUS the your JFrame has already added Listeners but tour frame is never on Focus because you got a lot of components inside your JFrame that also are focusable so try:
JFrame.setFocusable(true);
Good Luck
KeyListener is low level and applies only to a single component. Despite attempts to make it more usable JFrame creates a number of component components, the most obvious being the content pane. JComboBox UI is also often implemented in a similar manner.
It's worth noting the mouse events work in a strange way slightly different to key events.
For details on what you should do, see my answer on Application wide keyboard shortcut - Java Swing.
Deion (and anyone else asking a similar question), you could use Peter's code above but instead of printing to standard output, you test for the key code PRESSED, RELEASED, or TYPED.
#Override
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getID() == KeyEvent.KEY_PRESSED) {
if (e.getKeyCode() == KeyEvent.VK_F4) {
dispose();
}
} else if (e.getID() == KeyEvent.KEY_RELEASED) {
if (e.getKeyCode() == KeyEvent.VK_F4) {
dispose();
}
} else if (e.getID() == KeyEvent.KEY_TYPED) {
if (e.getKeyCode() == KeyEvent.VK_F4) {
dispose();
}
}
return false;
}
in order to capture key events of ALL text fields in a JFrame,
one can employ a key event post processor.
Here is a working example, after you add the obvious includes.
public class KeyListenerF1Demo extends JFrame implements KeyEventPostProcessor {
public static final long serialVersionUID = 1L;
public KeyListenerF1Demo() {
setTitle(getClass().getName());
// Define two labels and two text fields all in a row.
setLayout(new FlowLayout());
JLabel label1 = new JLabel("Text1");
label1.setName("Label1");
add(label1);
JTextField text1 = new JTextField(10);
text1.setName("Text1");
add(text1);
JLabel label2 = new JLabel("Text2");
label2.setName("Label2");
add(label2);
JTextField text2 = new JTextField(10);
text2.setName("Text2");
add(text2);
// Register a key event post processor.
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addKeyEventPostProcessor(this);
}
public static void main(String[] args) {
JFrame f = new KeyListenerF1Demo();
f.setName("MyFrame");
f.pack();
f.setVisible(true);
}
#Override
public boolean postProcessKeyEvent(KeyEvent ke) {
// Check for function key F1 pressed.
if (ke.getID() == KeyEvent.KEY_PRESSED
&& ke.getKeyCode() == KeyEvent.VK_F1) {
// Get top level ancestor of focused element.
Component c = ke.getComponent();
while (null != c.getParent())
c = c.getParent();
// Output some help.
System.out.println("Help for " + c.getName() + "."
+ ke.getComponent().getName());
// Tell keyboard focus manager that event has been fully handled.
return true;
}
// Let keyboard focus manager handle the event further.
return false;
}
}
This should help
yourJFrame.setFocusable(true);
yourJFrame.addKeyListener(new java.awt.event.KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
System.out.println("you typed a key");
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("you pressed a key");
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("you released a key");
}
});
Hmm.. what class is your constructor for? Probably some class extending JFrame? The window focus should be at the window, of course but I don't think that's the problem.
I expanded your code, tried to run it and it worked - the key presses resulted as print output. (run with Ubuntu through Eclipse):
public class MyFrame extends JFrame {
public MyFrame() {
System.out.println("test");
addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
System.out.println("tester");
}
public void keyReleased(KeyEvent e) {
System.out.println("2test2");
}
public void keyTyped(KeyEvent e) {
System.out.println("3test3");
}
});
}
public static void main(String[] args) {
MyFrame f = new MyFrame();
f.pack();
f.setVisible(true);
}
}
I have been having the same problem. I followed Bruno's advice to you and found that adding a KeyListener just to the "first" button in the JFrame (ie, on the top left) did the trick. But I agree with you it is kind of an unsettling solution. So I fiddled around and discovered a neater way to fix it. Just add the line
myChildOfJFrame.requestFocusInWindow();
to your main method, after you've created your instance of your subclass of JFrame and set it visible.
lol .... all you have to do is make sure that
addKeyListener(this);
is placed correctly in your code.
You could have custom JComponents set their parent JFrame focusable.
Just add a constructor and pass in the JFrame. Then make a call to setFocusable() in paintComponent.
This way the JFrame will always receive KeyEvents regardless of whether other components are pressed.