This question already has an answer here:
Loop doesn't see value changed by other thread without a print statement
(1 answer)
Closed 11 months ago.
I have some problem with my code. I want to implement a keylistener. I have a keyHandler class which takes care about keyinput and a while loop in the main class to check if a certain key is pressed or not. I dont understand the behavior of my code. the strange thing is that every thing works when I put the System.out.println("hello") command in front of my if statement. but when i comment it out my programm doesnt realize that i press the key Im checkin in my if statement. I think i could find a workaround. but i would be very glad to understand this strange behavior. why is this happening. Sorry for my bad english. I hope you guys can help me.
public static void main(String[] args) {
boolean running = true;
JFrame window;
KeyHandler k = new KeyHandler();
window = new JFrame();
window.setVisible(true);
window.addKeyListener(k);
while (running) {
//System.out.println("hello");
if (k.isKeyPressed(KeyEvent.VK_W)) {
System.out.println("--------------------------------------------------------------------------");
}
}
}
//here is the KeyHandler class
public class KeyHandler implements KeyListener {
private boolean[] keysPressed = new boolean[128];
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
keysPressed[e.getKeyCode()] = true;
System.out.println(e.getKeyChar());
System.out.println(keysPressed[e.getKeyCode()]);
}
#Override
public void keyReleased(KeyEvent e) {
keysPressed[e.getKeyCode()] = false;
System.out.println(e.getKeyChar());
System.out.println(keysPressed[e.getKeyCode()]);
}
public boolean isKeyPressed(int keyCode) {
return keysPressed[keyCode];
}
}
The whole purpose of events and event handling is you don't need a loop to listen for events. Simply start your UI, add the listeners to a list, and allow the listeners to handle the processing.
Create a listener
public interface MyListener extends EventListener {
public void doSomething();
}
Now use it. With this code it just spits out some text when W is pressed, but the listeners could be another component or anything that uses the interface. No need for extra loops.
public class Main {
private EventListenerList listenerList = new EventListenerList();
public Main() {
JFrame frame = new JFrame();
addListener(new MyListener() {
#Override
public void doSomething() {
System.out.println("Hello 1");
}
});
addListener(new MyListener() {
#Override
public void doSomething() {
System.out.println("Hello 2");
}
});
frame.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_W) {
fireMyEvent();
}
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void addListener(MyListener listener) {
listenerList.add(MyListener.class, listener);
}
private void fireMyEvent() {
MyListener[] listeners = listenerList.getListeners(MyListener.class);
if (listeners == null) {
return;
}
for (MyListener listener : listeners) {
listener.doSomething();
}
}
public static void main(String [] args) {
new Main();
}
}
Here's a link that might help. I would not check for a key being pressed by that method. You are creating a resource hog, first off: by having
boolean running = true;
you then enter a while loop,
while (running) {
do x;
}
this can create a spin lock on some systems, this is a very bad practice. As user Lei Yang stated it is really not needed especially with the classes we have today and modern GUI's, your creating an endless loop. One this most certainly is a way to slow down a system, two you really can't continue coding past that point as you have no way to exit the loop. Some IDE's also have a check that won't allow your application to start if you have a loop that is infinite, almost all will at least give you a warning. you should at least if you are looking for a certain key and have to implement it that way do:
while (running) {
//System.out.println("hello");
if (k.isKeyPressed(KeyEvent.VK_W) = "e") {
running = false;
}
}
at least that won't be an endless loop.
Related
I have a simple JFrame that asks a user for a confirmation to exit when they click X to close the window, this works fine. I also wanted the user to be presented with the same option if they also pressed the escape key (ESC), unfortunately it seems to be trapped in a state where the escape key seems to be constantly pressed when it is not. Where is the mistake and why?
public class Zz extends javax.swing.JFrame implements Events {
boolean exitAttempt = false;
java.awt.event.WindowEvent closeEvent;
//public Zz() {}
public static void main(java.lang.String[] args) {
Zz zz = new Zz();
zz.dostuff();
}
public void dostuff() {
setSize(800, 600);
setLocationRelativeTo(null);
setResizable(false);
setDefaultCloseOperation(javax.swing.JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent we) {
exitAttempt = true;
}
});
closeEvent = new java.awt.event.WindowEvent(
this, java.awt.event.WindowEvent.WINDOW_CLOSING);
setVisible(true);
java.awt.Canvas canvas = new java.awt.Canvas();
canvas.setPreferredSize(new java.awt.Dimension(800, 600));
add(canvas);
Keys keys = new Keys();
addKeyListener(keys);
pack();
while (true) {
events(keys);
if (exitAttempt) {
if (javax.swing.JOptionPane.YES_OPTION ==
showConfirmDialog("Do you want to Exit ?",
"Confirmation:", javax.swing.JOptionPane.YES_NO_OPTION,
javax.swing.JOptionPane.QUESTION_MESSAGE, null)) {
exit();
break; //while loop
}
exitAttempt = false;
}
}
dispose();
}
public void triggerCloseEvent() {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
dispatchEvent(closeEvent);
}
});
}
public int showConfirmDialog(java.lang.Object message,
java.lang.String title, int optionType, int messageType,
javax.swing.Icon icon) {
return javax.swing.JOptionPane.showConfirmDialog(
this, message, title, optionType, messageType, icon);
}
public boolean exit() {
setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
return true;
}
public void events(Keys keys) {
System.out.println((keys.getPressed())[java.awt.event.KeyEvent.VK_ESCAPE]);
if ((keys.getPressed())[java.awt.event.KeyEvent.VK_ESCAPE]) {
triggerCloseEvent();
}
}
}
interface Events {
public void events(Keys keys);
}
class Keys implements java.awt.event.KeyListener {
private final boolean[] pressed;
public Keys() {
pressed = new boolean[256];
}
public void keyTyped(java.awt.event.KeyEvent event) {}
public void keyPressed(java.awt.event.KeyEvent event) {
pressed[event.getKeyCode()] = true;
}
public void keyReleased(java.awt.event.KeyEvent event) {
pressed[event.getKeyCode()] = false;
}
public boolean[] getPressed() {
return pressed;
}
}
I have a simple JFrame that asks a user for a confirmation to exit when they click X to close the window, this works fine
Your design is incorrect.
You should NOT have a while (true) loop.
GUI's are event driven. You create the frame and make it visible. That is the end of the code in your main() method or constructor. The GUI will then sit there forever doing nothing.
However, eventually, the user will then generate events that the GUI responds to.
This means that the code to display the JOptionPane should be moved to the windowClosing() method of your WindowListener.
See: Closing an Application for some basics and helpful classes to use.
I also wanted the user to be presented with the same option if they also pressed the escape key
Don't use a KeyListener.
Swing was designed to be used with Key Bindings.
You can use the ExitAction contained in the Closing an Application link when creating your key bindings:
KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
InputMap im = frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
im.put(escapeKeyStroke, "escape");
frame.getRootPane().getActionMap().put("escape", new ExitAction());
Read the Swing tutorial. There are section on :
How to Uuse Key Bindings
How to Use Actions
to help explain how the above suggestions work.
The tutorial examples will also show you how to better structure your code and note that the never use a while (true) loop.
I'm trying to create a 2d game in Eclipse using a JFrame and keylistener. My code looks like the following:
Main.java
public static void main(String[] args) {
DrawerManager game = new DrawerManager();
JFrame frame = new JFrame("Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(game);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.addKeyListener(game);
}
DrawerManager.java
public class DrawerManager extends JPanel implements KeyListener {
public Scene scene = new GameScene( sceneContext );
int frame = 0;
public long last = 0;
public int ups = 30; //UPDATES PER SECOND
public DrawerManager() {
}
#Override
public void processKeyEvent(KeyEvent event) {
super.processKeyEvent(event);
}
#Override
protected void paintComponent(Graphics g) {
...
}
#Override
public void keyTyped(KeyEvent e) {}
#Override
public void keyPressed(KeyEvent e) {
scene.keyPressed(e);
}
#Override
public void keyReleased(KeyEvent e) {
scene.keyReleased(e);
}
}
GameScene.java
public class GameScene extends Scene{
GameContext context;
Player player;
public GameScene(SceneContext sceneContext) {
...
}
public void keyPressed(KeyEvent event) {
Logic here
}
public void keyReleased(KeyEvent event) {
Logic here
}
}
When just launching the game it works fine but after some time it randomly stops receiving any events. My initial thought was that it had something to do with holding down multiple keys, but that doesn't seem to be the case. From my tests I can see that holding down space for a couple of seconds for some reason makes it work again. Keep in mind that the issue is very random, sometimes it happens right away and sometimes it doesn't happen for a very long time.
The issue seems very odd, as the game is extremely simple right now I cannot see anything that would interfere with the event listener. I also tried exporting the code to a jar file with no luck...
I'm running the code in Eclipse Oxygen (4.7.1a) on Mac.
Any help is appreciated!
I have three classes, Main, DrawingPanel, and ToolboxPanel. ToolboxPanel contains all my buttons, including an Undo button. DrawingPanel is where I draw objects. I want the undo button to become enabled when an object is drawn on the screen, and disabled when there are no more objects left on the screen. Main creates an instance of DrawingPanel and of ToolboxPanel. I can get my undo button to work correctly if I use static methods and call, say, Main.setUndoStatus(false); from drawingPanel. The setUndoStatus then calls a setter in toolboxPanel. However, I've been reading about the Observer pattern and listeners and think I'm probably not doing it in a best-practice way.
How should I go about this using the observer pattern and/or mouse listeners correctly? (Or any "better" way of doing it).
Here's some code somewhat similar to what I'm doing.
public class Main
{
DrawingPanel drawingPanel;
ToolboxPanel toolboxPanel;
public Main()
{
drawingPanel = new DrawingPanel();
toolboxPanel = new ToolboxPanel(drawingPanel);
}
}
//A static method here to setUndoStatus, but I feel like I shouldn't have it
public static void setUndoStatus(boolean b)
{
{
toolboxPanel.setUndoStatus(b);
}
}
public class ToolboxPanel
{
JButton undoButton;
public ToolboxPanel(DrawingPanel drawingPanel)
{
undoButton = new JButton("Undo");
undoButton.setEnabled(false);
undoButton.addActionListener
(
new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
drawingPanel.undo();
undoButton.setEnabled(drawingPanel.getUndoStatus());
}
}
);
}
public void setUndoStatus(boolean status)
{
undoButton.setEnabled(status);
}
}
public class DrawingPanel
{
public DrawingPanel()
{
addMouseListener(new MouseAdapter()
{
public void mouseReleased(MouseEvent e)
{
//Some code here that's unrelated
if(objectsExist == true) //If something gets drawn, whatever
{
Main.setUndoStatus(true); //Don't like this
}
}
});
}
}
In Java, let's say I have a GUI with 2 buttons, Go and Pause.
When I press Go, "Hello" gets printed out over and over again. When I press Pause, "Hello" no longer gets printed to the screen.
Example: User presses Go button. "Hello" gets printed out for 1 minute until the user presses "Pause."
What is the proper way to express this approach in Java? Is it equivalent to my commented pseudocode within the goButton source?
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
// while user has not pressed the pause button
printHello();
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
Thanks
In order to get this to work, in reasonable fashion, you will need a Thread. This is executed in the background until such time as you decide to cancel/pause it.
This is an EXTREMELY basic example. Normally I'd wrap the task and the GUI up in appropriate classes rather then accessing static references, but it gives a basic idea
public class TestHello {
private static HelloTask task;
public static void main(String[] args) {
Thread thread = new Thread((task = new HelloTask()));
thread.setDaemon(true);
thread.start();
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
JButton goButton = new JButton("Go");
JButton stopButton = new JButton("Stop");
goButton.setActionCommand("Go");
stopButton.setActionCommand("Stop");
ActionHandler handler = new ActionHandler();
goButton.addActionListener(handler);
stopButton.addActionListener(handler);
frame.add(goButton);
frame.add(stopButton);
frame.setVisible(true);
}
public static class ActionHandler implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("Go")) {
task.start();
} else if (e.getActionCommand().equals("Stop")) {
task.pause();
}
}
}
public static class HelloTask implements Runnable {
private static final Object WAIT_LOCK = new Object();
private boolean dump = false;
public void start() {
synchronized (WAIT_LOCK) {
dump = true;
WAIT_LOCK.notify();
}
}
public void pause() {
synchronized (WAIT_LOCK) {
dump = false;
WAIT_LOCK.notify();
}
}
#Override
public void run() {
while (true) {
while (dump) {
System.out.println("Hello");
}
try {
synchronized (WAIT_LOCK) {
WAIT_LOCK.wait();
}
} catch (Exception e) {
}
}
}
}
}
Some further read:
Java Concurrency
Concurrency in Swing
Caveats
NEVER try and modify the GUI from any thread other then the Event Dispatching Thread.
To have responsive UI you would usually have to run printHello() in separate thread. Then as you do processing in this thread, for example, after every print statement, you check some flag boolean isPaused; and stop execution if it is true. When pause button is clicked you set the value of this flag to true.
You need to implement your loop in a separate thread. Otherwise the GUI will become irresponsive and the user might not be able to click the Pause button at all.
With this threaded approach, you also need a flag which indicates whether or not to print out the message. The printing loop can simply stop executing the thread when the flag is set to no longer print.
what about htis:
boolean flag=true;
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
while(true)
{
printHello();
}
}
else if(e.getSource() == pauseButton)
{
pause();
}
}
You can do this in a few ways the simplest being:
You have a boolean flag, keepPrinting and you set it to true when you push the Go button, false when you push the Pause. Next you have a thread somewhere executing a while loop which will print nothing when keepPrinting is false.
The threading here is really important, without it you're going to have your GUI freeze once the user pushes a button as the program prints hello and happily ignores anything else.
Pseudo Code
//GUI
public ThreadedPrinter greeter;
void ButtonGoPushed(args){
greeter.keepPrinting = true;
}
void ButtonPausePushed(args){
greeter.keepPrinting = false;
}
//ThreadedPrinter
boolean keepPrinting
void run(){
while(true){
if(keepPrinting){
print("Hello");
}
sleep(5); //Make sure that this thread yields if the system doesn't do it automatically
}
The good news about java concurrency versus say C++ is that this will just work, you don't have to worry about the boolean being crazy and inconsistent because in java variable sets are atomic. If you want to do more than just set the variable, make a synchronized method that sets the variable and does anything else you want.
Basically to keep UI responsive such task need to be performed in other thread.
There can be various ways in which you can implement this mechanism in java.
I have used simple mechanism of Runnalbe and volatile flag which ensure that thread exists when you call cancelPrint() method
public void actionPerformed(ActionEvent e) {
if(e.getSource() == goButton)
{
//start the thread here
}
else if(e.getSource() == pauseButton)
{
//call cancel here
}
}
public class HelloPrinter implements Runnable {
volatile boolean cancel = false;
#Override
public void run() {
while (!cancel) {
printHello();
}
}
public void cancelPrint() {
cancel = true;
}
}
I assume you want to do more than just printouts. Take a look at Swing Worker.
It allows you to pretty easily write your GUI-related code that gets executed in the AWT Event Thread and your long-executing code in other thread(s) and pass values back and forth. This will help prevent any GUI lockup issues you might experience.
I want my GUI to make some checks when a JOptionPane appears.
Because I can't find any other way, I though I can do those each time the application window loses focus(its just checking a string). For that reason I added the following code on my JFrame:
appFrame.addWindowListener(new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
#Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
});
The window closing listener works fine. Although when the JFrame isn't focused nothing happens. Shouldn't "Focus Lost" be printed each time I switch from JFrame to some other window? Also, will this method be triggered when a JOptionPane is shown?
The key to me is that you want a change in the GUI triggered by a change of a String variable. The best way I see to solve this is to make the String variable a bound property by using PropertyChangeListenerSupport. This way you can have the GUI attach a PropertyChangeListener to the class that holds the String variable and then be notified when it changes allowing you to update the GUI appropriately.
If you go this route, consider giving the observed class a SwingPropertyChangeSupport field so that the listeners will be notified on the Swing event thread and hopefully avoid any Swing concurrency issues.
Here's a brief example:
import java.awt.Dimension;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class ShowPropertyChangeSupport {
#SuppressWarnings("serial")
private static void createAndShowGui() {
final MainGUI mainGui = new MainGUI("Title");
final ObservedClass observedClass = new ObservedClass();
observedClass.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getPropertyName().equals(ObservedClass.BOUND_PROPERTY)) {
mainGui.setTitle(pcEvt.getNewValue().toString());
}
}
});
mainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainGui.pack();
mainGui.setLocationRelativeTo(null);
mainGui.setVisible(true);
int timerDelay = 6000; // every 6 seconds
new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
String result = JOptionPane.showInputDialog(mainGui,
"Please enter a String", "Set GUI title", JOptionPane.PLAIN_MESSAGE);
if (result != null) {
observedClass.setBoundProperty(result);
}
}
}){{setInitialDelay(1000);}}.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
// ** note that I don't like extending JFrame,
// but will do this for sake of example simplicity
class MainGUI extends JFrame {
public MainGUI(String title) {
super(title);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
class ObservedClass {
public static final String BOUND_PROPERTY = "bound property";
private String boundProperty = "";
private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
this);
public SwingPropertyChangeSupport getSpcSupport() {
return spcSupport;
}
public void setSpcSupport(SwingPropertyChangeSupport spcSupport) {
this.spcSupport = spcSupport;
}
public String getBoundProperty() {
return boundProperty;
}
public void setBoundProperty(String boundProperty) {
String oldValue = this.boundProperty;
String newValue = boundProperty;
this.boundProperty = newValue;
spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
spcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
spcSupport.removePropertyChangeListener(listener);
}
}
The key to all this in my mind is to use the listener so that the class with the bound property -- the String being listened to -- has no knowledge of the GUI, the listener, and the GUI, likewise has no knowledge of the class with the bound property. They are fully decoupled.
I'm not going to go into why you are doing what you are doing, but it is not working as you expect for the following reason:
WindowAdapter is a convenience class so you can create one listener and register it for multiple types of events. You have only registered it for one set of events, you need to also register it for focus events via: Window.addWindowFocusListener()
WindowAdapter adapter = new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
#Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
};
appFrame.addWindowListener(adapter);
appFrame.addWindowFocusListener(adapter);
1) JOptionPane / modal JDialog have got modality issue, but modality could be advantage if all containers have got own owner, for real workaround you need to know (I'll talking about how can I do test that)
numbers of Window[], and if isDisplayable(), then you can use follows
you can get SwingUtilities#getAccessibleIndexInXxx can returns AccessibleState
KeyboardFocusManager (very interesting methods for multi-touch) returns getXxxFocusXxx methods
Focus, FocusSubsystem is pretty asynchronous,
2) Please, with due respect, I don't know why you needed that, for why reasons I need to know about that, there is about business rules, you always need to know ...., and if is done on EDT
Focus, FocusSubsystem is pretty asynchronous,