Search code examples
javaswinganimationtimerpaintcomponent

how to animate a circle


Im in the process of learning GUI in Java. Im making this very simple GUI that literally scales the circle size every 5 milliseconds and then once the width and height reaches a certain number, it scales back down and it keep on doing this process. Ive managed to make the circle appear on the screen but for some weird reason its not scaling.

public class circle extends JPanel implements ActionListener {



    Timer tm = new Timer(5, this);

    int XDiameter = 20;
    int YDiameter = 20;

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
g.setColor(Color.GREEN);
        g.fillOval(40, 40, XDiameter, YDiameter);

        tm.start();
    }



    @Override
    public void actionPerformed(ActionEvent e) {

        SuperSizeCircle();
        repaint();

    }

    public void SuperSizeCircle(){
        while(true){
            XDiameter = XDiameter + 2;
            YDiameter = YDiameter + 2;
            if(XDiameter > 200 && YDiameter > 200){
                XDiameter --;
                YDiameter --;
            }else if(XDiameter < 20 && YDiameter < 20){
            XDiameter ++;
            YDiameter ++;
        }
        }
    }

}

Main class:

public class main {



public static void main(String[] args) {
    // TODO Auto-generated method stub
    JFrame frame = new JFrame("Circle enlarger");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(400,400);
    frame.setVisible(true);

    circle co = new circle();
    frame.add(co);

}

}


Solution

    1. The while(true) is never good in Swing animation. Throw it out the window and say goodbye to it forever. Take that out of your method. You don't need it. The timer takes care of the "looping"

    2. Don't start the Timer in the paintComponent method. Do it in the constructor.

    3. You should run your Swing apps on the Event Dispatch Thread by wrapping your code in a SwingUtilities.invokeLater... See Initial Threads for more details.

    4. You should be setting the frame visible after adding all component.

    5. You if statements in your SuperSizeCircle() mess with each other if you look at the logic. That's why you need the keep adding. That doesn't look right to me. Instead us a flag (boolean) to determine whether it should grow or shrink

      boolean grow = true;
      
      public void SuperSizeCircle() {
      
          if (XDiameter >= 200) {
              grow = false;
          }
          if (XDiameter <= 20) {
              grow = true;
          }
      
          if (grow) {
              XDiameter += 2;
              YDiameter += 2;
          } else {
              XDiameter -= 2;
              YDiameter -= 2;
          }
      }
      

    enter image description here

    import java.awt.Color;
    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.SwingUtilities;
    import javax.swing.Timer;
    
    public class CirclePanel extends JPanel implements ActionListener {
    
        Timer tm = new Timer(15, this);
    
        boolean grow = true;
    
        int XDiameter = 20;
        int YDiameter = 20;
    
        public CirclePanel() {
            tm.start();
        }
    
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.GREEN);
            g.fillOval(50, 50, XDiameter, YDiameter);
        }
    
        @Override
        public void actionPerformed(ActionEvent e) {
    
            SuperSizeCircle();
            repaint();
    
        }
    
        public void SuperSizeCircle() {
    
            if (XDiameter >= 200) {
                grow = false;
            }
            if (XDiameter <= 20) {
                grow = true;
            }
    
            if (grow) {
                XDiameter += 3;
                YDiameter += 3;
            } else {
                XDiameter -= 3;
                YDiameter -= 3;
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    JFrame frame = new JFrame("Circle enlarger");
                    CirclePanel co = new CirclePanel();
                    frame.add(co);
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setSize(300, 300);
                    frame.setVisible(true);
                }
            });
        }
    }