0

Here is the code for displaying circles with varying radius on a panel inside a frame with a given delay rate, but the code is showing the final output not the intermediate stages i.e., the circles are not appearing one by one but all the circles are coming at once as a final output. There may be some errors related to button action listeners and panel threads. The code is taking initial circle radius and the total number of iterations (the total number of circles to be displayed), radius of each next circle gets incremented by 10.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ControlCircle extends JFrame {
  private JButton jbtEnlarge = new JButton("Start");
  private JButton jbtShrink = new JButton("Stop");
  private CirclePanel canvas = new CirclePanel();

  private int radius = 0;
  private int iter;

  public ControlCircle() {
    JPanel panel = new JPanel();
    JPanel jp = new JPanel();
    jp.setPreferredSize(new Dimension(300, 0));
    panel.add(jbtEnlarge);
    panel.add(jbtShrink);

    this.add(jp, BorderLayout.WEST);
    this.add(canvas, BorderLayout.CENTER);
    this.add(panel, BorderLayout.SOUTH);

    final JTextField f1 = new JTextField(8),f2 = new JTextField(8);

    jp.setLayout(new FlowLayout(FlowLayout.RIGHT, 50, 30));
    jp.add(new JLabel("Radius"));
    jp.add(f1);
    jp.add(new JLabel("Iteration"));
    jp.add(f2);

    f1.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        radius = Integer.parseInt(new String(f1.getText()));
      }
    });

    f2.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        iter = Integer.parseInt(new String(f2.getText()));
      }
    });

    jbtEnlarge.addActionListener(new EnlargeListener());
    jbtShrink.addActionListener(new ShrinkListener());
  }

  public static void main(String[] args) {
    JFrame frame = new ControlCircle();

    frame.setTitle("ControlCircle");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(800, 600);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  class EnlargeListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      canvas.enlarge();
    }
  }

  class ShrinkListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      //canvas.shrink();
    }
  }

  class CirclePanel extends JPanel {
    private int r = radius;

    public void enlarge() {
      //radius += 2;

      repaint();
    }

    public void shrink() {
      radius -= 2;

      repaint();
    }

    protected void paintComponent(Graphics g) {
      super.paintComponent(g);

      for (int i = 0; i < iter; i++) {
        g.drawOval(getWidth() / 2 - r, getHeight() / 2 - r, 2 * r, 2 * r);

        try {
          Thread.sleep(100);
        } catch (Exception exp) {
        }

        r = r + 10;
      }

      r = 0;
    }
  }
}
Roman C
  • 49,761
  • 33
  • 66
  • 176
