Search code examples
javaswinggraphicstimerjframe

Java AWT How to draw objects with delay


I would like to draw a new random shape every 2 seconds.

I already have a window, that shows immediately some shapes. I tried to mess around with Timer to make new things appear in the window after a few seconds, but it didn't work, or the whole program freezes. Is it a good idea to use Timer? How should I implement it, to make it work?

import javax.swing.*;
import java.awt.*;
import java.util.Random;

class Window extends JFrame {

    Random rand = new Random();
    int x = rand.nextInt(1024);
    int y = rand.nextInt(768);
    int shape = rand.nextInt(2);

    Window(){
        setSize(1024,768);
        setVisible(true);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(new Color(0, 52, 255));
        switch(shape) {
            case 0:
                g.fillOval(x, y, 50, 50);
                break;
            case 1:
                g.fillRect(x,y,100,100);
                break;
        }
        repaint();
    }
}

public class Main {
    public static void main(String[] args) {
        Window window = new Window();
    }
}

I would also like to draw some random shapes. Is it ok, to use switch in paint method for this purpose? I would make a random variable, if it's 1 it would paint rectangle, if it's 2 it would paint oval etc.


Solution

  • First of all, do not change the way JFrame gets painted (with other words, do not override paintComponent() of a JFrame). Create an extending class of JPanel and paint the JPanel instead. Secondly, do not override paint() method. Override paintComponent(). Thirdly, always run Swing applications with SwingUtilities.invokeLater() since they should run in their own thread named EDT (Event dispatch thread). Finally, javax.swing.Timer is what you are looking for.

    Take a look at this example. It paints an oval shape in random X,Y every 1500ms.

    Preview:

    preview

    Source code:

    import java.awt.BorderLayout;
    import java.awt.Graphics;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    public class DrawShapes extends JFrame {
        private ShapePanel shape;
    
        public DrawShapes() {
            super("Random shapes");
            getContentPane().setLayout(new BorderLayout());
            getContentPane().add(shape = new ShapePanel(), BorderLayout.CENTER);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(500, 500);
            setLocationRelativeTo(null);
    
            initTimer();
        }
    
        private void initTimer() {
            Timer t = new Timer(1500, e -> {
                shape.randomizeXY();
                shape.repaint();
            });
            t.start();
        }
    
        public static class ShapePanel extends JPanel {
            private int x, y;
    
            public ShapePanel() {
                randomizeXY();
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                g.fillOval(x, y, 10, 10);
            }
    
            public void randomizeXY() {
                x = (int) (Math.random() * 500);
                y = (int) (Math.random() * 500);
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> new DrawShapes().setVisible(true));
        }
    }