Question

I am trying to make a program that work like this: In Window class every time I click on the button, the method panel2 of Panel is called: first it is drawing a first circle, then a second one (after the time defined in the timer). Then, I click again on the button, and it is drawing a fist circle, then a second one then a third one. etc. The problem is that it when I click to obtain 3 circles appearing one after the other, the two first circles drawn at the previous step (before I pressed a second time the button) stay on the screen and only the third circle is drawn when i press the button (instead of having : first circle drawn, second circle drawn, third circle drawn). I hope I am clear. Here is a simple code:

Window

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.JPanel;

public class Window extends JFrame implements ActionListener{
    int h = 2;

    Panel b = new Panel();
    JPanel container = new JPanel();

    JButton btn = new JButton("Start");
    JButton bouton = new JButton();


    Panel boutonPane = new Panel();

    public Window(){

        this.setTitle("Animation");
        this.setSize(300, 300);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        container.setBackground(Color.white);
        container.setLayout(new BorderLayout());
        JPanel top = new JPanel();

        btn.addActionListener(this);
        top.add(btn);
        container.add(top);
        this.setContentPane(container);
        this.setVisible(true);
    }

    public void window2(){
        this.setTitle("ADHD");
        this.setSize(1000,700);
        this.setLocationRelativeTo(null);


        if (h < 11){

            boutonPane.panel2(h);
            bouton.addActionListener(this);
            boutonPane.add(bouton);
            this.add(boutonPane);
            this.setContentPane(boutonPane);
            updateWindow2();
        }
        this.setVisible(true);
    }

    public void updateWindow2(){
        boutonPane.panel2(h);
        this.revalidate();
        this.repaint();
    }

    public void actionPerformed(ActionEvent e){
        if ((JButton) e.getSource() == btn){
            System.out.println("pressed0");
            window2();
        }
        if ((JButton) e.getSource() == bouton){
            h++;
            System.out.println("pressed" + h);
            updateWindow2();
        }
    }

    public static void main(String[] args){
        Window w = new Window();
    }
}

Panel

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;


public class Panel extends JPanel implements ActionListener{

    int m;
    int u=0;
    int lgi, lrgi;
    int [] ta;
    Timer timer1 = new Timer(300, this);

    Panel(){

    }

    public void panel2(int n){
        m=n;
        ta = new int [n];

        for(int it=0; it<m;it++){
            ta[it]=100*it;
        }

        timer1.start();
    }


    public void paintComponent(Graphics gr){
        super.paintComponent(gr);

        gr.setColor(Color.red);
        for(int i=0;i<m;i++){
            gr.fillOval(ta[i],ta[i], 150, 150);
        }
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        if(u<m){
            u++;
            revalidate();
            repaint();
        }
    }

}
Was it helpful?

Solution

Your code needs use two int values to decide how many circles to draw and when:

  • The first int should be the count of current circles to draw, say called, currentCirclesToDraw.
  • The second int will be the number of circles to draw total.
  • If you use a List<Ellipse2D> like I suggest, then this number will be the size of the list. So if the List is called ellipseList, then the 2nd number will be ellipseList.size().
  • The first variable will be incremented in the timer up to the size of the list, but no larger, and will be used by paintComponent method to decide how many circles to draw.
  • Key point here: the first number, the currentCirclesToDraw, must be re-set to 0 when the button is pressed. This way your paintComponent method will start out drawing 0 circles, then 1, then 2, ...

For example, the paintComponent method could look like so:

@Override
protected void paintComponent(Graphics g) {
  super.paintComponent(g);
  Graphics2D g2 = (Graphics2D) g;
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
  g2.setColor(CIRCLE_COLOR);
  for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
     g2.fill(ellipseList.get(i));
  }
}

I use the second term in the for loop conditional statement, i < currentCirclesToDraw && i < ellipseList.size() as an additional fail-safe to be sure that we don't try to draw more circles then we have in our list.

My Timer's ActionListener would increment the currentCirclesToDraw variable and call repaint. It would stop the Timer once currentCirclesToDraw reaches the size of the ellipseList:

private class TimerListener implements ActionListener {
  @Override
  public void actionPerformed(ActionEvent e) {
     if (currentCirclesToDraw < ellipseList.size()) {
        currentCirclesToDraw++;
        repaint();
     } else {
        // stop the Timer
        ((Timer)e.getSource()).stop();
     }
  }
}

And my button's actionPerformed method would reset currentCirclesToDraw to 0, would add a new Ellipse2D to my ellipseList (if we've not yet reached the MAX_CIRCLE_INDEX), would call repaint() to clear the JPanel, and would construct and start the Timer:

  public void actionPerformed(java.awt.event.ActionEvent arg0) {
     currentCirclesToDraw = 0;  // this is key -- reset the index used to control how many circles to draw
     if (ellipseList.size() < MAX_CIRCLE_INDEX) {
        double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
        double y = x;
        double w = CIRCLE_WIDTH;
        double h = CIRCLE_WIDTH;
        ellipseList.add(new Ellipse2D.Double(x, y, w, h));
     }
     repaint(); // clear image
     new Timer(TIMER_DELAY, new TimerListener()).start();
  };

Edit 3/30/14 Note it all can be put together like this:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

/**
 * http://stackoverflow.com/a/22714405/522444
 * http://stackoverflow.com/questions/22712655/repaint-in-panel-method-not-updated
 * @author Pete
 *
 */
@SuppressWarnings("serial")
public class TimerCircles extends JPanel {
   private static final int PREF_W = 1000;
   private static final int PREF_H = 700;
   private static final Color CIRCLE_COLOR = Color.RED;
   public static final int MAX_CIRCLE_INDEX = 11;
   public static final int TIMER_DELAY = 300;
   public static final int CIRCLE_WIDTH = 100;
   private final List<Ellipse2D> ellipseList = new ArrayList<>();
   private int currentCirclesToDraw = 0;

   public TimerCircles() {
      add(new JButton(new ButtonAction("New Circle", KeyEvent.VK_C)));
   }

   @Override
   public Dimension getPreferredSize() {
      return new Dimension(PREF_W, PREF_H);
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setColor(CIRCLE_COLOR);
      for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
         g2.fill(ellipseList.get(i));
      }
   }

   private class ButtonAction extends AbstractAction {
      public ButtonAction(String name, int mnemonic) {
         super(name);
         putValue(MNEMONIC_KEY, mnemonic);
      }

      public void actionPerformed(java.awt.event.ActionEvent arg0) {
         currentCirclesToDraw = 0;  // this is key -- reset the index used to control how many circles to draw
         if (ellipseList.size() < MAX_CIRCLE_INDEX) {
            double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
            double y = x;
            double w = CIRCLE_WIDTH;
            double h = CIRCLE_WIDTH;
            ellipseList.add(new Ellipse2D.Double(x, y, w, h));
         }
         repaint(); // clear image
         new Timer(TIMER_DELAY, new TimerListener()).start();
      };
   }

   private class TimerListener implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         if (currentCirclesToDraw < ellipseList.size()) {
            currentCirclesToDraw++;
            repaint();
         } else {
            // stop the Timer
            ((Timer)e.getSource()).stop();
         }
      }
   }

   private static void createAndShowGui() {
      TimerCircles mainPanel = new TimerCircles();

      JFrame frame = new JFrame("TimerCircles");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top