Thread.sleep() delaying entire program instead of only what's after it - java

Pretty much title. The code is supposed to draw one box, wait 1 second, then draw a new one at a different location and repaint. Instead, it will wait for 1 second then paint both boxes. Thanks for the help and sorry if I messed up on formatting.
import javax.swing.*;
import java.awt.*;
public class GameRunner extends JPanel{
#Override
public void paintComponent (Graphics g){
int x = 0;
boolean directionRight = true;
g.setColor(Color.blue);
g.fillRect(300,400,100,100);
repaint();
try{
Thread.sleep(1000);
}
catch (Exception ex){}
g.fillRect(600,400,100,100);
repaint();
}
public static void main (String[] args){
JFrame frame = new JFrame("Submarine");
GameRunner gameRunner = new GameRunner();
frame.add(gameRunner);
frame.setSize(1200,700);
frame.setVisible(true);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}

Thread.sleep(1000); will block the current running thread
paintComponent is called from within the context of the Event Dispatching Thread.
Swing won't update the state of the UI until it's finished processing the current (in this case "paint") event, meaning that while it's blocked at Thread.sleep, nothing will be updated on the UI and no new events will be processed.
Swing is a single threaded framework. You should never perform any blocking or long running operations from within the context of the Event Dispatching Thread.
Have a look at Concurrency in Swing for more details and How to use Swing Timers for a possible solution.
As a side note, you should NEVER modify the state if the UI or any variable the UI relies on from within any paint method. Painting should only paint the current state of the component, never modify it, this includes calling repaint directly or indirectly
For example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GameRunner extends JPanel {
private int xPos = 300;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
g.fillRect(xPos, 400, 100, 100);
repaint();
}
public GameRunner() {
Timer timer = new Timer(1000, new ActionListener() {
private boolean state = false;
#Override
public void actionPerformed(ActionEvent e) {
if (state) {
xPos = 300;
} else {
xPos = 600;
}
state = !state;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(700, 500);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GameRunner());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

Related

Move a rectangle down using Graphics

I'm newbie in Java and I want to move a rectangle down in a JFrame. I want to see this movement.
Why can't I see the rectangle moving down? Do I have to use other library or what?
import java.awt.Color;
import java.awt.Graphics;
import java.lang.Thread;
import javax.swing.JComponent;
public class Draw extends JComponent {
public void paintComponent(Graphics g) {
g.setColor(new Color(0, 128, 128, 128));
try {
for(int i = 70; i < 100; i++) {
g.fillRect(40, i, 100, 70);
Thread.sleep(10); // To see the moviment.
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
import javax.swing.JFrame;
public class Main extends JFrame {
public Main() {
setSize(300, 200);
Draw draw = new Draw();
add(draw);
}
public static void main(String[] args) {
Main m = new Main();
m.setVisible(true);
}
}
Swing is single threaded
This means that if you perform any long running or blocking operations within the context of the Event Dispatching Thread, you will prevent Swing from performing any painting operations or processing using input
Swing is not thread safe
This means that you should never try and update the UI, or any state the UI relies on, from outside the context of the Event Dispatching Thread, this can run you into all kinds of weirdness and unpredictable states
See Concurrency in Swing for more details
A simple solution...
Possibly the simplest solution is to use a Swing Timer, for example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new Draw());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Draw extends JPanel {
private int yPos = 0;
public Draw() {
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
yPos += 1;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(0, 128, 128, 128));
g2d.fillRect(40, yPos, 100, 70);
g2d.dispose();
}
}
}
See How to Use Swing Timers for more details
Why use a JPanel over a JComponent?
For simplicity. A JPanel will paint it's background (using the background property) automatically (if you call super.paintComponent) saving your the hassle
Your "time" consideration is not following Swing UI threading concept.
Think of it this way: Swing has a Frame, you paint the content, then wait for something to change, and paint again.
A suggestion could be a Thread, that every x milliseconds, requests the JFrame to update its content and then by using repaint() method.
An example:
Draw() {
new Timer().schedule(new TimerTask() {
#Override
public void run() {
// Move x and y of the desired rectangle and request repaint
x+=10;
repaint();
}}, 50);
}

Implementing a timer in java

import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import java.awt.*;
import javax.swing.*;
import java.util.Timer;
import java.awt.event.*;
import java.awt.event.*;
import javax.swing.*;
class autos extends JLabel
{
#SuppressWarnings("serial")
int z=100,i=50;
public static void main(String[] args)
{
JFrame frame=new JFrame("Rectangle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setSize(1000,1000);
frame.add(new autos());
}
#Override
public void paintComponent( Graphics g )
{
for(i=1;i<=7;i++)
{
g.drawRect(z,100,100,100);
z=z+120;
//timer delay
}
}
}
Hello,I'm trying to create a program in java that draws mre rectangles one after another with a delay(not all of them at once).
Since sleep and TimeUnit will freeze the paintComponent, I'm a bit clueless.I tried to use a timer to make a delay, but I failed.I cannot understand how to use the timer in this case.
How do I make a time delay between the rectangles?
You should start by taking a look at How to use Swing Timers and Concurrency in Swing
You have to think of a Swing Timer as pseudo loop, which, every time it ticks, you update some value which, when your call repaint and your paintComponent method is called, you can update the UI appropriately
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private int count = 0;
public TestPane() {
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
count++;
if (count == 7) {
((Timer)e.getSource()).stop();
}
repaint();
}
});
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
timer.stop();
count = 0;
timer.start();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension((120 * 7) + 100, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
super.paintComponent(g);
int x = 100;
for (int rect = 1; rect <= count; rect++) {
g2d.drawRect(x, 100, 100, 100);
x += 120;
//timer delay
}
g2d.dispose();
}
}
}
ps- Click the panel to get the timer to start
You don't need a for loop to do this job.You can use a swing Timer like this:
int delay=500;// delay time in ms
Timer t=new Timer(delay,new ActionListener() {
#Override
public void actionPerformed(ActionEvent e){
autos.this.getGraphics().drawRect(z,100,100,100); // draw rectangle in your panel's graphics
repaint(); // this line calls paint component to show changes
i++;
z+=120;
if (i>70) t.stop(); // for loop's condition
}
});

JFrame flash image for 1/100 second

I have JFrame and need to flash an Image for 10 milliseconds (or the minimum the monitor can support).
Currently, this is what I have done:
I have a JFrame with a JPanel with overwritten paintComponent() method. When I need to flash that image, I call the repaint() method to draw the image on the JFrame, then schedule a next to call repaint() again to remove the image after 10 milliseconds. However it makes a long flash, that is very visible to bare eye.
Here is my code:
public static boolean show = true;
public static void main(String[] args) {
final JFrame f = new JFrame("Test");
f.setUndecorated(true);
f.setAlwaysOnTop(true);
f.setFocusable(false);
JPanel c = new JPanel() {
#Override
public void paintComponent(Graphics g) {
if (show) {
try {
// I was too lazy to save the image as a variable ;)
g.drawImage(ImageIO.read(ClassLoader.getSystemResource("Puppy.png")), 1, 1, null);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
super.paintComponent(g);
}
};
c.setOpaque(false);
c.setPreferredSize(new Dimension(1920, 1080));
f.getContentPane().add(c);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
com.sun.awt.AWTUtilities.setWindowOpaque(f, false);
Timer timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
long time = System.nanoTime();
show = true;
f.repaint();
timer.schedule(new TimerTask() {
#Override
public void run() {
show = false;
f.repaint();
System.out.println(System.nanoTime() - time + Boolean.toString(show));
}
}, 10);
}
}, 1000, 1000);
}
Question: Why is the frame flashing for so long, and not for 10 milliseconds?
There are so many things that might be occuring...
The time between calling setVisible and the time that the frame is actually visible to the user...
The time to setup the Event Dispatching Thread...
The time to setup the Timer and schedule the TimerTask
The time between making a request for the frame to be repainted and when the repaint actually occurs
The fact that you are requesting the frame to be repainted and not the component which is actually displaying the image
Some other activity on the system, both within the Java API and/or the OS...
You need to try and mitigate some of these issues...
So, instead of scheduling the TimerTask straight after calling setVisible, may be use a WindowListener so you can be notified when the window is opened and start the timer then...
Instead of building everything within the thread that calls main, make sure you are creating and updating the UI from within the context of the Event Dispatching Thread
Instead of calling repaint on the frame, try repainting the actual component you want repainted...
Instead of using java.util.Timer, use a javax.swing.Timer, it's safer when making modifications to the state of the UI
The following example simply uses a JLabel to render the image and removes it after the javax.swing.Timer ticks...
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
/**
*
* #author shane
*/
public class Test {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
BufferedImage img = ImageIO.read(new File("C:\\hold\\thumbnails\\_MTCGAC__Pulling_Cords_by_Dispozition.png"));
JLabel label = new JLabel(new ImageIcon(img));
JFrame frame = new JFrame("Testing");
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
Timer timer = new Timer(10, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
frame.remove(label);
frame.revalidate();
frame.repaint();
}
});
timer.setRepeats(false);
timer.start();
}
});
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
}

