Painting inside the swing timer does not work

I never worked with Timer before my problem was probably stupid. My program draws a circle, red, and after random seconds, the circle should change color to green. I just made a rocking timer, as you can see below in the code. And he introduces the actionPerformed() method, but does not change color. Could you help me somehow fix my problem with changing colors?

My code is:

 package igrica; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; public class ChangingCircle implements ActionListener{ JFrame frame; Timer timer; Random r; public static void main(String[] args) { ChangingCircle gui = new ChangingCircle(); gui.go(); } public void go() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyPanel panel = new MyPanel(); frame.getContentPane().add(BorderLayout.CENTER, panel); frame.setSize(300, 300); frame.setVisible(true); } public void actionPerformed(ActionEvent event) { frame.repaint(); } class MyPanel extends JPanel { public void paintComponent(Graphics g) { g.setColor(Color.red); g.fillOval(100, 100, 100, 100); Random r = new Random(); Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() { public void actionPerformed(ActionEvent ev) { System.out.println("Timer out"); g.setColor(Color.green); g.fillOval(100, 100, 100, 100); } }); timer.start(); } } } 
+5
source share
3 answers

There is quite a mess in your code. Try the following:

 public class ChangingCircle { Color color = Color.RED; MyPanel panel = new MyPanel(); public static void main(String[] args) { SwingUtilities.invokeLater(() -> { ChangingCircle gui = new ChangingCircle(); gui.go(); }); } public void go() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(panel, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); Random r = new Random(); Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() { public void actionPerformed(ActionEvent ev) { System.out.println("Timer"); color = Color.GREEN; panel.repaint(); } }); timer.setRepeats(false); timer.start(); } class MyPanel extends JPanel { private int size = 100, loc = 100; @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(color); g.fillOval(loc, loc, size, size); } @Override public Dimension getPreferredSize() { return new Dimension(size + loc, size + loc); } } } 

The idea is that a timer only changes the shape property that needs to be drawn, and then calls repaint() to reflect the change. paintComponent is called whenever necessary, even in quick succession and should return quickly.

Specific notes:

  • Run Swing from EDT .
  • Create and run a timer from outside of paintComponent , as it is called many times, and this will create and run many timers.
  • You should probably set a timer so that it doesn't repeat.
  • Call super.paintComponent(g); like the first thing inside paintComponent .
  • You seem to have an ActionListener that does nothing.

General tips:

  • Use the @Override annotation, if applicable.
  • Call pack() on the frame instead of manually setting its size and the @Override method getPreferredSize component you are drawing onto. Return a significant size based on what you draw.
  • Use add(component, location) , not vice versa (deprecated).
  • Do not use fields when local variables will be executed ( Random r for example).
  • Use uppercase constant names ( Color.RED instead of Color.RED ).
+8
source

Do not start the timer from the paintComponent method. This method should only be for drawing and drawing. Instead, start a timer in your constructor and within the TimerPerromed action and call repaint() , change the state of the class field and use this information in paintComponent to use this information to paint any new information.

eg.

 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Random; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; public class ChangingCircle { JFrame frame; public static void main(String[] args) { ChangingCircle gui = new ChangingCircle(); gui.go(); } public void go() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); MyPanel panel = new MyPanel(); frame.getContentPane().add(BorderLayout.CENTER, panel); frame.setSize(300, 300); frame.setVisible(true); } public void actionPerformed(ActionEvent event) { frame.repaint(); } class MyPanel extends JPanel { private Random r = new Random(); private boolean draw = false; public MyPanel() { Timer timer = new Timer(r.nextInt(5000) + 1000, new ActionListener() { public void actionPerformed(ActionEvent ev) { draw = true; repaint(); } }); timer.setRepeats(false); timer.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); if (draw) { g.setColor(Color.red); g.fillOval(100, 100, 100, 100); } } } } 

Also, be sure to call the super paintComponent method from your override.

If you need to change colors, give the JPanel the Color field, say, called color and change its value from the timer, and then call repaint() . Again in paintComponent, use the value of this field to draw an oval. Also in this situation, the timer should be repeated, so get rid of timer.setRepeats(false) in this situation.

+6
source

The timer runs asynchronously, and paintComponent expires before the timer expires.

0
source

Source: https://habr.com/ru/post/1240944/


All Articles