Java-Swing : The ImageIcon cannot change in runtime? - java

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.

Related

Why does my timer event start triggering faster and faster every time I start it again?

I need a timer for a game I am building and what it basically does is fire up an event that moves and object a square every second when I hit play. Now either I let the game take its course(the game finishes or the object moves out of bounds) or press play once again, the timer seems to be triggering the object to move faster than previously and so on, going faster and faster every time I restart the time.
private void playButtonMouseClicked(java.awt.event.MouseEvent evt) {
/*code*/
timer = new Timer(timerSpeed, new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
/*code that ends with something that calls timer.stop()*/
}
}
});
if(timer.isRunning()) //in case I don't let the game stop the timer
timer.stop();
timer.start();
}
I checked with timer.getDelay() and the delay doesn't change, it stays the same, but I can see the arrow moving faster and faster every time. I'm using jPanels and a label with and icon to display the grid and the moving object.
Any ideas?
but I can see the arrow moving faster and faster every time.
This tells me you have multiple Timers executing.
Your current code creates a new Timer every time the button is clicked.
The timer.isRunning() check will never be true because you just created a new Timer which will not be running since you haven't started it yet.
So your old Timer may still be running but because you no longer have a reference to it, you can't stop it.
The solution:
Don't create a new Timer object every time you click the button. The Timer object should be created in the constructor of your class. Then you just start/stop the Timer as required

Java JFrame: Update GUI during an event (not within a timer)

Been trying at this a little while, going no where fast. I have elements in a JFrame that I want to update/toggle viability for when I click my play button (which triggers the ActionEvent). However, these only seem to be updating after the actionPerformed method is finished. This is what I think is happening because the constructor for my SoundPlayer object triggers (Thread.sleep)s, causing a noticeable delay. I did read here that using Thread.sleep() locks up a GUI, but I make my changes before calling SoundPlayer, so I don't think that would be the issue.
As seen below, I've tried repainting the JFrame in general, as well as the individual element. These do not update until after the SoundPlayer's delay is finished, though my println statement prints a the correct time.
MultiThreading has crossed my mind as a solution, though I don't see why I would need to do so. Any help on this matter is appreciated!
public void actionPerformed(ActionEvent e){
int channel = 0, volume = 0; //Assigned for safety.
String musicNotes = ""; //Will be filled with the under-input.
boolean willPlay = true; //Assumes will be played.
/*Stuff that makes 'willPlay' either true of false*/
//If nothing is wrong, plays the String!
if (willPlay) { //If above parameters are good...
badNums.setVisible(false);
prog.setVisible(true);
if (vis.isSelected())
prog.setText("Estimated duration: " + estiDuration(musicNotes)*(0.4) + "seconds");
else
prog.setText("Duration: " + estiDuration(musicNotes)*(0.3) + "seconds");
System.out.println("test");
repaint();
prog.repaint();
new SoundPlayer(channel, volume, musicNotes); //Plays the music!
} else {
vis.setVisible(false);
badNums.setVisible(true);
}
}
Swing is single threaded - all code for painting, events, etc... runs on this Thread (called the EDT). If you have a long running task and place it on the EDT, it can do nothing else until it is complete (note that calling repaint doesn't directly repaint the Component, so running something lengthy after this call does not mean the Component will actually paint itself before). If you need to perform lengthy operations, do so in a different Thread - this can be accomplished either directly using a Thread, or using a SwingWorker. If the former, be sure any calls to Swing get dispatched to the EDT using SwingUtilities.invokeXXX

Timer Schedule - Java

new java.util.Timer().scheduleAtFixedRate(timeleft(), 0, 1000);
This is my code for a method timeleft() that i want to happen every second.
My class is called Timer.
Not sure why I'm getting an error.
It says "void type not allowed here" when I hover over the line.
I am using a JFrame for a GUI and put this inside an event when I press a button along with a timer that counts down from whatever number you enter in a text field.
You state:
I am using a JFrame for a GUI and put this inside an event when I press a button along with a timer that counts down from whatever number you enter in a text field.
I'm going to recommend that you consider throwing out the code that you're asking your question on, to completely re-think your approach.
If you want to create and run a repeated event in a Swing GUI, don't use a java.util.Timer as you're doing, since you can easily run into serious Swing threading issues. Instead use a Swing Timer, a Timer that works well with Swing, since all code in the Timer's ActionListener is guaranteed to run on the Swing event thread.
To start you out, your code could look something like:
int timerDelay = 1000; // 1000 msecs or 1 second
Timer timer = new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// code that needs to be repeated goes here
}
});
timer.setInitialDelay(0);
timer.start();
Although for a count-down timer, I'm not sure that I'd set the initial delay to 0. Instead, I'd let it wait the period.
As you can see from the javadoc, the scheduleAtFixedRate method accepts a TimerTask as a first argument. You seem to be trying to pass the result of a method, named timeleft(), with a void return type.
If you need to execute some code at a fixed rate, then you need to create a TimerTask object with an appropriate run implementation and pass that to scheduleAtFixedRate.

GUI threading in java

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.

java setText in loop

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.

Categories