AvmnuSng
  • 335
  • 1
  • 2
  • 9
  • 2
    Here we go again. Read http://docs.oracle.com/javase/tutorial/uiswing/concurrency/. If you sleep in the EDT you prevent it from doing its job: repainting the screen. – JB Nizet May 31 '13 at 12:38
  • 1
    To start with, don't sleep within the Even Dispatching Thread. Take a look at [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/). Take a look at [this](http://stackoverflow.com/questions/13888026/java-clock-isnt-counting-in-swing/13888101#13888101) – MadProgrammer May 31 '13 at 12:38
  • Move the loop to the action to make access to the properties in the paint event. – Roman C May 31 '13 at 12:42
  • @RomanC So instead of blocking the EDT in the paint method, you'd suggest the OP blocks it in the action performed method instead? – MadProgrammer May 31 '13 at 12:44
  • @MadProgrammer Where did you read that I suggest "blocking the EDT"? – Roman C May 31 '13 at 12:55
  • @RomanC: the loop blocks the EDT already. Moving it somewhere else, but still executing it in the EDT, would still cause the GUI to freeze until the loop has ended. – JB Nizet May 31 '13 at 13:00
  • @JBNizet You are talking to nothing, OP said that with using threads and event listeners. – Roman C May 31 '13 at 13:03
  • @romanc But there are no threads in the OPs code. Using a Thread to solve the OPs problem is overkill for what they are trying to achieve. A simple javax.swing.Timer would be more then sufficient – MadProgrammer May 31 '13 at 20:26
  • @MadProgrammer You should not stick with the code OP expose, rather take it into attention and write the code OP is asked to implement, not the code you want to write. – Roman C Jun 01 '13 at 07:57
  • @RomanC you are write the both code presented by MadProgrammer are implementation of some good work of programming but far away from my problem that is to be solved. If there is any suggestion you can give to me.. – AvmnuSng Jun 01 '13 at 08:07
  • @romanc We're also not code factories. Where possible we should attempt to help posters reach a suitable solution to their problems, based on the small amount of information which is generally provided to use via their code snippets. This is not always achievable. We should also be encourage good coding practices where we can - IMHO – MadProgrammer Jun 01 '13 at 08:16
  • @avmnusng It's not only your problem but a known problem and has solutions I even tried to implement myself. I will not say that I completely understand it but it seemed to work that time. I'm interesting in the answer if I understand precisely what the question is about and what the answer is required. Unfortunately here on SO it's difficult to achieve. – Roman C Jun 01 '13 at 08:17
  • @MadProgrammer Not only code snippets but the content that fully describes the problem. Code snippets only help to see what OP is tried and what's wrong with it but it doesn't answer the question. Encourage good codding is a good intention but it depends how good is the coder, thus less helpful here. BTW good or bad is not provable everybody has his own opinion. I'd say better to keep a well known standards in the code use a case approach. – Roman C Jun 01 '13 at 08:36
  • @RomanC i have a frame, in this frame **panel p1** and **panel p2** are added (say using border layout, WEST and EAST respectively) on frame. **Panel p1 has two text fields and a button**, first is **radius** to take input for radius of the very first circle and second is **iterations** to take input for how many circles we want to draw one by one by increment the radius by 10. Now when i press the button the drawing should start on Panel p2. Everything is going fine in my code I posted here in stackOverflow but the problem is that Panel p2 is showing just the final stage (all circles at end) – AvmnuSng Jun 01 '13 at 09:10
  • @avmnusng I have changed something in your code to allow it to draw one by one and not at the end. Look at my answer. – Roman C Jun 01 '13 at 14:19
  • +1 I don't understand why downwote had been put on this question. – Roman C Jun 01 '13 at 16:36
  • @RomanC actually someone has seen the word drawing circles and thought that someone is kidding here so my question got a downvote :D – AvmnuSng Jun 01 '13 at 17:03

2 Answers2

2

The problem you're having is far to common.

Swing is a single threaded framework. This means that all UI related interactions must occur within the context of this thread (AKA the Event Dispatching Thread).

The EDT is responsible for, amongst other things, dispatching repaint requests. If any part of your code stops this thread (block I/O, time consuming process, Thread.sleep), the EDT will be unable to process any new events.

Have a read through Concurrency in Swing for more details.

You now face two issues...

  1. You can't block the EDT
  2. You can't update the UI from any thread other then the EDT.

Luckily, there are a number of solutions. The simplest is using a javax.swing.Timer.

This timer triggers it's tick events within the EDT but waits within it's own thread...

enter image description here

