Refreshing JPanel after loading next Image into it - java

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

Related

(multiple image ) animation Java

I have got a set of nodes in my program, each have a specific x,y location.
and each have a set of image icons.
I want to draw image animation for each nodes at its specific location.
Here is my code: (this only shows the last image which i know why!.)
public void showPicture() {
//nodes :
for(int i=0;i<thisGraph.getNode().size();i++){
if(thisGraph.getNode().get(i).getImageIcon()!=(null)){
for(int j=0;j<thisGraph.getNode().get(i).getImageIcon().size();j++){
if(j>0)
lables.get(lables.size()-1).setVisible(false);
JLabel jLabel1 = new JLabel();
lables.add(jLabel1);
jLabel1.setLayout(new GridBagLayout());
jLabel1.setIcon(thisGraph.getNode().get(i).getImageIcon().get(j));
jLabel1.setVisible(true);
jLabel1.setBounds((int)thisGraph.getNode().get(i).getX(),(int)thisGraph.getNode().get(i).getY(),195,163);
jPanel1.add(jLabel1);
}
}
}
}
This method showPicture() is called in a buttonActionListener.
And I also have another button which I want it to stop the image animations for all labels.
What I have tried:
Thread.sleep() -> it freezes the button and it only shows the last image
I figured I had to use timer, but through all the topics I went they only used it on one label, not multiple labels.
Edit
->
i read those examples given in the comments . and here is what i have resolved but it still is freezes the button and doesn't works :
int j = 0;
public void showPicture(){
//nodes :
for(int i=0;i<thisGraph.getNode().size();i++){
if(thisGraph.getNode().get(i).getImageIcon()!=(null)){
j=0;
while( j<thisGraph.getNode().get(i).getImageIcon().size()){
if(j>0)
lables.get(lables.size()-1).setVisible(false);
JLabel jLabel1 = new JLabel();
lables.add(jLabel1);
jLabel1.setLayout(new GridBagLayout());
jLabel1.setIcon(thisGraph.getNode().get(i).getImageIcon().get(j));
jLabel1.setVisible(true);
jLabel1.setBounds((int)thisGraph.getNode().get(i).getX(),(int)thisGraph.getNode().get(i).getY(),195,163);
jPanel1.add(jLabel1);
//
ActionListener act;
act = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
jLabel1.setVisible(true);
j++;
}
};
Timer timer = new Timer(1000, act );
timer.start();
timer.stop();
//
}
}
}}
Swing is single threaded and not thread safe. This means that you shouldn't block the Event Dispatching Thread with long running or blocking operations, like Thread.sleep. You should also, only ever update the UI (or anything it relies on) from within the context of the Event Dispatching Thread.
See Concurrency in Swing for more details.
Probably the simplest solution to your problem is to use a Swing Timer.
The idea is a you use a single Timer to act as the "main animation loop", changing the properties of ALL the objects you need updated within it.
The following is pretty basic example, it animates 100 JLabels, simply changing their background color with a randomly picked color
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
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 List<JLabel> nodes = new ArrayList<>(100);
private Random random = new Random();
private Color[] colors = new Color[] { Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, Color.MAGENTA};
public TestPane() {
setLayout(new GridLayout(0, 10));
for (int index = 0; index < 100; index++) {
JLabel label = new JLabel();
label.setBorder(new EmptyBorder(5, 5, 5, 5));
label.setOpaque(true);
label.setBackground(pickColor());
nodes.add(label);
add(label);
}
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (JLabel label : nodes) {
label.setBackground(pickColor());
}
}
});
timer.start();
}
protected Color pickColor() {
return colors[random.nextInt(colors.length)];
}
}
}
See How to Use Swing Timers for more details

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

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);
}
});
}
}

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 GUI: Image will be overwritten, Path the same -> show it in the frame (image still the same)

