1

I would like to use the method SwingUtilities.invokeLater so that all the Swing components of my program are updated by the event dispatch thread, since it is a good practice.

But if I wrap all the code of the main method in SwingUtilities.invokeLater(new Runnable { public void run() { /* code */ }}); the window freezes (which is normal since my code has an animation loop that takes a few seconds to complete). Where should I put that SwingUtilities.invokeLater method?

Program code (without the SwingUtilities.invokeLater method)

import java.awt.Color;
import java.awt.Dimension;
import java.awt.geom.Rectangle2D;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test {

  public static void main(String[] args) {
    int width = 854;
    int height = 480;
    String title = "Test";
    BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    JFrame frame = new JFrame();
    JPanel panel = new JPanel() {
      protected void paintComponent(Graphics graphics) {
        super.paintComponent(graphics);
        Graphics2D graphics2D = (Graphics2D) graphics;
        graphics2D.drawImage(bufferedImage, 0, 0, null);
      }
    };
    frame.add(panel);
    frame.getContentPane().setPreferredSize(new Dimension(width, height));
    frame.pack();
    frame.setTitle(title);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
    int size = height/3;
    int x = -size;

    while (x <= width) {
      Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
      graphics2D.setColor(Color.RED);
      graphics2D.fill(new Rectangle2D.Double(x, 0, size, size));
      graphics2D.setColor(Color.GREEN);
      graphics2D.fill(new Rectangle2D.Double(x, size, size, size));
      graphics2D.setColor(Color.BLUE);
      graphics2D.fill(new Rectangle2D.Double(x, 2 * size, size, size));
      graphics2D.dispose();
      panel.repaint();
      ++x;

      try {
        Thread.sleep(10);
      } catch (InterruptedException exception) {
        exception.printStackTrace();
      }
    }

    frame.dispose();
  }
}

Program screenshot

animation

This is an animation in which the three coloured strips are gradually stretching to the right edge of the window.

Géry Ogam
  • 6,336
  • 4
  • 38
  • 67
  • 4
    You `while-loop` is blocking the EDT, preventing updates. A Swing `Timer` is probably the solution you're looking for. Take a look at [How to use Swing Timers](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) for starters. You need to think of the timer as a pseudo loop, each time it ticks, it acts as an iteration of the loop. Because it updates within the context of the EDT, it's safe to use to make updates to the UI – MadProgrammer Jan 24 '17 at 00:52
  • 3
    And you've got way too much code in your main static method. In fact your program is nothing but one big static main method. Instead your code should strive to be more OOPs compliant, and your main method should serve only to create the main classes, hook them together, start them running, and that's it. – Hovercraft Full Of Eels Jan 24 '17 at 01:09
  • Animations, hmmm... Oh, did you ever hear of JavaFX? – Mordechai Jan 24 '17 at 01:34
  • Swing was designed for older systems where animations were very heavy. With stronger CPU power nowadays, and with hardware accellaration Swing is somewhat outdated. JavaFX makes animations a breeze to use. – Mordechai Jan 24 '17 at 01:37
  • 3
    For [example](http://stackoverflow.com/questions/15078835/i-am-trying-to-make-ball-gradually-move/15079240#15079240), [example](http://stackoverflow.com/questions/23819196/how-to-move-a-circle-automatically-in-java/23819480#23819480), [example](http://stackoverflow.com/questions/28342538/java-swing-timer-and-animation-how-to-put-it-together/28342986#28342986) – MadProgrammer Jan 24 '17 at 01:58

1 Answers1

3

Here is that self-contained code (good call on posting that, BTW) updated to use a Timer as suggested by @MadProgrammer. In order to access the x variable, it was moved into the action listener defined for the timer. In order to access the Timer from within the action listener, it was moved to being a class attribute. The latter meant it was easier to move the bulk of the code into a constructor for an instance of the object.

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class Test {

    Timer timer;

    Test() {
        int width = 854;
        int height = 480;
        String title = "Test";
        BufferedImage bufferedImage = new BufferedImage(
                width, height, BufferedImage.TYPE_INT_RGB);
        JFrame frame = new JFrame();
        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics graphics) {
                super.paintComponent(graphics);
                Graphics2D graphics2D = (Graphics2D) graphics;
                // when you have an ImageObserver, may as well use it
                //graphics2D.drawImage(bufferedImage, 0, 0, null);
                graphics2D.drawImage(bufferedImage, 0, 0, this);
            }

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(width,height);
            }
        };
        frame.add(panel);
        frame.pack();
        frame.setTitle(title);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        int size = height / 3;

        ActionListener animationListener = new ActionListener() {

            int x = -size;

            @Override
            public void actionPerformed(ActionEvent e) {
                if (x <= width) {
                    Graphics2D graphics2D = (Graphics2D) bufferedImage.getGraphics();
                    graphics2D.setColor(Color.RED);
                    graphics2D.fill(new Rectangle2D.Double(x, 0, size, size));
                    graphics2D.setColor(Color.GREEN);
                    graphics2D.fill(new Rectangle2D.Double(x, size, size, size));
                    graphics2D.setColor(Color.BLUE);
                    graphics2D.fill(new Rectangle2D.Double(x, 2 * size, size, size));
                    graphics2D.dispose();
                    panel.repaint();
                    ++x;
                } else {
                    timer.stop();
                    frame.dispose();
                }
            }
        };
        timer = new Timer(10, animationListener);
        timer.start();
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            new Test();
        };
        SwingUtilities.invokeLater(r);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • 2
    @MadProgrammer Some people are mongrels. I always like your answers. This one was my little 'indulgence' given the nice self contained example - I just couldn't resist it. If people want to down-vote, they can go for broke. ;) – Andrew Thompson Jan 24 '17 at 02:25
  • 1
    I have had some downvoted answers too when answering questions like this, but as you did, when I see some effort in the self contained code and a good explanation I also can't resist to answer (1+ btw) – Frakcool Jan 24 '17 at 02:42
  • 1
    Thank you, that is exactly what I was looking for. Now I understand the purpose of the `Timer` class. – Géry Ogam Jan 24 '17 at 20:35
  • Your welcome. Glad I could help, and please keep up the hood work of posting runnable code and an explicit question. :) – Andrew Thompson Jan 24 '17 at 20:37