import com.sun.org.apache.bcel.internal.generic.LSTORE;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Droplets {

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

    public Droplets() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new DropletPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
    protected static final int MAX_RADIUS = 50;
    protected static final int GROWTH_RATE = 1;

    public class DropletPane extends JPanel {

        private List<Droplet> droplets;

        public DropletPane() {
            droplets = new ArrayList<>(25);
            addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent e) {
                    droplets.add(new Droplet(e.getPoint()));
                }
            });

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    for (Droplet droplet : droplets.toArray(new Droplet[droplets.size()])) {
                        droplet.grow();
                        if (droplet.getRadius() >= MAX_RADIUS) {
                            droplets.remove(droplet);
                        }
                    }
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();
        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Composite comp = g2d.getComposite();
            for (Droplet droplet : droplets) {

                float alpha = 1f - ((float) droplet.getRadius() / (float) MAX_RADIUS);
                g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
                Point p = droplet.getLocation();
                int radius = droplet.getRadius();
                g2d.drawOval(p.x - (radius / 2), p.y - (radius / 2), radius, radius);
                g2d.setComposite(comp);

            }
            g2d.dispose();
        }
    }

    public class Droplet {

        private Point p;
        private int radius;

        public Droplet(Point p) {
            this.p = p;
        }

        public Point getLocation() {
            return p;
        }

        public int getRadius() {
            return radius;
        }

        public void grow() {
            radius += GROWTH_RATE;
            if (radius > MAX_RADIUS) {
                radius = MAX_RADIUS;
            }
        }
    }
}

Extended Example

This example will, when you click the "Start" button, create a random number of droplets at a random interval (between each droplet). You can press start multiple times and it will compound the output.

enter image description here

import static droplets.Droplets.MAX_RADIUS;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Droplets02 {

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

    public Droplets02() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new DropletPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }
    protected static final int MAX_RADIUS = 50;
    protected static final int GROWTH_RATE = 1;

    public interface Pool {

        public void addDroplet(Droplet droplet);

        public Dimension getSize();
    }

    public class DropletPane extends JPanel implements Pool {

        private List<Droplet> droplets;
        private Timer timer;

        public DropletPane() {

            setLayout(new GridBagLayout());
            JButton button = new JButton("Start");
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    new DropletWorker(DropletPane.this).execute();
                }
            });
            add(button);

            droplets = new ArrayList<>(25);
            timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (!droplets.isEmpty()) {
                        for (Droplet droplet : droplets.toArray(new Droplet[droplets.size()])) {
                            droplet.grow();
                            if (droplet.getRadius() >= MAX_RADIUS) {
                                droplets.remove(droplet);
                            }
                        }
                        if (droplets.isEmpty()) {

                            ((Timer) e.getSource()).stop();

                        }
                        repaint();
                    }
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);

        }

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

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Composite comp = g2d.getComposite();
            for (Droplet droplet : droplets) {

                float alpha = 1f - ((float) droplet.getRadius() / (float) MAX_RADIUS);
                g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
                Point p = droplet.getLocation();
                int radius = droplet.getRadius();
                g2d.drawOval(p.x - (radius / 2), p.y - (radius / 2), radius, radius);
                g2d.setComposite(comp);

            }
            g2d.dispose();
        }

        @Override
        public void addDroplet(Droplet droplet) {
            if (!timer.isRunning()) {
                timer.start();
            }
            droplets.add(droplet);
        }
    }

    public class Droplet {

        private Point p;
        private int radius;

        public Droplet(Point p) {
            this.p = p;
        }

        public Point getLocation() {
            return p;
        }

        public int getRadius() {
            return radius;
        }

        public void grow() {
            radius += GROWTH_RATE;
            if (radius > MAX_RADIUS) {
                radius = MAX_RADIUS;
            }
        }
    }

    public class DropletWorker extends SwingWorker<Void, Droplet> {

        private Pool pool;

        public DropletWorker(Pool pool) {
            this.pool = pool;
        }

        public Pool getPool() {
            return pool;
        }

        protected int random(int minRange, int maxRange) {
            return minRange + (int) (Math.round(Math.random() * (maxRange - minRange)));
        }

        @Override
        protected Void doInBackground() throws Exception {

            int dropCount = random(1, 100);
            Pool pool = getPool();
            Dimension size = pool.getSize();
            for (int index = 0; index < dropCount; index++) {
                Thread.sleep(random(10, 1000));
                int x = random(0, size.width);
                int y = random(0, size.height);
                Droplet droplet = new Droplet(new Point(x, y));
                publish(droplet);
            }

            return null;
        }

        @Override
        protected void process(List<Droplet> chunks) {
            for (Droplet droplet : chunks) {
                getPool().addDroplet(droplet);
            }
        }
    }
}

