A small app containing a BufferedImage uses the Timer class for animation. Everything works, but not as expected.
Sometimes the system struggles to draw the animation, as though it is draining system resources (especially after installing the JRE), which makes the animation redraw at abnormally low speeds. Other times it works as expected.
What could cause this?
Thank you!
Switch to java.util.Timer, javax.swing.Timer seems to be better suited to actions that are called every minute or second as opposed to java.util.Timer which is far better adapted to tens of calls per second.
Sorry everyone..my bad!
The class I am using is javax.swing.Timer!
Here is part of the code (timer implementation -- not the method timer calls):
Timer t = new javax.swing.Timer(10, new ActionListener(){
public void actionPerformed(ActionEvent ev){
//This little if/step statement actually decides if
//the ellipses I have on screen will be colored all together
//or step by step
if(app.flagStep == false){
for (int i = 0; i<list.size(); i++){
drawBuffer(i);
}
}
else{
if(app.stepBut.isEnabled()){
app.stepBut.setEnabled(false);
}
drawBuffer(app.myStep);
}
}
});
Related
So, I have an object that extends JPanel and displays dots in a matrix via paintComponent. The dots of the matrix can move, disappear or multiply given certain conditions, and I want to show their evolution over time automatically like so:
for(int i = 0; i < 100; ++i){
matrix = calculateNextMatrix(); //Calculate possible movements, deaths or births of dots
myGraphic.updateMatrix(matrix); //Pass new dots to the JPanel object
myGraphic.repaint(); //Draw new dots
Thread.sleep(100); //Wait 0.1 seconds for next iteration (yes, this should be in a
//try-catch)
}
However, I only get drawn the last iteration after the loop is finished, and all the previous calls to repaint() are basically ignored. If I do the iterations only one at a time (for example, via a manual button press), I have no problem.
Is there any way to get multiple, periodic repaint calls automatically?
I had a simile problem with JComponent in my library and I found a solution with swing timer, I reported the java description of timer
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task automatically executes on the event-dispatch thread. However, you might use a general-purpose timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
You can use Swing timers in two ways:
To perform a task once, after a delay.
For example, the tool tip manager uses Swing timers to determine when to show a tool tip and when to hide it.
To perform a task repeatedly.
For example, you might perform animation or update a component that displays progress toward a goal.
I think you are in one of this cases.
Without a minimal example reproducible, I can use the my code.
You should create the Swing action listener, like this:
public class UpdateComponentListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
count += 10;
timeLabel.setText(count + "");
//The label call repaint
//in your app you should be call the repaint
//In your cases
/*
matrix = calculateNextMatrix(); //Calculate possible movements, deaths or births of dots
myGraphic.updateMatrix(matrix); //Pass new dots to the JPanel object
myGraphic.repaint();
*/
}
}
The timer constructor get in input the delay and the action listener, so you can build your timer, with this code:
Timer timer = new Timer(1000, new UpdateComponentListener());
timer.start();
You can stop, restart your timer, so you should be set how propriety the timer.
The GUI example:
I wrote the post and after I see the #camickr comment. I post the answer because my work is finished but, the comment answered your question.
I hope to have to build a food example
I have this code in java
private void buttonShowImageActionPerformed(java.awt.event.ActionEvent evt)
{
if(folderFiles != null)
{
for(int i=0; i<folderFiles.size(); i++)
{
icon = new ImageIcon(folderFiles.get(i));
labelImage.setIcon(icon);
timeDelay(2); // A method that delays 2 secs - it works
}
}
}
When I push the button, it waits some time, and from the 1st goes to the last image, and skips the images from the folder one-by-one
The delay method works (I tested it)
Thanks in advance !
Regarding:
timeDelay(2); // A method that delays 2 secs - it works
No, it doesn't work, regardless of how you tested it. Swing doesn't work that way, and you would do well to avoid making such assumptions. Use a Swing Timer.
You will ask, how do I know that it doesn't work, and I'll tell you: that code does not call a background thread, nor does it start a Swing Timer, so the only thing it can do is delay the current thread, likely somewhere with a Thread.sleep(...). If you call this and "test" this, yes, it will cause delays that will show up in your System.out.println(...) statements to the console, but it will also sleep the Swing event thread, and put your application to sleep. So you really don't want to do this.
And in fact the best test to see if this works in Swing is your current code. What happens then? You state:
When I push the button, it waits some time, and from the 1st goes to the last image, and skips the images from the folder one-by-one
So in fact you know for a fact that your delay doesn't work for Swing, as you describe the classic symptoms of code that stomps on the Swing event thread, bringing it to its knees. So again, use a Swing Timer. Here's a link to the Swing Timer Tutorial.
If this were my code, I'd not read in the images each time the button is pushed, but instead read them all at once, and one time only and put them in an ArrayList<ImageIcon> say called iconList. Assuming that you did this, then the code could look like:
private void buttonShowImageActionPerformed(java.awt.event.ActionEvent evt) {
// !! I'd use a variable or constant instead of the magic number 2000
new Timer(2000, new ActionListener() {
int count = 0;
actionPerformed(ActionEvent e) {
if (count != iconList.size()) {
labelImage.setIcon(iconList.get(count));
count++;
} else {
// stop the Timer
((Timer) e.getSource()).stop();
}
}
}).start();
}
Edit
You ask:
My friend one last question ..... I put the "int count = 0;" inside the actionPerformed and nothing happend ... I cannot understand why it works only if it is outside the method .... ?
Please understand that the way a Swing Timer works is that the actionPerformed method is called repeatedly, here every 2000 seconds. What my code does is that when the timer is started, the count is set to 0. Each time the actionPerformed method is called, count increments by 1, and the next ImageIcon is displayed.
With your code, when the actionPerformed method is called, the count is re-set to 0, the first image is displayed, and the count is then incremented. But each time the actionPerformed method is called, your code resets the count back to 0, so the incrementation has no effect.
I have a Board that contains tiles and the Tiles contain Obstacles. An obstacle can be one of the several types (wall, laser, ...).
Every round of the game I execute the board, which exectutes all the Obstacles. Once an obstacle has been executed and something has changed the GUI is notified and the affected tiles are refreshed.
In the GUI a tile is an OverLayLayout that contains overlapping images (JLabels), so all the overlapping images are visible.
Now the problem is when I execute them, it goes to fast, you can't anything happen. E.g. you don't see the laser beam appear, the player will be damaged but you didn't see it happen...
So I tried to let the game wait for e.g. 500ms so the user can see the GUI update. I've tried (but might have done it wrong): timers, swingtimers, countdownlatch, ... but I can't seem to get it to work.
Any ideas on how to fix this? Or maybe an alternative for the waiting?
Thanks in advance!
Update:
The execute in board looks like this:
Iterator<Obstacle> it = obstacles.iterator();
while (it.hasNext())
it.next().execute(this);
CountDownLatch countDown = new CountDownLatch(1);
setChanged();
notifyObservers(countDown);
countDown.await();
The update function of the GUI board (which is notified) looks like this:
final CountDownLatch countDown = (CountDownLatch) arg;
SwingUtilities.invokeLater(new Runnable(){
public void run() {
try {
updateUI();
wait(500);
} catch (InterruptedException ex) { }
countDown.countDown();
}
});
Without seeing your code, it's hard to identify the problem; I suspect you're blocking the event dispatch thread.
This tile-based game uses an instance of javax.swing.Timer to animate play. Press A to enable animation, and click on a free square to see the effect. You may be able adapt the approach to your game.
I'm trying to code a simple game in Java. The basic structure is a single JFrame with different JPanels that I add/remove at different times. At startup, there is a JPanel that's a basic menu (start game, high scores, etc). Once the "Start" button is pressed it switches to a level selector panel with three buttons to select the difficult level of the game. Once any of the three buttons is pressed, it switches to another panel that will displays a three second countdown, then the actual game. All three buttons call the same method, just with a different difficulty value passed in.
I have all the separate pieces working fine, but I'm having troubles with the transition from the level selection panel to the countdown. If I don't use threads the screen freezes on button press and does not switch to the new panel. I've tried messing around with threads, but I don't know that much about them and have only had limited success (I've got it so it will successfully switch some of the time, but not consistently).
In terms of code, in the level selection panel I have something like this listening for button clicks:
private class ButtonClickedListener implements ActionListener {
public void actionPerformed(ActionEvent evt) {
gui.newLevel(1);
}
}
where in place of just gui.newLevel(1) I've messed around with starting new threads and calling the method from them.
The newLevel() method look like:
getContentPane().removeAll();
levelPanel = new LevelPanel(levelNum, this);
add(levelPanel);
validate();
levelPanel.start();
I use very similar code when switching from the start menu JPanel to the level selector panel (again, with an ActionListener on the buttons), which works just fine.
LevelPanel's start() method initializes values for the new JPanel and displays the countdown on screen (currently with the following code, although I messed with putting something like this in the newLevel() method instead) before displaying the actual game:
try {
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
Thread.sleep(1000);
//update countdown number
validate();
repaint();
} catch (Exception e) {
System.out.println(e);
}
//start game
I would really appreciate any help getting this code to work, and I'm pretty sure some sort of threading is the way to go but I'm not quite sure where/how. Any suggestions and/or code samples would be great!
Thanks in advance!
EDIT: I ended up rewriting the countdown using a timer instead of Thread.sleep(), which fixed part of the problem and the rest of it I eventually figured out and was entirely unrelated to GUI stuff, which is why I didn't think to check it in the first place.
never really never use Thread.sleep(1000); during EDT, this code caused freeze on GUI is un_resposible, untill a new event invoke EDT or mouse hover over can alive this container too
1) there are two ways how to dealy any event(s) in the Swing GUI, by implements
Swing Timer
delaying by using Thread.sleep(1000); in the SwingWorker
The layout and painting must be done in EDT. Use SwingUtilities.invokeAndWait to call the validate() and repaint()
You can start some code with a time delay using TimerTask:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
invokeLater(); // This starts after [delay] ms
// and - if given - will run every [period] ms.
}
}, delay, period);
You could solve your problem with this, though it won't be a pretty solution.
// edit: (see comments) you should synchronize accesses to the gui properly, else it will give you errors.
Hi im trying to setText to JTextArea in loop but I want to do it, thtat in each loop every line will be seen in frame.
I have tryied with Thread.sleep(500), becouse I thought loop is too fast to set each line, but its didnt help.
Is it possible ?? to do it ? I want to do it to show to user progress with downloading files from database.
the nature of event-based singlethreaded guis makes it so that the changes are only visible once the event is fully handled (returned from the event handler)
blocking the event dispatch thread won't help (and even makes the entire app unresponsive)
you should use a timer to simulate the adding one at the time with a delay in between
final String[] lines;
for(int i=0; i<10; i++){
Timer t = new Timer(500*i,new ActionListener(){
int ind=i;
void actionPerformed(ActionEvent e){
area.setText(area.getText() + "\n ...");
}
});
t.start();
}
this creates 10 timers each adding a line after some time (increments of 500)
there's a better way to do this that reuses the timer and stops it after everything is done but it's a bit more verbose
I think JTextArea.append(text) might be more useful.