-1

I have 2 icons, and I want them to change every second. I also want it to always run and not stop. I am doing it with this code but I am not successful.

public static void main(String[] args) {
        Timer timer = new Timer();

        JFrameLeds jframeLeds = new JFrameLeds();
        jframeLeds.setVisible(true);

        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                Icon icono;

                icono = new ImageIcon(getClass().getResource("camera.png"));
                jframeLeds.jLabel1.setIcon(icono);



                icono = new ImageIcon(getClass().getResource("target.png"));
                jframeLeds.jLabel1.setIcon(icono);
            }
        };

        timer.schedule(timerTask, 0, 1000);
    }

2 Answers2

3

Using Thread class, or TimerTask is not recommended in a Swing environment. You should be using Swing Timers or Swing Workers since component updates should only take place to the Event Dispatch Thread. Take a look at this example.

However, in your case a flag boolean might be required in order to achieve what you want. An example that changes icons to a label:

public class ChangeIconsTest extends JFrame {
    private boolean icon1IsActive;

    public ChangeIconsTest(Icon icon1, Icon icon2) {
        super("test");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        JLabel label = new JLabel(icon1);
        icon1IsActive = true;

        Timer swingTimer = new Timer(1000, e -> {
            label.setIcon(icon1IsActive ? icon2 : icon1);
            icon1IsActive = !icon1IsActive;
        });
        swingTimer.start();

        add(label);
        pack();

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                ImageIcon icon1 = new ImageIcon(
                        new URL("https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg"));
                Icon icon2 = new ImageIcon(new URL("https://www.sample-videos.com/img/Sample-png-image-500kb.png"));
                ChangeIconsTest test = new ChangeIconsTest(icon1, icon2);
                test.setVisible(true);
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        });
    }
}
George Z.
  • 6,643
  • 4
  • 27
  • 47
1

As many people here reminded, using TimerTask class from java.util is highly NOT recommended while working in Swing or JavaFX environment.

The Swing components aren't thread-safe, changing the state or repainting the components in different thread than the one used by Swing components may lead to unexpected behaviour and strange bugs.

The Swing and AWT components are using Event Dispach Thread as main background thread to process the events. Events are fired inside every component method that might cause the change of interface. The setIcon() and even setText() methods of JLabel are also firing an event to the EDT.

To avoid future bugs every component state change should be done undnder EDT. The EDT can be called through EventQueue.invokeLater(Runnable), but since you are using Swing, you can call the SwingUtilities.invokeLater(Runnable) which calls the EventQueue inside.

The invokeLater method schedules the task and returns, there's also a invokeAndWait which schedules the task and waits until it's finished before returning.

For the sample below I borrowed the icon urls from the George Z. answer.
Sample code for covering the timed icon change:

public class TimedIconChange {

    static String ICON_1_URL = "https://4.img-dpreview.com/files/p/E~TS590x0~articles/3925134721/0266554465.jpeg";
    static String ICON_2_URL = "https://www.sample-videos.com/img/Sample-png-image-500kb.png";
    static String ICON_3_URL = "http://www.frankieballard.com/sites/g/files/g2000005856/f/Sample-image10-highres.jpg";

    public static void main(String[] args) throws MalformedURLException {
        Icon icon1 = new ImageIcon(new URL(ICON_1_URL));
        Icon icon2 = new ImageIcon(new URL(ICON_2_URL));
        Icon icon3 = new ImageIcon(new URL(ICON_3_URL));
        List<Icon> circularIcons = new ArrayList<>() {
            int i = 0;
            @Override
            public Icon get(int index) {
                return get();
            }
            private Icon get() {
                if (i == size()) {
                    i = 0;
                }
                return super.get(i++);
            }
        };
        circularIcons.add(icon3);
        circularIcons.add(icon2);
        circularIcons.add(icon1);

        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame();
            JLabel label = new JLabel();
            label.setIcon(icon1);
            frame.setLayout(new BorderLayout());
            frame.add(label);
            frame.pack();
            frame.setVisible(true);
            new Timer(1000, e -> label.setIcon(circularIcons.get(0))).start();
        });
    }
}

The sample contains a little implementation of circular list for circularIcons variable, to reduce the need of using boolean flag.

Additionaly, for longer tasks which are supposed to be working in the background using the SwingWorker class is recommended.

References and further reading on EDT:
https://en.wikipedia.org/wiki/Event_dispatching_thread
Why should I use a separate thread to show a GUI in JAVA
Why does my boilerplate Java desktop app JFrame use EventQueue.invokeLater in the main method?
https://docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html

itwasntme
  • 1,442
  • 4
  • 21
  • 28
  • 2
    (1-) You should NOT be using a TimerTask. Swing components should be updated on the Event Dispatch Thread. You should be using a Swing Timer. – camickr Nov 15 '19 at 19:22
  • Nice... but that's true, even had to remove the swing import to use the find the proper import of TimerTask used by OP and the post answers the question. But maybe it would be better to firstly state the issue of an answer instead of giving -1 instantly, because I'm more than happy to edit it and provide at least a good answer. (which I'm going to do now, will take me a bit) – itwasntme Nov 15 '19 at 19:32
  • @Frakcool you've got the point here. Understood and hopefuly provided proper answer, if this is still not right I'll fix it again. – itwasntme Nov 15 '19 at 21:09
  • @camickr if you could.. if not, it's also ok :) – itwasntme Nov 15 '19 at 21:10
  • Well done, I changed my -1 to +1 now and removed my comment – Frakcool Nov 15 '19 at 21:14
  • Yes, it is a better more accurate answer, but we already have an answer to the question. There is no need to duplicate answers. I have removed the down vote but can't up vote because the same basic answer/example was given 2 hours earlier. – camickr Nov 15 '19 at 21:22
  • I know that there's no need to duplicate answers, but I posted my original answer which provided "some solution" a minute after the one from George. After having issues pointed out in my answer I thought it would be better to fix it instead of removing. In addition both answers provide different approaches to the problem from the question. I stopped being greedy for upvotes, but (-) hurts the heart :P – itwasntme Nov 15 '19 at 21:41