Search code examples
javagraphicsjpanelpaintellipse

Drawing a shape on a JPanel which already uses the paint method


Let's say I have two classes, the first extends JPanel and using Graphics draws a playing board on it. The second makes a JFrame and adds the panel to it.

You can imagine the frame looking something like this: sample JFrame

I now want to add an ellipse to a specific rectangle on click. I understand that I would be using a two-dimensional array in order to get the position I wanted, but I don't understand how the ellipse itself would be drawn on to the existing panel since I used the paint(Graphics g) to draw the playing board.

Here is the code for drawing the board itself if you need it:

class MyBoard extends JPanel {
    private static int height = 6;
    private static int width = 7;
    private static int squareSize = 100;
    private int board[][] = new int[height][width];

    public void paint(Graphics g) {
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                g.drawRect(j * squareSize, i * squareSize, squareSize, squareSize);
            }
        }
    }
}

Thanks!


Solution

  • First two things that you should remember: Never override paint but paintComponent and call super.paintComponent in there so that borders and everything works as expected. Regarding why this is the case, reference this question: Difference between paint() and paintcomponent()?


    Now to answer your question. Assuming you have an existing logic to determine in which square you want to draw your Ellipse (let's assume you have two Integers elX and elY that are the column and row of your square) you can simply go and draw it after you have finished drawing the board itself.

    Imagine sample code like this:

    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
    
        // Draw the board
        for (int i = 0; i < height; i++)
        {
            for (int j = 0; j < width; j++)
            {
                g.drawRect(j * squareSize, i * squareSize, squareSize, squareSize);
            }
        }
    
        // Draw the ellipse at the correct location using half the size of a normal square.
        g.drawOval(elX * squareSize + squareSize / 4, elY * squareSize + squareSize / 4, squareSize / 2 , squareSize / 2);
    }
    

    Now the final part of how to go about actually determining where to draw your ellipse. A simple solution would be to add a MouseListener to your panel. And then in the mouseClicked method you calculate where you actually did click.

    Could look like this:

    this.addMouseListener(new MouseListener()
    {
    
        @Override
        public void mouseClicked(MouseEvent e)
        {
            int column = e.getX() / squareSize;
            int row = e.getY() / squareSize;
    
            board[column][row] = 1;
        }
    
        [...] // add the other methods to override
    
    }
    

    Then you slightly adapt your paintComponent method with something like this:

    for (int column = 0; column < width; ++column)
    {
        for (int row = 0; row < height; ++row)
        {
            if (board[column][row] == 1)
            {
                g.drawOval(column * squareSize + squareSize / 4, row * squareSize + squareSize / 4, squareSize / 2, squareSize / 2);
            }
        }
    }
    

    and now you draw an ellipse everywhere you click. You could also check if the clicked square already has 1 set as a value and reset it to 0 to have some toggle mechanism or increment it and draw different things based on the integer value... it's all up to you :)