I want my program to start off using a countdown timer that will be displayed to the user. The only way I can think to do that is to label.setText() and Thread.sleep(). I've tried for about 20min trying to get it to work, but can't seem to crack it. Help?
Use a Swing timer. Basically you create a class that implements ActionListener, create a javax.swing.Timer, and the actionPerformed method of the ActionListener gets called at an interval you specify. In your actionPerformed method, check the time, and update the value to be displayed when appropropriate, and call repaint(). Use an interval of 0.1 second, and your countdown will be smooth and accurate enough.
I wouldn't count on the accuracy of Thread.sleep, but:
for(int i = 20; i >= 0; i--) {
label.setText(i + " seconds remaining");
Thread.sleep(1000);
}
This will, of course, block the UI thread while sleeping, so you probably need to run it on a separate thread. This means you will need something like SwingUtilities.InvokeLater to update the UI, since you are using a thread different than the UI one:
for(int i = 20; i >= 0; i--) {
SwingUtilities.InvokeLater(new Runnable() {
public void run() {
label.setText(i + " seconds remaining");
}
});
Thread.sleep(1000);
}
Related
I want to make a ProgressBar move gradually using a Jbutton. To achieve this I am using a for loop and the method Thread.sleep. The problem is that insted of moving a tiny bit every second (the progress bar) after pressing the button, the program waits until the loop finishes and then does instantly move the progress up. When I take the loop outside of the button listener it works as I want but I really need it to work when pressing the button. Here is the code:
progressBar.setOrientation(SwingConstants.VERTICAL);
progressBar.setMinimum(0);
progressBar.setMaximum(100);
progressBar.setValue(50);
panel1.setLayout(null);
panel1.add(progressBar);
progressBar.setBounds(40,6,100,100);
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int counter = 5;
for (int i = 0; i < 5; i++) {
progressBar.setValue(progressBar.getValue() + counter);
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}
}
});
If anyone can help me I will be very grateful!
Your code runs on the Event Dispatcher Thread (EDT). That thread is responsible for handling events, but also for repainting. Because it's just one thread, the repainting only occurs after your method ends.
There are some ways to solve this. In your case, a javax.swing.Timer would probably be easiest. Instead of running the loop in a single method, the button click starts a timer that runs every second. When it's done the timer can cancel itself. A slightly more difficult alternative is to use a SwingWorker. You could publish data in the doInBackGround method, and override process to perform the updates to the progress bar.
For more information, please read Concurrency in Swing.
I'm attempting to get an animation working in a game I'm developing. The animation works by setting a button size to very small, then gradually growing it to its normal size again. I have it working, except I'm having timing issues.
Sometimes the button will grow almost instantly, sometimes it goes VERY slow. I'm looking for something inbetween, and I need it to ALWAYS grow at that size, not some times fast sometimes slow.
I've looked into it and I found this pseudocode:
distance_for_dt = speed * delta_time
new_position = old_position + distance_for_dt
Unfortunately I don't understand what's being said, and I don't know how to apply this to my code. Can anyone help with that or explain what's being said in the above pseudocode?
Here's my timer code, timer is already defined above as a Timer, and z[] is just a pair of coordinates:
timer = new Timer(18, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Dimension dim = button[z[0]][z[1]].getSize();
if (dim.getHeight() < 79.9) {
button[z[0]][z[1]].setSize((int) (dim.getWidth() + 6), (int) (dim.getHeight() + 6));
} else {
button[z[0]][z[1]].setSize(80, 80);
timer.stop();
}
}
});
timer.start();
Depending on how many updates you're calling on your Swing application, it may be getting "backed up" and slowing down. For instance, if you wanted to accomplish the animation without a Timer, you could just do something like this:
// example method to do animation
public void animateButton(final int wait){
Thread thread = new Thread(){
public void run(){
// some loop structure to define how long to run animation
Dimension dim = button[z[0]][z[1]].getSize();
while (dim.getHeight() < 79.9){
SwingUtilities.invokeLater(new Runnable(){
//update Swing components here
});
try{ Thread.Sleep(wait); }
catch(Exception e){}
}
}
}
}
thread.start();
}
I think this may be similar to how a Timer updates the GUI, as Timers run on a separate thread. I would look into whether or not you need to use invokeLater(new Runnable) inside a timer to properly schedule the task. I had to do this to allow a project I was working on to keep responsive during long tasks. If you really needed to ensure the speed and maybe DROP updates to adjust for system lag, then you'll need to be calculating how complete the animation is vs how much time has passed, using a method call such as System.currentTimeMillis() or System.nanoTime(). Then, adjust accordingly for each step of the animation.
i have here a strange behaviour of my graphical user interface.
At first here a piece of code:
/**
*
*/
#Override
protected Void doInBackground() throws Exception {
final ModelGameState actualGameState = controller.getActualGameState();
final ModelCoinState actualCoinState = (actualGameState.getPlayersTurn() == ModelConstants.PLAYER_ONE_ID? actualGameState.getCoinsPlayerOne() : actualGameState.getCoinsPlayerTwo());
final List<ModelCoinState> temp = MoveCalculator.getMoves(actualCoinState, this.cellID);
final CountDownLatch lock = new CountDownLatch(temp.size());
int time = 500;
for(int i = 0; i < temp.size(); i++) {
final int index = i;
Timer timer = new Timer(time, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(actualGameState.getPlayersTurn() == ModelConstants.PLAYER_ONE_ID) {
actualGameState.setCoinsPlayerOne(temp.get(index));
} else {
actualGameState.setCoinsPlayerTwo(temp.get(index));
}
controller.setActualGameState(new ModelGameState(actualGameState));
lock.countDown();
}
});
timer.setRepeats(false);
timer.start();
time += 500;
}
lock.await();
return null;
}
at second here my gui:
and here my problem: everytime lock.await is called my screen
looks like that:
As you can see, behind each of my circles the top left corner
of my gui is shown everytime lock.await() is called (At least i
think it is when lock.await()is called because when i delete lock.await()
i cant see the whole animation of my gui but i also cant
see this strange behaviour and that behaviour appears always
when the program is through all code of doInBackground().
What causes this strange behaviour?
not an answer only disagree with, my comments against, no reviews, not tried your code, apologize me that there are any reason, maybe my bad
doInBackground() is bridge between AWT/Swing EDT and Workers Thread(s), by default never notified EDT
process, publish, setProgress and done() notify EDT
then Swing Timer inside doInBackground() is against all intentions, why was SwingWorker implemented in official APIs, there is place to execute long running, hard or non_safe code
again SwingWorker is designated as bridge between AWT/Swing and Workers Thread(s)
_____________________________
there are two ways
use CountDownLatch with invokeLater() or Swing Timer. don't mix that together nor from SwingWorker
use CountDownLatch, util.Timer, SheduledExecutor with notify EDT by wrap (only relevant, only output, methods will be repainted on the screen) Swing methods to the invokeLater()
use only Swing Timer (non_accurate on hour period)
I made CountDown.java file and try to add in my Word-trouble.java file (which is main applet) as CountDown ct = new CountDown();
but it is not showing timer in main applet.
Here is coding:
package pack.urdu;
import java.awt.*; //windows toolkit
import java.applet.*; //applet support
public class CountDown extends Applet implements Runnable{
int counter; Thread cd;
public void start() { // create thread
counter = 60; cd = new Thread(this); cd.start();
}
public void stop() { cd = null;}
public void run() { // executed by Thread
while (counter>0 && cd!=null) {
try{Thread.sleep(1000);} catch (InterruptedException e){}
--counter; repaint(); //update screen
}
}
public void paint(Graphics g) {
g.drawString(String.valueOf(counter),25,75);
}
}
You are making a mistake that I see a lot of programmers make: you are mixing up the calculation of elapsed time, with the calculation of the refresh time. If the duration of sleep takes long than a second because of thread contention, your timer will drift.
Instead of tracking a counter that increments every second, just record the start time:
long startTime = System.currentTimeMillis();
Then later, your paint method becomes:
public void paint(Graphics g) {
int elapsedSeconds = (int)(System.currentTimeMillis()-startTime)/1000
g.drawString(String.valueOf(elapsedSeconds),25,75);
}
This method can be called as often, and as many times as you like, and it will always display the correct elapsed seconds. There is no need to increment anything at any specified time.
The only other thing you have to do is to arrange that the screen gets refreshed. (I like to say that you only have to refresh the screen when the user looks at it :-) but since we don't know that we need to refresh more often). The mechanism for this may depend upon the graphic library. One lazy idea is to refresh ten times a second and the screen will be right most of the time.
If you do want to have a thread that sends repaint events, you should have those events sent just at the time that timer clicks over to a new value, and thereby send only one per second. This is done with:
while (stillRunning) {
long elapsedTime = System.currentTimeMillis() - startTime;
long timeTillNextDisplayChange = 1000 - (elapsedTime % 1000);
Thread.sleep(timeTillNextDisplayChange);
repaint();
}
Note that you do not sleep 1000ms! If your system is performing well, this will be very close to 1000ms, but slightly less than that to account for (1) the thread startup delay, possibly caused by thread contention, and (2) the processing time for this loop (which is quite small). In any case, calculating the sleep in this way will prevent timer drift, and assure that your display updates just as the seconds value changes.
See an extended discussion of Common Misunderstandings of Timers on my website.
I am using java.
I have a click event that adds "squares" to a container, in a loop. I want each square to show up RIGHT when it is added. I tried running the 'adding of the squares' in a separate thread, but it is not working.
Here is some code I use for 'public class GuiController implements ActionListener, MouseListener':
#Override
public void mouseClicked(MouseEvent e)
{
//createBoardPane();
new Thread
(
new Runnable()
{
public void run()
{
showAnimation();
}
}
).start();
}
public void showAnimation()
{
for(int i = 0; i < model.getAnimationList().size(); i++)
{
String coord = model.getAnimationList().get(i);
int x = Integer.parseInt(coord.substring(0, coord.indexOf(',')));
int y = Integer.parseInt(coord.substring(coord.indexOf(',') + 1, coord.length() - 2));
boolean shouldPlacePiece = (coord.charAt(coord.length() - 1) == 'p');
if(shouldPlacePiece)
{
model.getView().getBoardPane().getComponent(x + (y * model.getBoardSize())).setBackground(Color.BLACK);
}
else
{
model.getView().getBoardPane().getComponent(x + (y * model.getBoardSize())).setBackground(Color.WHITE);
}
model.getView().getBoardPane().repaint();
long time = System.currentTimeMillis();
while((System.currentTimeMillis() - time) < 250)
{
// wait loop
}
}
}
any help is appreciated!
Creating a separate Thread to run for this longish-running task was an excellent idea - unless you want to lock-up interactions with your GUI while doing your animation.
Now, Swing GUI objects are not Thread safe (with few exceptions), so you cannot work with them from a thread other than Swing's Event Dispatch Loop's thread. So take all the GUI update code in your for-loop, and wrap it with a new Runnable (yes, another one).
Then call SwingUtilities.invokeLater(Runnable doRun) with that Runnable on each iteration of the loop.
Your GUI update code will then be scheduled to run ASAP on the Event Dispatch Loop, which will occur while your worker thread goes to sleep (do you have anything against Thread.sleep?).
Alternative: Use SwingWorker instead of Thread
SwingWorker will create and manage a new the Thread for you, and publish data that it (SwingWorker) will cause to be run on the Event Dispatch Loop's thread. You'll override doInBackground with your code. Call publish with parameters to push across into the Event Dispatch Thread. Override process with code to process those parameters and update your GUI.
On gotcha with SwingWorker is that it accumulates published events over a period of about 33 milliseconds. If you're publishing more frequent than that you may get all your events bunched together every 33 milliseconds or so. In you case, 250 milliseconds between updates shouldn't be a problem.