Animation Basics

You need three things to perform animation.

  • A Start state
  • A Target state
  • A delta or time range.

(You also need some way to store the current state)

The start and target states are self explanatory, they describe where you are now and where you want to change to.

The delta would be the amount to apply to the current state at each "time interval" (or tick) until you reach the delta.

Or

The time range would be the amount of time you want to use to move from the start state to the end state.

The delta approach is the simpler mechanism, but isn't nearly as flexible as the time range approach...

Once you have these basic elements set up, you need some kind of "tick" that is triggered at regular intervals which allows you to calculate the current state, which is either a linear movement from the start state to the target state (delta) or a progression of change of over time (time range)

A final, full working rework

Apart from you're attempt to block the EDT within the paint method and failing to following the Initial Thread requirements of Swing, the only other, significant, problem I found was your reliance on the radius and iter values.

Basically, these were never getting set UNLESS you pressed the Enter key...which I wasn't.

This example uses the code that you posted and the ideas from the first example...

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ControlCircles extends JFrame {

    private JButton jbtEnlarge = new JButton("Start");
    private JButton jbtShrink = new JButton("Stop");
    private CirclePanel canvas = new CirclePanel();
    private JTextField f1 = new JTextField(8);
    private JTextField f2 = new JTextField(8);

    public ControlCircles() {
        JPanel panel = new JPanel();
        JPanel jp = new JPanel();
        jp.setPreferredSize(new Dimension(300, 0));
        panel.add(jbtEnlarge);
        panel.add(jbtShrink);

        this.add(jp, BorderLayout.WEST);
        this.add(canvas, BorderLayout.CENTER);
        this.add(panel, BorderLayout.SOUTH);


        jp.setLayout(new FlowLayout(FlowLayout.RIGHT, 50, 30));
        jp.add(new JLabel("Radius"));
        jp.add(f1);
        jp.add(new JLabel("Iteration"));
        jp.add(f2);

        jbtEnlarge.addActionListener(new EnlargeListener());
        jbtShrink.addActionListener(new ShrinkListener());
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new ControlCircles();

                frame.setTitle("ControlCircle");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setSize(800, 600);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    class EnlargeListener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            int radius = Integer.parseInt(f1.getText());
            int iter = Integer.parseInt(f2.getText());
            canvas.start(radius, iter);

        }

    }

    class ShrinkListener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            //canvas.shrink();
        }

    }

    class CirclePanel extends JPanel {

        private int radius;
        private int iterations;

        private int iteration;

        private List<Integer> circles;
        private Timer timer;

        public CirclePanel() {
            circles = new ArrayList<>(25);
            timer= new Timer(100, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    iteration++;
                    if (iteration < iterations) {
                        circles.add(radius);
                        radius += 10;
                    } else {
                        ((Timer)e.getSource()).stop();
                    }
                    repaint();
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int width = getWidth() - 1;
            int height = getHeight()- 1;
            g.drawRect(0, 0, width, height);
            for (Integer radius : circles) {
                int x = (width - radius) / 2;
                int y = (height - radius) / 2;
                g.drawOval(x, y, radius, radius);
            }
        }

        public void start(int radius, int iter) {
            timer.stop();
            circles.clear();
            this.radius = radius;
            iterations = iter;
            iteration = 0;
            System.out.println("radius = " + radius);
            System.out.println("iterations = " + iterations);
            timer.start();
        }
    }
}

