I keep getting a expected error at the displayTimer.start(); line... what's the reason for this? I am just trying to understand how to use the swing Timer with two inputs into the constructor, and nothing else fancier. I got this code from: http://albertattard.blogspot.com/2008/09/practical-example-of-swing-timer.html
import java.awt.event.*;
import javax.swing.Timer;
public class Five {
public static void main(String[] args){
ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
System.out.println("hello");
}
};
Timer displayTimer = new Timer(1000, listener);
displayTimer.start();
}
}
adding the main method that fixed that error, but now it doesn't seem to be constantly running... it never prints hello.
Probably because the JVM exists before the Timer has a chance to fire. The point of a Timer is to use it with a GUI.
So create a more practical example. First create a JFrame and make the frame visible. Then the JVM will not exit while the frame is visible. Then you can start the Timer.
Read the section from the Swing tutorial on Concurrency for more information on the different Threads used in Swing.
Related
For example, I want a JTextfield to display different random numbers continuously with start, stop and resume buttons. What is the possible solution to automatically update the JTextField continuously when the start button is pressed?
I tried using while loop inside the start button's action listener but it just makes the button stuck in the while loop.
This is the part of the code that I tried.
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
while(true){
textField.setText(String.valueOf(random.nextInt()));
}
}
});
Read Concurrency in Swing.
You can use a javax.swing.Timer to change the text of the JTextField.
A tiny example:
public class TimerExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(()->{
JTextField field = new JTextField(10);
Timer timer = new Timer(100, e->{
field.setText(String.valueOf(Math.random()));
});
timer.start();
JOptionPane.showMessageDialog(null,field);
});
}
}
If you use while(true) in the Thread that runs the UI (this thread is called EDT - event dispatch thread), the thread won't be able to handle events since it is stucked inside the while loop.
So I am new in swing and working on the hangman game. So the way it is supposed to work is that the user is prompt to welcome message which will last a 3seconds, disappears, and then sends the user to the next frame. Everything is working perfectly except that when I run it the first frame is still visible and running in the shadow even though it goes to the next one. I have tried to use the dispose method but it's just closing the frame without going to the next one.
Here is my what I have done so far
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.Timer;
public class PA1test extends JFrame{
public static void main(String[] args) {
// opens the first page
JFrame gui = new JFrame("Hangman");
gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gui.add(new First_PageImage());
gui.pack();
gui.setVisible(true);
// action to open the second page
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//...Perform a task...
JFrame secpage = new JFrame("Hangman");
secpage.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
secpage.add(new SecondPage());
secpage.pack();
secpage.setVisible(true);
}
};
// set timer to perform action after 3 seconds
Timer timer = new Timer(3000 ,taskPerformer);
timer.setRepeats(false);
timer.start();
}
}
A few points:
Setting the dispose operation to JFrame.EXIT_ON_CLOSE will exit your entire application. Consider using JFrame.DISPOSE_ON_CLOSE
Sounds like you wish to create a splash screen - consider reading Oracle's tutorial on using the SplashScreen class
If your intent is a splash screen and you wish to use a JFrame or JDialog rather than java's build in SplashScreen from (2), consider designing it to look like a splash screen by removing the decorations (setUndecorated(true)) and centering (setLocationRelativeTo(null);)
In order to dispose/hide the initial JFrame/splash, you need to do so after the Timer has fired, which can be done from within the Timer's ActionListener implementation - in order to access the splash screen instance from within the anonymous class, you must mark it as final.
Pseudo-code:
final JFrame splashScreen = new JFrame("Hangman");//mark as final for visibility's sake
splashScreen.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
ActionListener taskPerformer = new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
//logic to create the main UI frame here
splashScreen.dispose();
}
};
// set timer to perform action after 3 seconds
Timer timer = new Timer(3000 ,taskPerformer);
timer.setRepeats(false);
timer.start();
Try .setVisible(false) on the first frame.
I'm using swing timer for my animation in japplet.
I'm having an issue on understanding how timer really works.
I came up with this code(this is a small part of it):
Action actionListener2 = new AbstractAction() {
public void actionPerformed(ActionEvent actionEvent) {
timer = new Timer(500,this);
timer.start();
System.out.println("S");
p.moveSquare(p.SnakeHeadX,p.SnakeHeadY+p.SnakeHeadH);
p.eatBlueSquare(p.SnakeHeadX,p.SnakeHeadY);
}
};
No, don't create a new Timer inside of the ActionListener as that will repeatedly create many many timers. Instead simply move your square inside of the Timer's ActionListener and call repaint(). Have you read the Swing Timer tutorial? If not, I urge you to Google it and give it a look.
A Swing timer (an instance of javax.swing.Timer (in the API reference documentation)) fires one or more action events after a specified delay. Don't confuse Swing timers with the general-purpose timer facility that was added to the java.util package in release 1.3
Timer basically a java thread which started after a delay or instantly and perform assigned task.
you should move your Timer creation from actionperformed function as mentioned above.
Timer timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e)
{
System.out.println("S");
p.moveSquare(p.SnakeHeadX,p.SnakeHeadY+p.SnakeHeadH);
p.eatBlueSquare(p.SnakeHeadX,p.SnakeHeadY);
}
});
timer.start();
for more details go through
http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html
should a game loop in a java program always run in the event dispatch thread?
Because my keylisteners are running in the AWT event thread and I want the key events to be processed in the same thread as the rest of the game to avoid thread based bugs.
If I don't run the game loop in the event thread can I use an ArrayBlockingQueue to send events from the event handler to the game loop? Is there a better solution?
thanks
Maybe something like this?
ConcurrentLinkedQueue<AWTEvent> eventQueue = new ConcurrentLinkedQueue<AWTEvent>();
JFrame frame = new JFrame() {
#override
protected void processEvent(AWTEvent e) {
if(e instanceof InputEvent) eventQueue.add(e);
else super.processEvent(e);
}
}
//game loop
while(true) {
while(!eventQueue.isEmpty()) frame.super.processEvent(eventQueue.poll());
...
}
edit: Thank you, I think I now know how to do this. I'm gonna do it like this
import java.util.concurrent.ConcurrentLinkedQueue;
import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.event.*;
import javax.swing.*;
public class QueueTest {
static ConcurrentLinkedQueue<AWTEvent> eventQueue = new ConcurrentLinkedQueue<AWTEvent>();
public static void main(String[] args) {
final JPanel panel = new JPanel() {
#Override
protected void processEvent(AWTEvent e) {
eventQueue.add(e);
}
{
enableEvents( AWTEvent.MOUSE_EVENT_MASK
| AWTEvent.MOUSE_MOTION_EVENT_MASK
| AWTEvent.KEY_EVENT_MASK);
}
};
final JFrame frame = new JFrame();
frame.addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent e) {
panel.requestFocusInWindow();
}
});
frame.add(panel);
panel.setPreferredSize(new Dimension(800,600));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
while(true) {
while(!eventQueue.isEmpty()) System.out.println(eventQueue.poll()+"\n");
}
}
}
I'm not a game programmer so your question prompted some research :) You cannot run the game loop in the event dispatch thread because it is in a loop of its own servicing user input. You're application would essentially freeze up and not respond to any further input events. Your game loop should be a separate thread with a thread-safe queue for game events - the queue should be non-blocking because you want the game loop to continue even if there has been no user input. Any rendering you need to do as a result of input will probably have to be put back on the event queue - swing provides SwingUtilities for this although not sure which environment you are in.
I'll probably get slammed by experienced games developers now.
I had a similar problem in the game engine I wrote.
In the end I had to have a separate thread for my game engine loop. When AWT events were fired I had to store them, and fire them during the next logical update within the game loop.
This was my solution however, I believe that the 'easiest' thing for events is to actually poll them! :)
I wonder what is the best approach to make a JOptionPane style plain message box disappear after being displayed for a set amount of seconds.
I am thinking to fire up a separate thread (which uses a timer) from the main GUI thread to do this, so that the main GUI can carry on processing other events etc. But how do I actually make the message box in this separate thread disappear and terminate the thread properly. Thanks.
Edit: so this is what I come up with by following the solutions posted below
package util;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
public class DisappearingMessage implements ActionListener
{
private final int ONE_SECOND = 1000;
private Timer timer;
private JFrame frame;
private JLabel msgLabel;
public DisappearingMessage (String str, int seconds)
{
frame = new JFrame ("Test Message");
msgLabel = new JLabel (str, SwingConstants.CENTER);
msgLabel.setPreferredSize(new Dimension(600, 400));
timer = new Timer (this.ONE_SECOND * seconds, this);
// only need to fire up once to make the message box disappear
timer.setRepeats(false);
}
/**
* Start the timer
*/
public void start ()
{
// make the message box appear and start the timer
frame.getContentPane().add(msgLabel, BorderLayout.CENTER);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
timer.start();
}
/**
* Handling the event fired by the timer
*/
public void actionPerformed (ActionEvent event)
{
// stop the timer and kill the message box
timer.stop();
frame.dispose();
}
public static void main (String[] args)
{
DisappearingMessage dm = new DisappearingMessage("Test", 5);
dm.start();
}
}
Now the question is that, as i cam going to create multiple instances of this class throughout the course of the interaction between the user and the main GUI, I wonder whether the dispose() method cleans up everything properly every time. Otherwise, I may end up with accumulating lots of redundant objects in memory. thanks.
I think in your situation, you can't use any of JOptionPane static methods (showX...). You have to create a JOptionPane instance instead, then create a JDialog from it and show that JDialog yourself. Once you have JDialog, you can force its visibility.
// Replace JOptionPane.showXxxx(args) with new JOptionPane(args)
JOptionPane pane = new JOptionPane(...);
final JDialog dialog = pane.createDialog("title");
Timer timer = new Timer(DELAY, new ActionListener() {
public void actionPerformed(ActionEvent e) {
dialog.setVisible(false);
// or maybe you'll need dialog.dispose() instead?
}
});
timer.setRepeats(false);
timer.start();
dialog.setVisible(true);
I haven't tried it so I can't guarantee that it works but I think it should ;-)
Of course, here Timer is javax.swing.Timer, as someone else already mentioned, thus you're sure the action will run in the EDT and you won't have any problem with creating or terminating your own Thread.
Timers have their own threads. I think what you probably should do is create a new Timer (or, preferably, make one that you reuse till you don't need it any more), schedule a task that will ask for the message box to disappear and then have that task add another task to the event queue, which will remove the message box.
There might be a better way though.
In addition:
Yes, using javax.swing.timer would probably be better. The reason I talk about using two tasks in the above is that I assume you will have to execute your hiding method inside of the AWT thread to avoid certain subtle race issues that might arise. If you use javax.swing.Timer you're already executing in the AWT thread, so that point becomes moot.