Search code examples
javaswingframepaintcomponentcustom-painting

How can I add more than one paintComponent() to a frame?


So this is my main class:

package testgame;

import java.awt.EventQueue;
import javax.swing.JFrame;

public class Game extends JFrame {

public static JFrame frame = new JFrame("Just a test!");

public static void LoadUI() {
    frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
    frame.setSize(550, 500);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true); }

public static void main(String[] args) {
    LoadUI();
    frame.add(new Circles());
    }
}

And this is the class that handles what I want to paint:

package testgame;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Circles extends JPanel {

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    drawBubbles(g); }

public void drawBubbles(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    RenderingHints rh
            = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    rh.put(RenderingHints.KEY_RENDERING, 
            RenderingHints.VALUE_RENDER_QUALITY);
    g2d.setRenderingHints(rh);
    int x, y, size;
    x = (int) (Math.random() * 500) + 15;
    y = (int) (Math.random() * 450) + 15;
    size = (int) (Math.random() * 50) + 25;
    g2d.setColor(Color.GREEN);
    g2d.drawOval(x, y, size, size);
    g2d.fillOval(x, y, size, size); }
}

If I add another

 frame.add(new Circles());

Nothing happens. I think it has to do with the layout of the frame, but the coordinates of the bubbles are random so I'm not sure how to work with this.


Solution

  • In this case I'm using a fixed-size array of 5, you may change it to a bigger fixed-size array or an ArrayList, as shown in this answer

    For your particular case I would create a Circle class that may contain the data for each circle, being the coords and the size

    Then create a CirclePane class that would paint all the Circles in a single paintComponent() method.

    And finally, the Main class that would have a JFrame that may contain the CirclePane added to it.

    With the above tips in mind, you could end up with something like this:

    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Point;
    import java.awt.geom.Ellipse2D;
    
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class CircleDrawer {
        private JFrame frame;
        
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new CircleDrawer()::createAndShowGui); //We place our program on the EDT
        }
        
        private void createAndShowGui() {
            frame = new JFrame(getClass().getSimpleName());
            
            CirclePane circle = new CirclePane(5); //We want to create 5 circles, we may want to add more so we change it to 10, or whatever number we want
            
            frame.add(circle);
            
            frame.pack();
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        }
        
        //Data class
        class Circle {
            private Point coords;
            private int size;
            
            public Circle(Point coords, int size) {
                this.coords = coords;
                this.size = size;
            }
            
            public Point getCoords() {
                return coords;
            }
            public void setCoords(Point coords) {
                this.coords = coords;
            }
            public int getSize() {
                return size;
            }
            public void setSize(int size) {
                this.size = size;
            }
        }
        
        //The drawing class
        @SuppressWarnings("serial")
        class CirclePane extends JPanel {
            private int numberOfCircles;
            private Circle[] circles;
    
            public CirclePane(int numberOfCircles) {
                this.numberOfCircles = numberOfCircles;
                
                circles = new Circle[numberOfCircles];
                
                for (int i = 0; i < numberOfCircles; i++) {
                    Point coords = new Point((int) (Math.random() * 500) + 15, (int) (Math.random() * 450) + 15); //We generate random coords
                    int size = (int) (Math.random() * 50) + 25; //And random sizes
                    circles[i] = new Circle(coords, size); //Finally we create a new Circle with these properties
                }
            }
            
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D) g;
                
                for (int i = 0; i < numberOfCircles; i++) {
                    g2d.draw(new Ellipse2D.Double(circles[i].getCoords().getX(), circles[i].getCoords().getY(), circles[i].getSize(), circles[i].getSize())); //We iterate over each circle in the array and paint it according to its coords and sizes
                }
            }
            
            @Override
            public Dimension getPreferredSize() { //Never call JFrame.setSize(), instead override this method and call JFrame.pack()
                return new Dimension(500, 500);
            }
        }
    }
    

    Which produces a similar output to this:

    enter image description here

    I hope this helps you to get a better idea, read about the MVC pattern as I made use of it for this answer.


    Note:

    In this answer I used the Shapes API, according to the recommendation of @MadProgrammer in this other answer. I used it in the g2d.draw(...) line.

    For a deeper understanding in how custom painting works in Swing, check Oracle's Lesson: Performing Custom Painting and Painting in AWT and Swing tutorials.