How can you draw rectangles on a JPanel?

Tags: , , , ,



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.

Answer

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



Source: stackoverflow