I want to show a changing image on my frame. The imagepath is always the same, but the image will be getting overwritten every 10 seconds from another program.
The problem is that the image is not changing when I overwrite it with another image with the same name. So in my understanding: Compiler looks every look in the path and gets the image -> when the image changed it will be changed on the frame!
I hope you understand my problem and somebody could help me.
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.io.File;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GUI extends JFrame{
public ImageIcon imageBar;
public JLabel labelimage1;
private JLabel labelimage2;
private JLabel bar1 = new JLabel();
private JLabel bar2 = new JLabel();
private JLabel bar3 = new JLabel();
private JLabel bar4 = new JLabel();
private JLabel bar5 = new JLabel();
private JButton buttonBar1 = new JButton("1");
private JButton buttonBar2 = new JButton("2");
private JButton buttonBar3 = new JButton("3");
private JButton buttonBar4 = new JButton("4");
private JButton buttonBar5 = new JButton("5");
private JPanel panel1 = new JPanel();
private JPanel panel2 = new JPanel();
private JPanel panel3 = new JPanel();
private JFrame window = new JFrame("Interface");
public GUI(){
//set the layouts
panel1.setLayout(new GridLayout(1, 2));
panel2.setLayout(new GridLayout(2, 1));
panel3.setLayout(new GridLayout(2, 5));
//place Panel2 and Panel3 in the window
panel1.add(panel2);
panel1.add(panel3);
//----Panel2
//refreshImage();
//----Panel3
panel3.add(buttonBar1); //add the bars 1-5 on panel3
panel3.add(buttonBar2);
panel3.add(buttonBar3);
panel3.add(buttonBar4);
panel3.add(buttonBar5);
//configure the frame
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.setSize(800, 400);
window.getContentPane().add(panel1);
}
public void refreshImage() {
panel2.removeAll(); //delete the old panel
//panel2.repaint();
//panel2.revalidate()
DrawImage pan = new DrawImage();
panel2.add(pan);
panel2.add(labelimage2);
}
}
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class DrawImage extends JPanel implements ActionListener{
private ImageIcon image;
public DrawImage(){
image = new ImageIcon("C:\\Users\\usuario\\Desktop\\image.png");
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
image.paintIcon(this, g, 50, 50);
repaint();
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
}
import java.io.File;
public class Main {
public static void main(String[] args) {
GUI Interface = new GUI();
while(true)
{
Interface.refreshImage();
try {
Thread.sleep(5000); //wait for 5000ms
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thank you very much!
The likely cause is Java is caching the image in memory, associated with the source name. So rather then trying to reload the image again, Java simply returns the cached version.
You could use ImageIcon#getImage#flush to force Java to reconstruct the image
Problems
You are calling refreshImage from a Thread other then the Event Dispatching Thread, this could cause issues with the updating of the components and cause rendering artifacts
You are forcefully removing the DrawImage pane and adding a new instance, rather the trying to reload the image
You're calling repaint within the paintComponent method, don't do this...
You should consider using a Swing Timer, which will allow you to schedule a regular update and be notified within the context of the Event Dispatching Thread.
You could provide a simple refresh method which flushes the current ImageIcon and schedule a repaint of the panel...or you could just use a JLabel and save your self the time
An example of Image#flush
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SlideShow {
public ImageIcon imageBar;
public static void main(String[] args) {
new SlideShow();
}
public SlideShow() {
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 DrawImage());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class DrawImage extends JPanel {
private ImageIcon image;
public DrawImage() {
image = new ImageIcon("D:\\thumbs\\image.png");
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
refresh();
}
});
timer.start();
}
public void refresh() {
image.getImage().flush();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image.getImage(), 0, 0, this);
}
}
}
The problem with this, is because the image data is loaded in a background thread, it won't may no be available when the component is first repainted, which could make the component appear to flicker.
A better approach would be to use ImageIO.read, which will ensure that the image is fully loaded before the method returns, the draw back here is that could cause the application to "pause" momentary as the image is loaded, personally, I'd use the refresh method to stop the the Timer (or set the Timer to non-repeating), start a background Thread to load the image (using ImageIO.read) call repaint (which is thread safe) and restart the Timer...
Your while (true) loop risks typing up the Swing event thread locking your program. If it doesn't do that, then you risk unpredictable threading issues by making Swing calls off of the event Thread. These problems can be solved easily by your using a Swing Timer not a while true loop to do your swapping.
Rather than removing and adding components, why not simply display images as ImageIcons within a single non-swapped JLabel.
To swap images here, simply call setIcon(...) on the JLabel.
For an example of using a Swing Timer to swap images, please check out my answer to a similar question here.
For example:
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class TimerImageSwapper {
public static final String[] IMAGE_URLS = {
"http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_01.png",
"http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_02.png",
"http://imaging.nikon.com/lineup/dslr/d7000/img/sample/img_04.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_08.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_05.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_01.png",
"http://imaging.nikon.com/lineup/dslr/d3200/img/sample/img_06.png" };
private ImageIcon[] icons = new ImageIcon[IMAGE_URLS.length];
private JLabel mainLabel = new JLabel();
private int iconIndex = 0;;
public TimerImageSwapper(int timerDelay) throws IOException {
for (int i = 0; i < icons.length; i++) {
URL imgUrl = new URL(IMAGE_URLS[i]);
BufferedImage image = ImageIO.read(imgUrl);
icons[i] = new ImageIcon(image);
}
mainLabel.setIcon(icons[iconIndex]);
new Timer(timerDelay, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
iconIndex++;
iconIndex %= IMAGE_URLS.length;
mainLabel.setIcon(icons[iconIndex]);
}
}).start();
}
public Component getMainComponent() {
return mainLabel;
}
private static void createAndShowGui() {
TimerImageSwapper timerImageSwapper;
try {
timerImageSwapper = new TimerImageSwapper(5 * 1000);
JFrame frame = new JFrame("Timer Image Swapper");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(timerImageSwapper.getMainComponent());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

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...

Categories

Resources