I picked up this java code from stackoverflow and I'm trying to add a jbutton that will both begin animation and end animation of the circle, I'm stuck. So far I made the button but now what? I'm guessing that the TestPane constructor should contain a conditional statement...
this is what I have so far ``` import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; 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; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class BounceyDot { JButton button = new JButton("Animate Circle"); public static void main(String[] args) { new BounceyDot(); } public BounceyDot() { EventQueue.invokeLater(new Runnable() { public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH, button); } }); } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { button.addActionListener(this); } } public class TestPane extends JPanel { private int x = 0; private int y = 100; private int radius = 20; private int xDelta = 2; public TestPane() { Timer timer = new Timer(40, new ActionListener() { public void actionPerformed(ActionEvent e) { x += xDelta; if (x + (radius * 2) > getWidth()) { x = getWidth() - (radius * 2); xDelta *= -1; } else if (x < 0) { x = 0; xDelta *= -1; } repaint(); } }); timer.start(); } public Dimension getPreferredSize() { return new Dimension(200, 200); } protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.RED); g.fillOval(x, y - radius, radius * 2, radius * 2); } } } ```
``` JButton button = new JButton("Animate Circle"); class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { button.addActionListener(this); } } ``` The button isn't currently executing an 'actionlistener' when clicked, as the code inside actionPerformed never executes. You can add the actionlistener in your constructor ``` button.addActionListener(new AnimateCircleListener()); ``` Now that the button executes code, you should be able to get it to function as you want, to start and stop the animation.
Okay so I did that, but what I can't understand is how do I make the code execute, when the button is pressed. what is linking the two together?
okay, I got it, also I changed something's in the code and this is what I have so far... ``` import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class MovingCircleGUI{ JFrame frame; public int x,y; public int diameter=100; CircleDrawPanel drawPanel; Color color = Color.magenta; JButton startButton = new JButton("Click me to start the animation"); public static void main (String[] args){ MovingCircleGUI gui = new MovingCircleGUI(); gui.go(); } //this method sets up the JFrame and adds the drawpanel to the frame public void go(){ frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawPanel = new CircleDrawPanel(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setSize(500,500); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH,startButton); startButton.addActionListener(new AnimateCircleListener() { public void actionPerformed(ActionEvent evt) { for (int i = 0; i<2000; i++) { x++; while ((x + diameter) > frame.getWidth()) { while (x != 0) { x--; drawPanel.repaint(); try { Thread.sleep(3); } catch (Exception ex) {} } } drawPanel.repaint(); try { Thread.sleep(3); } catch (Exception ex) {} } } }); } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { startButton.addActionListener(this); } } class CircleDrawPanel extends JPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); } } } ``` only problem is the ball won't move anymore :s
You need the interface method "action performed"
I used it...didn't I? but the circle only ends up at the end...does that make sense?
AnimateCircleListener Ac= new AnimateCircleListener(); button.addActionListener(Ac);
import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; 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; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Tetris { JButton button = new JButton("Animate Circle"); public static void main(String[] args) { new Tetris(); } public Tetris() { EventQueue.invokeLater(new Runnable() { public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH, button); AnimateCircleListener Ac= new AnimateCircleListener(); button.addActionListener(Ac); } }); } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { //test System.exit(0); } } public class TestPane extends JPanel { private int x = 0; private int y = 100; private int radius = 20; private int xDelta = 2; public TestPane() { Timer timer = new Timer(40, new ActionListener() { public void actionPerformed(ActionEvent e) { x += xDelta; if (x + (radius * 2) > getWidth()) { x = getWidth() - (radius * 2); xDelta *= -1; } else if (x < 0) { x = 0; xDelta *= -1; } repaint(); } }); timer.start(); } public Dimension getPreferredSize() { return new Dimension(200, 200); } protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.RED); g.fillOval(x, y - radius, radius * 2, radius * 2); } } }
now use the new actionperfomed to give comment to your button
wait i'm totally confused... this is what I have at the moment... ``` import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class MovingCircleGUI{ JFrame frame; public int x,y; public int diameter=100; CircleDrawPanel drawPanel; Color color = Color.magenta; JButton startButton = new JButton("Click me to start the animation"); public static void main (String[] args){ MovingCircleGUI gui = new MovingCircleGUI(); gui.go(); } //this method sets up the JFrame and adds the drawpanel to the frame public void go(){ frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawPanel = new CircleDrawPanel(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setSize(500,500); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH,startButton); startButton.addActionListener(new AnimateCircleListener() { public void actionPerformed(ActionEvent evt) { for (int i = 0; i<2000; i++) { x++; while ((x + diameter) > drawPanel.getWidth()) { while (x != 0) { x--; drawPanel.repaint(); try { Thread.sleep(3); } catch (Exception ex) {} } } drawPanel.repaint(); try { Thread.sleep(3); } catch (Exception ex) {} } } }); } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { startButton.addActionListener(this); } } class CircleDrawPanel extends JPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); } } } ``` Now the issue is, the circle doesn't move, when the for-loop reaches the end (i = 2000), the circle just moves to the position where it should be at the end. whereas, if I take out the for-loop from the actionPerformed and just paste it in go() , the circle moves but obviously the button doesn't work anymore I want both...the button to work and circle to move, and the circle should continue moving infinitely until I close the program, though ideally I need to create a button that should be able to stop it - but i'll get to that later...right now I'm just trying to make it go till ininity
first of all the "startButton.addActionListener(this);" shouldnt be in the actionperformed method.
What you had to start with was pretty good, and only missing 2 steps to functioning as you envisioned. 1. Getting the startButton to execute code. 2. To have said code^^ toggle (turn on and off) the animation. 1. Getting a button to execute code, I wrote up a quick example you can look at ``` import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; public class ButtonTest2 { public static void main(String[] args) { new ButtonTest2(); } public ButtonTest2() { JFrame jf = new JFrame("button test"); jf.setSize(400, 200); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JPanel jp = new JPanel(); JButton jb = new JButton("button"); jb.addActionListener(new ClickListener()); jp.add(jb); jf.add(jp); jf.setVisible(true); } private class ClickListener implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println("Button clicked"); } } } ``` And here's a button being used as a random color generator: ``` import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class ButtonTest { public static void main(String[] args) { new ButtonTest(); } public JButton jb = new JButton("Change Color"); public JPanel jp = null; public String currentColor = "(255, 255, 255)"; public JLabel display = new JLabel("current color: "+currentColor); public ButtonTest() { jb.addActionListener(new ChangeColorListener()); JFrame jf = new JFrame("Color Generator"); jf.setSize(400, 300); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jp = new JPanel(); jp.setLayout(new BorderLayout()); //with borderlayout, we add components in desired areas of the JPanel, either at //BorderLayout.NORTH / SOUTH / EAST / WEST or CENTER like so //these layouts are called 'layout managers', I also like GridLayout jp.add(jb, BorderLayout.NORTH); jp.add(display, BorderLayout.SOUTH); jf.add(jp); jf.setVisible(true); } private class ChangeColorListener implements ActionListener { public void actionPerformed(ActionEvent e) { System.out.println("Button clicked"); //know button works Color randomColor1 = new Color((float)Math.random(), (float)Math.random(), (float)Math.random()); //I used the (float, float, float) constructor, which is //really (red, blue, green) values, //and should have a min of 0 and a max value of 1 //Math.random() provides a (pseudo) random number between 0-.999 // https://docs.oracle.com/javase/7/docs/api/java/awt/Color.html Color randomColor2 = Color.BLACK; switch((int)Math.floor(Math.random()*3)) { case 0: randomColor2 = Color.GREEN; break; case 1: randomColor2 = Color.MAGENTA; break; case 2: randomColor2 = Color.YELLOW; break; } //set jpanel background to a random color jp.setBackground(randomColor1); //set button background to a random color jb.setBackground(randomColor2); //get rgb value from jp background currentColor = "("+randomColor1.getRed()+", "+randomColor1.getGreen()+", "+ randomColor1.getBlue()+")"; //update jlabel display.setText("current color: "+currentColor); } } } ``` 2. Writing the code that pauses/continues the circle animation There are many ways to go about this, I'll show one that first came to my mind ``` //1. have a boolean represent if animation is currently running or not public boolean isAnimating = true; //2. toggle the isAnimating variable when the button is clicked private class AnimateButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { isAnimating = !isAnimating; } } //3. only update the position of the circle if isAnimating == true public void actionPerformed(ActionEvent e) { if(isAnimating) { x += xDelta; if (x + (radius * 2) > getWidth()) { x = getWidth() - (radius * 2); xDelta *= -1; } else if (x < 0) { x = 0; xDelta *= -1; } repaint(); } } }); ```
Be back soon, happy coding.
Okay so it did all that and still 'no go'...it seems that every time I use recursion in the actionPerformed, it just ends up collating all the loops and displaying only the end result - the result of the last loop. when I use infinite looping it the programme just goes on and on with no results and I end up terminating it.
okay...I got this...I think, I really should have figured it out earlier. The repaint() method runs on an event dispatching thread. When calling this method from within the loop, the event will not be processed until the loop runs to completion. Once the while loop has been completed the repaint() gets executed.
so now, it would probably be wise to get rid of the loops. Can't belive it took me 4 days to figure this out
okay so its up and running, this is what I've got so far...granted its a little messy it the moment ``` import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class MovingCircleGUI{ JFrame frame; JButton button = new JButton("Click me to start the animation"); public int x,y,xDelta; public int diameter=100; CircleDrawPanel drawPanel; Color color = Color.orange; public static void main (String[] args){ MovingCircleGUI gui = new MovingCircleGUI(); gui.go(); } //this method sets up the JFrame and adds the drawpanel to the frame public void go(){ frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawPanel = new CircleDrawPanel(); //drawPanel = new AnimateCircle(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setSize(500,500); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH, button); button.addActionListener(new AnimateCircleListener()); //frame.getContentPane().add(BorderLayout.CENTER, drawPanel); //drawPanel.add(button); } class CircleDrawPanel extends JPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); } } class AnimateCircle extends CircleDrawPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); //going forward if ((x + diameter) < getWidth() && (xDelta + diameter) != drawPanel.getWidth()) { x++; xDelta++; } //coming back if ((xDelta + diameter) == getWidth()) { x--; } //looping it back to forward if (x == 0) { xDelta = 0; } //prevents getting lost on changing window size /*changing window size back to small on return journey * when nearing turning point jams circle at edge, * circle will continue rolling if window size is increased again*/ if ((x + diameter) > getWidth()) { x = getWidth() - diameter; xDelta = x; } repaint(); try { Thread.sleep (1); } catch (Exception ex){} } } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { drawPanel = new AnimateCircle(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setVisible(true); } } } ``` now I need to work on the buttons
Here's the code I was alluding to before, if it helps at all; ``` import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; 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; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class BounceyDot { JButton button = new JButton("Animate Circle"); public TestPane animatePanel = new TestPane(); public static void main(String[] args) { new BounceyDot(); } public BounceyDot() { button.addActionListener(new AnimateCircleListener()); EventQueue.invokeLater(new Runnable() { public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setLayout(new BorderLayout()); frame.add(animatePanel); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH, button); } }); } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { animatePanel.isAnimating = !animatePanel.isAnimating; } } public class TestPane extends JPanel { private int x = 0; private int y = 100; private int radius = 20; private int xDelta = 2; public boolean isAnimating = true; public TestPane() { Timer timer = new Timer(40, new ActionListener() { public void actionPerformed(ActionEvent e) { if(isAnimating) { x += xDelta; if (x + (radius * 2) > getWidth()) { x = getWidth() - (radius * 2); xDelta *= -1; } else if (x < 0) { x = 0; xDelta *= -1; } } repaint(); } }); timer.start(); } public Dimension getPreferredSize() { return new Dimension(200, 200); } protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.RED); g.fillOval(x, y - radius, radius * 2, radius * 2); } } } ``` I'll look a bit at your newly made code too
Interestingly enough, you don't have to do ``` //g2.setColor(color); //g2.fillOval(x,y,diameter,diameter); ``` drawing in AnimateCircle, because when you do ``` super.paintComponent(g); ``` in AnimateCircle, it executes the ``` g2.setColor(color); g2.fillOval(x,y,diameter,diameter); ``` in the CircleDrawPanel class Good luck working on the buttons
so I was working on the button and this is what I have... I used what you showed my before by setting the Boolean ``` import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class MovingCircleGUI{ JFrame frame; JButton button = new JButton("Click me to start the animation"); public int x,y,xDelta; public int diameter=100; public boolean isAnimating = true; CircleDrawPanel drawPanel; Color color = Color.orange; public static void main (String[] args){ MovingCircleGUI gui = new MovingCircleGUI(); gui.go(); } //this method sets up the JFrame and adds the drawpanel to the frame public void go(){ frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawPanel = new CircleDrawPanel(); //drawPanel = new AnimateCircle(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setSize(500,500); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH, button); button.addActionListener(new AnimateCircleListener()); } class CircleDrawPanel extends JPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); } } class AnimateCircle extends CircleDrawPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); //going forward if ((x + diameter) < getWidth() && (xDelta + diameter) != drawPanel.getWidth()) { x++; xDelta++; } //coming back if ((xDelta + diameter) == getWidth()) { x--; } //looping it back to forward if (x == 0) { xDelta = 0; } //prevents getting lost on changing window size /*changing window size back to small on return journey * when nearing turning point jams circle at edge, * circle will continue rolling if window size is increased again*/ if ((x + diameter) > getWidth()) { x = getWidth() - diameter; xDelta = x; } repaint(); try { Thread.sleep (1); } catch (Exception ex){} } } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { if(isAnimating) { button.setText("Click me to start the animation"); isAnimating = false; drawPanel = new AnimateCircle(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setVisible(true); System.out.println("start"); } else { button.setText("Click me to stop the animation"); isAnimating = true; drawPanel = new CircleDrawPanel(); System.out.println("stop"); } } } } ``` the thing is sometimes the button works other times it doesn't, sometimes resizing the window makes the circle move regardless of whether or not its on
I thought that I was writing over that method, that's why I added ``` //g2.setColor(color); //g2.fillOval(x,y,diameter,diameter); ``` again
oh btw I mixed up the labels on the buttons, I put the stop where the start should be and vice versa which explains why resizing the window at the wrong time previously was animating the circle
I like the idea of having two separate JPanel classes, one for a paused circle and the other for an animated one. But I'm a little suspect of making multiple 'instances' of those classes, and the loop that changes the x/y values might not end even when you switch to the paused circle class. I'm not an expert, just thinking of the possibilities.. for a cleaner approach I stuck the old if(isAnimating) { //change x/y value } code back in there: ``` package God; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; public class MovingCircleGUI{ JFrame frame; JButton button = new JButton("Click me to start the animation"); public int x,y,xDelta; public int diameter=100; public boolean isAnimating = false; CircleDrawPanel drawPanel; Color color = Color.orange; public static void main (String[] args){ MovingCircleGUI gui = new MovingCircleGUI(); gui.go(); } //this method sets up the JFrame and adds the drawpanel to the frame public void go(){ frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); drawPanel = new AnimateCircle(); //drawPanel = new AnimateCircle(); frame.getContentPane().add(BorderLayout.CENTER, drawPanel); frame.setSize(500,500); frame.setVisible(true); frame.getContentPane().add(BorderLayout.SOUTH, button); button.addActionListener(new AnimateCircleListener()); } class CircleDrawPanel extends JPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); } } class AnimateCircle extends CircleDrawPanel{ public void paintComponent (Graphics g){ super.paintComponent(g); Graphics2D g2=(Graphics2D)g; g2.setColor(color); g2.fillOval(x,y,diameter,diameter); //going forward if(isAnimating) { if ((x + diameter) < getWidth() && (xDelta + diameter) != drawPanel.getWidth()) { x++; xDelta++; } //coming back if ((xDelta + diameter) == getWidth()) { x--; } //looping it back to forward if (x == 0) { xDelta = 0; } //prevents getting lost on changing window size /*changing window size back to small on return journey * when nearing turning point jams circle at edge, * circle will continue rolling if window size is increased again*/ if ((x + diameter) > getWidth()) { x = getWidth() - diameter; xDelta = x; } } repaint(); try { Thread.sleep (1); } catch (Exception ex){} } } class AnimateCircleListener implements ActionListener { public void actionPerformed(ActionEvent e) { if(isAnimating) { button.setText("Click me to start the animation"); isAnimating = false; System.out.println("start"); } else { button.setText("Click me to stop the animation"); isAnimating = true; System.out.println("stop"); } } } } ``` Though I think the idea of having 2 separate classes, one for paused and one for animated is still possible.
Thank you...I like how you changed it, I had a feeling that the multiple instances thing was messing with the x values. This way works much better Thank you so much to both of you, @woodrow73 @magepker728 , you were both very helpful. Now tomorrow I'll look at getting rid of that irritating loop hole in the AnimateCircle class...goodnight :)
:D anytime
This is incredible. Great work guys!
Join our real-time social learning platform and learn together with your friends!