Search code examples
javaswingjpanelrectanglesjslider

How can you draw rectangles on a JPanel?


I'm trying to create a program that generates rectangles using a slider. The user should be able to move the slider and rectangles should appear on the JPanel above the slider with random positions. I have tried running the program but I'm still unable to display anything, I move the slider but nothing appears on screen. I have tried coding this program using examples from the book but im stuck when it comes to actually drawing the rectangles. I am able to create and change the layouts as well as to display the slider and a few labels but i'm unable to get the rectangles to appear on the JPanel. Here is my code:

import java.util.*;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JLabel;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Dimension;
import java.lang.Object;

public class RectangleFrame extends JFrame
{
    private static final int FRAME_WIDTH = 600;
    private static final int FRAME_HEIGHT = 500;
    
    private JPanel RectanglePanel;
    private JSlider RectangleSlider;
    
    int x = 0;
    int y = 0;
    
    /**Creates a new Rectangle frame objects.
     Creates control panel and sets the size.*/
    public RectangleFrame()
    {
        RectanglePanel = new JPanel();
        RectanglePanel.setPreferredSize(new Dimension(600, 300));
        
        add(RectanglePanel, BorderLayout.CENTER);
        createControlPanel();
        setRectangles();
        setSize(FRAME_WIDTH,FRAME_HEIGHT);
    }
    
    
    class RectangleListener  implements ChangeListener
    {
        public void stateChanged(ChangeEvent event)
        {
            setRectangles();
        }
    }
    
    
    /**Creates the Panel where the user can slide and generate rectangles. */
    public void createControlPanel()
    {
        ChangeListener listener = new RectangleListener();
        
        RectangleSlider = new JSlider(JSlider.HORIZONTAL, 1, 20, 1);
        RectangleSlider.addChangeListener(listener);
        
        JPanel controlPanel = new JPanel();
        controlPanel.setLayout(new GridLayout(1,3));
        
        controlPanel.add(new JLabel("Fewer"));
        controlPanel.add(RectangleSlider);
        controlPanel.add(new JLabel("More"));
        
        add(controlPanel, BorderLayout.SOUTH);
        
    }
    
    public void setRectangles()
    {
        
        
        Random random = new Random();

        
        //Read slider value
        int numberOfRectangles = RectangleSlider.getValue();
        
        for(int i = 0; i < numberOfRectangles; i++)
        {
            x = random.nextInt(540);
            y = random.nextInt(290);
            
            
        }

    }
    
    
    protected void paintComponent(Graphics g) 
    {
        super.paintComponents(g);
        
        g.setColor(Color.BLACK);
        g.drawRect(x, y, 60, 10);
    }  
}

I have tried drawing one simple rectangle but not even that appears on the JPanel, let alone multiple. Any resources to further look into this would also bee highly appreciated.


Solution

  • You cannot draw directly on top of a JFrame. For "custom painting" (as this is called) you need to create a subclass of a component that overrides the paintComponent method. For example a JPanel:

    class RectanglePanel extends JPanel {
    
        int numberOfRectangles = 2;
    
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
    
            g.setColor(Color.BLACK);
            Random random = new Random(42);
    
            for (int i = 0; i < numberOfRectangles; i++) {
                x = random.nextInt(540);
                y = random.nextInt(290);
                g.drawRect(x, y, 60, 10);
            }
    
        }
    }
    

    You use this custom component the same way you would use a JPanel:

        rectanglePanel = new RectanglePanel();
        rectanglePanel.setPreferredSize(new Dimension(600, 300));
        add(rectanglePanel, BorderLayout.CENTER);
    

    To draw fewer or more rectangles, the simplest thing you can do is change the numberOfRectangles of the custom component and then ask it to repaint itself.

    int numberOfRectangles = RectangleSlider.getValue();
    rectanglePanel.numberOfRectangles = numberOfRectangles;
    rectanglePanel.repaint();
    

    Note that this is a simplified demo and does not demonstrate "good practices" such as encapsulation. A more advanced technique is to using the model-view-controller pattern, and create a "model" object that encapsulates what is supposed to be drawn. You can read more about how that works for example here: The MVC pattern and Swing