Java Swing JButton Time Delays (Flicker)

I am trying to make my JButton flicker red for this game I am creating. All the solutions on this website suggest using a thread and putting it to sleep or using a timer, however, the pause allays seems to come after the color change
Here is my code:
Color cb = board[Y1][X1].getBackground();
board[Y1][X1].setBackground(Color.RED);
//Pause
board[Y1][X1].setBackground(cb);
If I put a thread and put it to sleep on line 3 and comment out line 4 the pause will come before the JButton is turned red. (Note board is just a 2D array of JButtons)
There are any number reasons why this might be occurring and equally, any number of ways it might be fixed.
Based on your description, it sounds like you're trying to update the UI from outside of the Event Dispatching Thread.
Swing is a single thread environment, it's also not thread safe. Basically what this means is, there is an expectation that all interactions/changes to the UI are carried out within the context of the EDT. Failing to following this rule can lead to all sorts of weird and wonderful behaviour.
The simplest solution is to use a javax.swing.Timer, which allows you to schedule regular timed events which are guaranteed to be executed within the EDT, for example
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FlashyButton {
public static void main(String[] args) {
new FlashyButton();
}
public FlashyButton() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JButton button;
private Color[] colors = new Color[]{Color.RED, Color.YELLOW};
public TestPane() {
button = new JButton("Flash Gorden");
button.setContentAreaFilled(false);
button.setBorderPainted(false);
button.setFocusPainted(false);
button.setOpaque(true);
button.setBackground(Color.YELLOW);
setLayout(new GridBagLayout());
add(button);
Timer timer = new Timer(500, new ActionListener() {
private int counter = 0;
#Override
public void actionPerformed(ActionEvent e) {
counter++;
if (counter % 2 == 0) {
button.setBackground(colors[0]);
} else {
button.setBackground(colors[1]);
}
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
}
Take a look at Concurrency in Swing and How to Use Swing Timers for more details.
A more complex solution would allow you to use a Thread, but would require to update the UI by using SwingUtilities.invokeLater, which would place an event onto the EDT that would execute a Runnable interface, which you would use to update the UI. This could have synchronisation issues as the Thread you're calling from will have moved on before the actual event is triggered and could cause some dirty updates, unless you control the update process carefully...

Refreshing JPanel after loading next Image into it

I have created ImagePanel which is able to display images from the specified directory -> it sleeps 1 second and loads next image from the java project's directory.
It actually loads next image but it is not displayed(it does not refresh the panel), when it is done with all the files from the directory it shows only the last image from the directory. I would like to make it refresh after loading every image.
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class Okno extends JFrame {
JPanel jp;
ImagePanel ImagePanel;
JButton buttonExit;
JButton buttonWyjscie;
public Okno() {
}
public void createGUI() {
setSize(400, 400);
setLayout(new GridLayout());
buttonExit = new JButton("Exit");
buttonWyjscie = new JButton("Wyjscie");
// Sluchacz sluchacz = new Sluchacz();
// buttonExit.addActionListener(sluchacz);
buttonExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
jp = new JPanel();
jp.setBorder(new LineBorder(new Color(40, 120, 80), 4));
ImagePanel = new ImagePanel();
ImagePanel.setBorder(new LineBorder(Color.blue, 4));
jp.add(buttonExit);
add(jp);
add(ImagePanel);
setVisible(true);
slajd();
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void slajd() {
try {
File f = new File(".");
File[] tablicaPlikow = f.listFiles();
for (File el : tablicaPlikow) {
String rozszerzenie = el.getName().substring(
el.getName().length() - 3);
if (rozszerzenie.equals("jpg") || rozszerzenie.equals("peg")) {
System.out.println(rozszerzenie);
ImagePanel.setImage(el);
}
repaint();
}
setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Okno().createGUI();
}
});
}
}
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class ImagePanel extends JPanel {
private BufferedImage image;
public ImagePanel() {
}
public ImagePanel(String sciezka) {
setImage(new File(sciezka));
}
public void setImage(File plik) {
try {
image = ImageIO.read(plik);
System.out.println("tutaj");
repaint();
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
public void paint(Graphics g) {
if (image != null) {
Image b = image.getScaledInstance(getWidth(), getHeight(),
Image.SCALE_FAST);
g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
}
}
Sleeping in the EDT prevents swing from doing the painting, so you see only the last image. Instead of sleeping in the event dispatch thread, use a swing Timer to do repeated tasks:
private final ActionListener timerTask = new ActionListener() {
#Override
public void actionPerformed(final ActionEvent e) {
// Whatever you need to to that
showNextImage();
}
};
Timer timer = new Timer(1000, timerTask);
timer.start();
If loading the images is taking long time, consider using a background task for preloading the next one in memory, without blocking the EDT.
The short answer could be: in method slajd, after call to repaint();, add
Thread.sleep(1000);
However, this is completely contrary to the event-based nature of Swing, and in this particular case doesn't even work because, for efficiency reasons, Swing does not execute repaint() calls immediately. It "collects" and executes them only once after all event processing has concluded. If you include an sleep period (or any other long-running operation) in an event handler (directly or indirectly), repainting will be delayed and the application be extremely unresponsive to the point, as in this case, of not really be working.
What you need to do is in createGUI instantiate a Swing Timer (javax.swing.Timer; do not confuse it with java.util.Timer, or Timer classes in a few other packages) that fires every 1 second instead of calling slajd(). First firing should be immediate, or you could include code to display the first file. The associated listener, which would replace slajd() should keep track of the next file to display. You will most probably want to make this listener a full-fledged class with fields to support this tracking, a pointer to the ImagePanel where to display files, etc.
For more information, read Java's Tutorial on How to Use Swing Timers

Categories

Resources