I have a Timer that shows an animation on the screen when a game event takes place. What I want to do is have the main execution pause until this animation finishes, but everything I try seems to block the Timer from running. I've tried using Thread.sleep() and calling wait() and notify() on a lock object, but with the same result. The Timer listener's actionPerformed() is never called.
This code sets up the timer:
protected void showMovingEffect(int steps, Direction dir, AnimatedImageSet imgs) {
effectsPane.runAnimation(imgs, dir, steps);
waiting = true;
while (waiting) {
synchronized(animationWaitLock) {
try {
animationWaitLock.wait();
} catch (InterruptedException e) {}
}
}
}
And the Timer and listener inside the EffectsPane object:
private void runAnimation(AnimatedImageSet ais, Direction dir, int iters) {
System.out.println("run");
imgs = ais;
top = 0;
left = 0;
topStep = dir.getRowIncrement() * 10;
leftStep = dir.getColIncrement() * 10;
iterations = iters;
index = 0;
active = true;
timer.start();
}
public void actionPerformed(ActionEvent e) {
System.out.println(index);
top += topStep;
left += leftStep;
index++;
currentImage = imgs.getImage(index);
repaint();
if (index == iterations) {
active = false;
timer.stop();
synchronized(animationWaitLock) {
animationWaitLock.notify();
}
waiting = false;
}
}
The System.out.println call at the start of the actionPerformed() is never called, so either the wait() call is also pausing the Timer, or something is blocking it. If I comment out the sleep/wait lines in showMovingEffect(), the animation runs, but the program does not pause.
One approach would be to display a modal dialog while the animation proceeds. As discussed here, user interaction will be foreclosed, but background GUI updates in response to the javax.swing.Timer will continue. You can allow the user to dismiss the dialog at any time, as shown here, but you may want to abandon the animation at that point.
no input from the user is needed.
You can block user interaction without displaying a dialog by entering a SecondaryLoop after the animation starts.
SecondaryLoop loop = Toolkit.getDefaultToolkit()
.getSystemEventQueue().createSecondaryLoop();
timer.start();
loop.enter();
When the animation concludes, exit() the SecondaryLoop:
public void actionPerformed(ActionEvent e) {
…
if (index == iterations) {
timer.stop();
loop.exit ();
…
}
}
You can try this:
timer.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// your code
}
});
Related
atm i have a problem with my actual Android application.
For explanation:
At first i want to show a Text in a TextView Char by Char. This is my actual Code for this
tvIntro.setText("");
final Handler textHandler = new Handler();
for(int i=0; i<intro.length();i++){
final int finalCount = i;
textHandler.postDelayed(new Runnable() {
#Override
public void run() {
tvIntro.setText(tvIntro.getText() + (intro.charAt(finalCount)+""));
}
}, 150 * i);
}
After the whole text is displayed, i want to play a sound and continuously change the Color of the Screen for 5 Seconds. For this, my code is:
myBackground.setBackgroundColor(Color.RED);// set initial colour
final Thread blink = new Thread(new Runnable() {
public void run() {
while (getRunning()) {
try {
Thread.sleep(100);
if(start[0] !=1){
mp.start();
start[0] = 1;
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
updateColor(myBackground);
whichColor = !whichColor;
}
}
});
private void updateColor(final RelativeLayout myBackground) {
runOnUiThread(new Runnable() {
#Override
public void run() {
if (whichColor)
myBackground.setBackgroundColor(Color.RED);
else
myBackground.setBackgroundColor(Color.GREEN);
}
});
}
All the functions are working, but i want too finish the first handler, before the second handler is executed. Furthermore the Second handler should stop after x seconds.
I have some problems understanding how the handler and the Threads work.
Would be nice if someone of you have a solution for me.
To delay the performing of tasks until a specified thread (or threads) finishes, add this line immediately after the thread you wish to wait on:
myThread.join();
And then immediately follow it with the code you wish to run after it finishes.
For your second problem, you can set a variable to be the value (in milliseconds) for the amount of time you want to wait before ending the thread, and then decrease that value by 100 (or whatever amount you choose to tell it to sleep for) each time the code runs. Have a check for the value to be less than or equal to zero, and then if that condition returns true, end the thread with an interrupt. So basically:
long timeToRun = 5000, sleepTime = 100;
// Your code here...
Thread.sleep(sleepTime);
timeToRun -= sleepTime;
if(timeToRun <= 0) {
myThread.interrupt();
}
There are likely more graceful ways to accomplish this, but at the very least this should solve your problems.
I'm trying to make several JRadioButtons blink at the same time with this blink method:
private void blink(JRadioButton button, boolean blinking)
{
if(blinking)
{
while(true)
{
try
{
button.setSelected(true);
Thread.sleep(500);
button.setSelected(false);
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
else
{
button.setSelected(false);
}
}
I know it has something to do with threads but I'm not that familiar with them.
I'm trying to make several JRadioButtons blink at the same time with this blink method
IMHO you don't need multiple while loops.
Just add all buttons you want to blink to an ArrayList, then in the same while loop, loop over that list and blink the buttons. So, instead of
button.setSelected(true);
Thread.sleep(500);
button.setSelected(false);
Thread.sleep(500);
You can use
for(int i=0; i<yourList.size(); i++) {
yourList.get(i).setSelected(true);
}
Thread.sleep(500);
for(int i=0; i<yourList.size(); i++) {
yourList.get(i).setSelected(false);
}
Thread.sleep(500);
But this is a bad practice. Use the Timer class and schedule a thread to execute every 500ms instead:
Timer t = new Timer(500, new ActionListener() {
boolean selected = false;
#Override
public void actionPerformed(ActionEvent e) {
selected = !selected;
for(int i=0; i<yourList.size(); i++) {
yourList.get(i).setSelected(selected);
}
}
});
t.start();
You can't animate the GUI by using Thread.sleep. In fact, you must never call Thread.sleep on the Event Dispatch Thread because that very thread is in charge of repainting the GUI, which it will clearly not be able to do while sleeping.
What you must use is the Swing Timer class and schedule it to repeat at the desired interval.
I am making a media player using JMF, I want to use my own control components
Can anyone please help me in making a seek bar for media player so that it can play song according to the slider position.
Just suggest me some logic, I can figure out the coding part afterwards
if(player!=null){
long durationNanoseconds =
(player.getDuration().getNanoseconds());
durationbar.setMaximum((int) player.getDuration().getSeconds());
int duration=(int) player.getDuration().getSeconds();
int percent = durationbar.getValue();
long t = (durationNanoseconds / duration) * percent;
Time newTime = new Time(t);
player.stop();
player.setMediaTime(newTime);
player.start();
mousedrag=true;
Here is the code. Now how can I make the slider move along with the song?
Slider works when I drag/click on it, but it doesn't move with the song.
The problem with using a slider for this is that when the slider position is moved programmatically, it fires events. When an event is fired on a slider, it typically means the app. has to do something, such as move the song position. The effect is a never ending loop. There is probably a way around this by setting flags and ignoring some events, but I decided to go a different way.
Instead I used a JProgressBar to indicate the location in the track, and a MouseListener to detect when the user clicks on a separate position. Update the progress bar use a Swing Timer that checks the track location every 50-200 milliseconds. When a MouseEvent is detected, reposition the track.
The bar can be seen in the upper right of this GUI. Hovering over it will produce a tool tip showing the time in the track at that mouse position.
You could use a JSlider.
You can learn more from the Slider tutorial
You don't have to revalidate the container in order to change the slider.
Use these lines each time a new player is created:
slider.setMinimum(0);
slider.setMaximum(duration);
slider.setValue(0);
new UpdateWorker(duration).execute();
where duration is the variable holding the duration of the song in seconds.
And here is the code (used as inner class) which updates the slider:
private class UpdateWorker extends SwingWorker<Void, Integer> {
private int duration;
public UpdateWorker(int duration) {
this.duration = duration;
}
#Override
protected Void doInBackground() throws Exception {
for (int i = 1; i <= duration; i++) {
Thread.sleep(1000);
publish(i);
}
return null;
}
#Override
protected void process(List<Integer> chunks) {
slider.setValue(chunks.get(0));
}
}
Now the slider will move to the right until the end of the song.
Also note that unless you want to use a custom slider, JMF provides a simple (and working) slider via player.getVisualComponent() (see this example).
UPDATE
In order to pause/resume the worker thread (and thus the slider and the song), here is an example with a button that sets the appropriate flags.
private boolean isPaused = false;
JButton pause = new JButton("Pause");
pause.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JButton source = (JButton)e.getSource();
if (!isPaused) {
isPaused = true;
source.setText("Resume");
} else {
isPaused = false;
source.setText("Pause");
}
}
});
The method doInBackground should be changed to something like that:
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i <= duration; i++) {
if (!isPaused) {
publish(i);
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
while (isPaused) {
try {
Thread.sleep(50);
continue;
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
return null;
}
Modify it accordingly to pause/resume the song along with the slider.
You should also consider #AndrewThompson's answer.
I've got a working Java program and I would like to draw an object on the display every X seconds. What is the best way to do this? I was thinking of using a for loop and some sleep statements, but I'm curious if there is an easier or more efficient way to go about this.
Thanks.
The simplest way would be to use a javax.swing.Timer
Timer timer = new Timer(X, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
// Update the variables you need...
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
You might also like to have a read through
The Event Dispatching Thread
Concurrency in Swing
So you can understand why you should never use a while (true) { Thread.sleep(X) } call in Swing (inside the EDT)
ScheduledExecutorService might help here. The Javadoc shows example usage. Don't forget to call the shutdown method when you're finished.
Using Thread, this will draw a rectangle on the screen every XMilSeconds. This will stop after 5 runs. Edit the xMilSeconds for slower runs, and j > 4 for how many runs before stoping. It does freeze though, that I can't fix.
int i = 0;
private long xMilSeconds = 300;
private boolean paint;
public boolean running = true;
public void paint(Graphics g)
{
super.paint(g);
if(paint)
{
for(;i < i+1;)
{
g.drawRect(i+49,i+49,i+299,i+99);
g.setColor(Color.RED);
g.fillRect(i+49,i+49,i+299,i+99);
}
paint = false;
}
}
public void run()
{
while(running)
{
try
{
Thread.sleep(xSeconds);
paint = true;
repaint();
i++;
j++;
if(j > 4)
{
running = false;
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
I have one three rectangles in my canvas. I wanted to change the colours of three rectangles
in a slow manner one by one.
For example: When starting the application, user should be able to see three rectangles with the same colour (blue).
After 2 secons that rectangles colour should change to red.
Again after 2 secons the next rectangles colour should get changed.
The last one is also done the same way, that means after 2 seconds of the 2nd rectangle.
I wrote in my own way. But it is not working. All the rectanlges are changed together. I want one by one.
Could anyone give me the logic.
final Runnable timer = new Runnable() {
public void run() {
//list of rectangles size =3; each contain Rectangle.
for(int i = 0 ; i < rectangleList.size();i++){
if(rectangleListt.get(i).getBackgroundColor().equals(ColorConstants.blue)){
try {
rectangleList.get(i).setBackgroundColor(ColorConstants.yellow);
Thread.sleep(1500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//rectSubFigureList.get(i).setBorder(null);
}/*else{
rectSubFigureList.get(i).setBackgroundColor(ColorConstants.blue);
}*/
}
You're likely calling Thread.sleep inside of Swing's event thread or EDT (for event dispatch thread), and this will cause the thread itself to sleep. Since this thread is responsible for all of Swing's graphics and user interactions, this will in effect put your entire application to sleep, and is not what you want to have happen. Instead, read up on and use a Swing Timer for this.
References:
Swing Timer tutorial
Swing Event Dispatch Thread and Swingworker tutorial
To expand on Hidde's code, you could do:
// the timer:
Timer t = new Timer(2000, new ActionListener() {
private int changed = 0; // better to keep this private and in the class
#Override
public void actionPerformed(ActionEvent e) {
if (changed < rectangleList.size()) {
rectangleList.setBackgroundColor(someColor);
} else {
((Timer) e.getSource()).stop();
}
changed++;
}
});
t.start();
You can set a Timer:
// declaration:
static int changed = 0;
// the timer:
Timer t = new Timer(2000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the colour here:
if (changed == 0) {
// change the first one
} else if (changed == 1) {
// change the second one
} else if (changed == 2) {
// change the last one
} else {
((Timer) e.getSource()).stop();
}
changed ++;
}
});
t.start();