Question

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

}

}

Was it helpful?

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

OTHER TIPS

Here is a JavaFX based solution, just for variation.

small big

import javafx.animation.ScaleTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class CircleScaler extends Application {

    private static final double MIN_RADIUS = 10;
    private static final double MAX_RADIUS = 100;
    private static final double PANE_SIZE  = 2 * (MIN_RADIUS + MAX_RADIUS);
    private static final double SCALE_FACTOR = MAX_RADIUS / MIN_RADIUS;
    private static final Duration SCALE_DURATION = Duration.seconds(3);

    @Override
    public void start(Stage stage) {
        final Circle circle = new Circle(MIN_RADIUS, Color.CRIMSON);

        final StackPane root = new StackPane(circle);
        root.setPrefSize(PANE_SIZE, PANE_SIZE);

        stage.setScene(new Scene(root, Color.PALETURQUOISE));
        stage.show();

        animateCircle(circle);
    }

    private void animateCircle(Circle circle) {
        ScaleTransition scaler = new ScaleTransition(
                SCALE_DURATION,
                circle
        );
        scaler.setFromX(1);
        scaler.setToX(SCALE_FACTOR);
        scaler.setFromY(1);
        scaler.setToY(SCALE_FACTOR);

        scaler.setAutoReverse(true);
        scaler.setCycleCount(ScaleTransition.INDEFINITE);
        scaler.play();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

For more sophisticated animated circles in JavaFX, see james-d's animation of 300 balls.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top