Search code examples
javaswingjframejpanelborder-layout

How do you add multiple panels to a JFrame using a loop in Java?


I have a class called DrawRectangles that takes an array of integers.

What I am trying to do is go through the numbers in the array and for each one, create a new panel using the number in the array as the panel's width and X position.

Let's say I pass in [2, 4, 6, 8]. I want to create a new panel to add to the JFrame with each of these numbers.

So the first panel should start at position 2 and have a width of 2. I also have a random color generator which is supposed to create a new color for each panel. Here is what I have:

public class DrawRectangles {
    JFrame frame;
    DrawPanel panel;
    Random randomGenerator = new Random();
    int red = randomGenerator.nextInt(255);
    int green = randomGenerator.nextInt(255);
    int blue = randomGenerator.nextInt(255);
    Color randomColor;
    int[] newWidth;

    DrawRectangles(int[] width){
        this.newWidth = width;
    }

    public void setUpFrame(){
        frame = new JFrame();
        frame.setSize(500,100);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        for(int x = 0; x < newWidth.length; x++){
            panel = new DrawPanel(newWidth[x]);
            frame.add(panel);
        }
    }
    class DrawPanel extends JPanel{
        int newWidth;
        DrawPanel(int width){
            this.newWidth = width;
            System.out.println(newWidth);
        }
        public void paint(Graphics g) {
           super.paint(g);
           randomColor = new Color(red,green,blue);
           g.setColor(randomColor);
           g.fillRect(newWidth, 10, newWidth, 30);
        }
    }
}

Solution

  • You have a number of issues...

    • Frame uses a BorderLayout by default. This will only allow a single component to occupy any one of the pre-defined position available to it. This means that only the last pane will be visible.
    • You don't control the paint process. This means that every time randomColor changes values, all the components referencing it will also be repainted with it on the next paint cycle.
    • You should be using paintComponent instead of paint

    This is a basic example using a single DrawRectangle component to paint all the rectangles.

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.Rectangle;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class DrawRectangles {
    
        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) {
                    }
    
                    DrawRectangles dr = new DrawRectangles(new int[]{2, 4, 6, 8});
                    dr.setUpFrame();
    
                }
            });
        }
    
        JFrame frame;
        DrawPanel panel;
        Random randomGenerator = new Random();
        int[] newWidth;
    
        DrawRectangles(int[] width) {
            this.newWidth = width;
        }
    
        public void setUpFrame() {
            frame = new JFrame();
            frame.setSize(500, 100);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            DrawPanel panel = new DrawPanel();
            for (int x = 0; x < newWidth.length; x++) {
                int red = randomGenerator.nextInt(255);
                int green = randomGenerator.nextInt(255);
                int blue = randomGenerator.nextInt(255);
                panel.addRectangle(new Color(red, green, blue), newWidth[x]);
            }
            frame.setSize(200, 200);
            frame.add(panel);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        }
    
        public class MyRectangle {
    
            private Color color;
            private Rectangle rectangle;
    
            public MyRectangle(Color color, int width) {
                this.color = color;
                rectangle = new Rectangle(width, 10, width, 30);
            }
    
            public Color getColor() {
                return color;
            }
    
            public Rectangle getRectangle() {
                return rectangle;
            }
    
            public void draw(Graphics2D g2d) {
                g2d.setColor(color);
                g2d.fill(rectangle);
            }
        }
    
        public class DrawPanel extends JPanel {
    
            private List<MyRectangle> rectangles;
    
            public DrawPanel() {
                rectangles = new ArrayList<>();
            }
    
            public void addRectangle(Color color, int width) {
                rectangles.add(new MyRectangle(color, width));
            }
    
            public void paint(Graphics g) {
                super.paint(g);
                Graphics2D g2d = (Graphics2D) g.create();
                for (MyRectangle rect : rectangles) {
                    rect.draw(g2d);
                }
                g2d.dispose();
            }
        }
    }
    

    You could use a different layout manager, but then your DrawRectangle component must also return a preferredSize so that the layout manager doesn't layout out the component with a size of 0x0