Search code examples
javaswingtimerjbuttonactionlistener

How to make a circle stop after revolving once or stop after 1 cycle?


So this is what I currently have. After I click the button, it should bounce from center to right and then left and then back to its original position. Then I should be able to click the button again so that it would start another cycle.

public class Bounce extends JFrame {

private static JButton btnMovement = new JButton("Click");
private Container container;
private Timer timer;
private int x = 290;
private int y = 350;
private int radius = 100;
private int moves = 2;

public Bounce() {
    container = getContentPane();
    container.setLayout(new FlowLayout());
    final MoveListener ml = new MoveListener();
    btnMovement.addActionListener(ml);
    timer = new Timer(5, ml);
}

private void Move() {
    x += moves;
    
    if (x + (radius * 2) > getWidth()) {
        x = getWidth() - (radius * 2);
        moves *= -1;
    } else if (x < 0) {
        x = 0;
        moves *= -1;
    }
    repaint();
}
    
class MoveListener implements ActionListener {
    public void actionPerformed(final ActionEvent event) {
        if (!timer.isRunning()){
            timer.start();
        } else if (timer.isRunning() && x == 290 && y == 350){ // I don't know what condition to put
            timer.stop(); 
        }
        Move();
    }
}

public void paint (Graphics g){
    super.paint(g);
    g.setColor(Color.black);
    g.fillOval(x - 5, y - radius - 5, radius + 110, radius + 110);
    g.setColor(Color.red);
    g.fillOval(x, y - radius, radius * 2, radius * 2);
}

public static void main(String args[]){
    final JFrame window = new Bounce();
    window.add(btnMovement);
    window.setSize(800, 800);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setVisible(true);
}
}

Solution

  • The ActionListener for the Timer should be seperate - it acts as pseudo loop. Basically, once the ball bounces of the left side, you change a flag to indicate that it should stop once it reaches or passes the mid point, for example...

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.Timer;
    
    public class Main {
        public static void main(String[] args) {
            new Main();
        }
    
        public Main() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            private Timer timer;
    
            private int xPos;
            private int yPos;
    
            public TestPane() {
                JButton btn = new JButton("Start");
    
                xPos = 95;
                yPos = 95;
    
                timer = new Timer(5, new ActionListener() {
                    private int xDelta = 1;
                    private boolean hasBounced = false;
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        xPos += xDelta;
                        int middleX = (getWidth() / 2) - 5;
                        if (xPos + 10 > getWidth()) {
                            xPos = getWidth() - 10;
                            xDelta *= -1;
                        } else if (xPos < 0) {
                            xPos = 0;
                            xDelta *= -1;
                            hasBounced = true;
                        } else if (hasBounced && xPos >= middleX) {
                            timer.stop();
                            btn.setEnabled(true);
                            hasBounced = false;
                        }
                        repaint();
                    }
                });
    
                setLayout(new BorderLayout());
                btn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        btn.setEnabled(false);
                        timer.start();
                    }
                });
    
                add(btn, BorderLayout.SOUTH);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(200, 200);
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.drawOval(xPos, yPos, 10, 10);
                g2d.dispose();
            }        
        }
    }
    

    A more complicated solution would be to record the current position as the timer starts and use that as the "end point", but I'll leave that up to you