javamultithreadingswingpaintevent-listener

The code for displaying circles with varying radius on a panel inside a frame


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;
    }
  }
}

Solution

  • 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 Painting in AWT and Swing.

    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);
    
        }
    
      }
    
    }