This code works based on the description of your problem by correcting the common mistakes with animation in Swing but some of your code didn't quite make sense to me (ie enlarge and shrink) so I focused on the description your provided.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • thanks for having a look at my problem but the problem i am having is that if i am drawing the circles directly on the panel or canvas without using any JTextFields and JButtons to start drawing circles its just working very fine, but if i am using button to start drawing the circles, in this case only the final state is shown to me not all the intermediate states. So basically my problem is like **without any button to start drawing all the intermediate states are shown; program is working fine but adding a button to start the task, in this case just the final state is appearing** – AvmnuSng Jun 01 '13 at 04:37
  • Okay. So when you click the button, start a timer. The basic principal is the same. – MadProgrammer Jun 01 '13 at 04:44
  • that's all is fine but please help me with this issue, suppose i am drawing some 10 circle, the first circle is of radius 10 and radius for each circle is incremented by 15. so if there is no button to start the task firstly i saw circle of radius 10, then of radius 25, then of radius 40 and so on.. and its my desired output, but when i am trying to do this task by pressing a **start** button i am seeing all the 10 circles at once no step by step drawing. So adding a button creating all the problems as i mentioned in my question, so please help me with this problem. I am clear about the EDT. – AvmnuSng Jun 01 '13 at 04:55
  • @MadProgrammer This is an excellent example of not an answer. – Roman C Jun 01 '13 at 07:53
  • @romanc It demonstrates the principles that the OP needs to overcome there problems and reach a solution suitable for their particular needs. Sometimes it's better to teach people to fish, but if you have a better solution I'd be pleased to see it ;) – MadProgrammer Jun 01 '13 at 08:04
  • @MadProgrammer Oh, well I understand you but not in this case. – Roman C Jun 01 '13 at 08:23
  • @MadProgrammer the input issue can be solved by using KeyListener FocusListener but for the real issue was about the painting not getting the input..and you solved that. thanks!!! – AvmnuSng Jun 01 '13 at 17:21
  • @avmnusng The input issue can be solved with a JSpinner and only using the values when you need them. KeyListener is very rarely a good idea. To be frank, I solved your paint issue in the first example – MadProgrammer Jun 01 '13 at 20:50
-1

The idea is to control the drawing animation on the panel used as a canvas with the buttons Start, Stop and I added Continue and Reset additional controls to better explain the idea. These buttons control the animation thread execution thus drawing circles on the drawing surface. the drawing surface I separated as inner class that has only function to draw whatever performed. Another idea that the approach is taken to draw the circles one by one incrementally until it finishes drawing thus used incremental painting.

I have used the code from the above and changed it a little to support my ideas. If you need more and usually better examples look at this article.

The code is below, I didn't polish it enough to have a production wise look and feel but for demonstration purpose only.

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

public class ControlCircle extends JFrame implements Runnable {
  private JButton jbtStart = new JButton("Start");
  private JButton jbtStop = new JButton("Stop");
  private JButton jbtContinue = new JButton("Continue");
  private JButton jbtReset = new JButton("Reset");
  private CirclePanel canvas = new CirclePanel();

  private JTextField f1;
  private int radius = 0;

  private JTextField f2;
  private int iter;

  protected boolean  incrementalPainting;

  /**
   * Flag indicates that a thread is suspended
   */
  private boolean suspended = false;


  /**An instance of the class Thread.*/
  private Thread thread = null;

  public ControlCircle() {
    JPanel panel = new JPanel();
    JPanel jp = new JPanel();
    jp.setPreferredSize(new Dimension(300, 0));
    panel.add(jbtStart);
    panel.add(jbtStop);
    panel.add(jbtContinue);
    panel.add(jbtReset);

    this.add(jp, BorderLayout.WEST);
    this.add(canvas, BorderLayout.CENTER);
    this.add(panel, BorderLayout.SOUTH);

    f1 = new JTextField(8);
    f2 = new JTextField(8);

    jp.setLayout(new FlowLayout(FlowLayout.RIGHT, 50, 30));
    jp.add(new JLabel("Radius"));
    jp.add(f1);
    jp.add(new JLabel("Iteration"));
    jp.add(f2);


    jbtStart.addActionListener(new StartListener());
    jbtStop.addActionListener(new StopListener());
    jbtContinue.addActionListener(new ContinueListener());
    jbtReset.addActionListener(new ResetListener());
  }

  public static void main(String[] args) {
    JFrame frame = new ControlCircle();

    frame.setTitle("ControlCircle");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(800, 600);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }

  class StartListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (thread == null) {
        repaint();
        startThread();
      }
    }
  }

  class StopListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (thread != null){
        mySuspend();
      }
    }
  }

  class ContinueListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      myResume();
    }
  }

  class ResetListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      if (thread != null) {
        stopThread();
      }
      repaint();
    }

  }

  /**
   * my Suspend
   */
  private void mySuspend() {
    System.out.println("mySyspend()");
    suspended = true;
  }

  /**
   * my Resume
   */
  private synchronized void myResume(){
    System.out.println("myResume()");
    suspended = false;
    notifyAll();
  }

  public void run(){
    System.out.println("run() - started");

    Thread me = Thread.currentThread();
    while (thread == me) {
      radius = Integer.parseInt(f1.getText());
      iter = Integer.parseInt(f2.getText());
      for (int i = 0; i < iter; i++) {
        if (thread == null) return;
        incrementalPainting = true;
        myRepaint();
        try {
          Thread.sleep(1000);
        }
        catch(InterruptedException e){}
        radius += 10;
      }
      if(thread != null) thread = null; // exiting while
    }
    System.out.println("run() - exiting");
  }

  /**
   * start Thread
   */
  private void startThread(){
    System.out.println("startThread()");
    if(thread == null){
      thread = new Thread(this);
      thread.start();
    }
  }

  /**
   *  stop Thread
   */
  private synchronized void stopThread() {
    System.out.println("stopThread()");
    thread = null; // exiting from while
    if (suspended) {
      suspended = false;
      notify();
    }
  }

  /**
   * This is called from the run method to invoke painting.
   */
  private void myRepaint() {
    System.out.println("myRepaint()");
    incrementalPainting = true;
    repaint();
    synchronized (this) {
      while (incrementalPainting) {
        System.out.println("wait while incremental painting");
        try {
          wait();
        } catch (InterruptedException e) {
          System.out.println("interrupted");
        }
      }
    }

    suspend();
  }
  /**
   * This method should place somewhere when run() has started. Perfectly
   * when repaint() performed.
   */
  private void suspend(){
    System.out.println("suspend()");
    synchronized (this) {
      while (suspended) {
        System.out.println("wait while suspended");
        try {
          wait();
        } catch (InterruptedException e) {
          System.out.println("interrupted");
        }
      }
    }

  }

  public synchronized void myPaint(Graphics g) {
    if (g == null){
      if (incrementalPainting){
        incrementalPainting = false;
        notifyAll();
      }
      return;
    }
    if (incrementalPainting){
      myDraw(g);
      incrementalPainting = false;
      notifyAll();

    }
    else {
      myDraw(g);
    }
  }

  public void myDraw(Graphics g){
    g.drawOval(getWidth() / 2 - radius, getHeight() / 2 - radius, 2 * radius, 2 * radius);
  }

  protected final class CirclePanel extends JPanel {
    //Offscreen buffer of this canvas
    private BufferedImage backBuffer = null;

    public void paintComponent (Graphics g) {
      System.out.println("incrementalPainting="+incrementalPainting);
      // First paint background
      super.paintComponent(g);
      Dimension d = this.getSize();
      if (! incrementalPainting)
        backBuffer = (BufferedImage) this.createImage(d.width, d.height);
      Graphics2D g2 = backBuffer.createGraphics();
      if (! incrementalPainting){
        g2.setColor(Color.WHITE);
        g2.fillRect(0,0, d.width, d.height);
      }
      myPaint(g2);
      g.drawImage(backBuffer, 0, 0, this);

    }

  }

}
Roman C
  • 49,761
  • 33
  • 66
  • 176