Vexx
Vexx

Reputation: 9

Repaint() relocating buttons

I'm writing a Java program that will draw a circle or rectangle depending on the button pressed. While it does draw the given shape, upon drawing it creates new buttons at the top left((0,0) most likely) of the window. Am I breaking some rule of paint()/repaint()?

Here's my class:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JPanel;
public class Components extends JPanel implements ActionListener
{
  String em="Nothing";
  public Components()
{
    JButton c=new JButton("Rectangle");
    JButton b=new JButton("Circle");
    b.addActionListener(this);
    c.addActionListener(this);
    add(c, 0);
    add(b, 1);
    setBackground(Color.WHITE);
    setVisible(true);
}

public void actionPerformed(ActionEvent e)
{
    if(e.getActionCommand().equals("Rectangle"))
    {
        em="Rectangle";
        repaint();
    }
    else
    {
        em="Circle";
        repaint();
    }
}

public void paint(Graphics g)
{
    if(em.equals("Rectangle"))
    {
        g.drawRect(50, 50, 50, 50);
    }
    else if(em.equals("Circle"))
    {
        g.drawOval(50, 50, 50, 50);
    }

}

}

As well as the less-important JFrame class:

import java.awt.Graphics;

import javax.swing.JFrame;
public class Wndw extends JFrame
{
    public Wndw()
    {
        setTitle("Hello");
        setSize(400, 400);
        setResizable(false);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        add(new Components());
    }

public static void main (String [] args)
{
    new Wndw();
}
}

Upvotes: 0

Views: 378

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347332

It's got (very little) to do with repaint and everything to do with your violation of the painting routines

Start by having a look at Performing Custom Painting and Painting in AWT and Swing for more details into how painting works.

paint delegates it's responsibilities, by calling paintComponent, paintBorder and paintChildren. When you override paint and fail to call it's super method, you are breaking this responsibility and you end up with issues like you have now.

The Graphics context is a shared resource among Swing components. Every component which is painted during a paint cycle gets the same Graphics. This means, one of the first things that should be done, is the Graphics context should be prepared for the component to paint on it. This is typically done by the paintComponent method, which you are no longer calling

The first thing to do to is, stop overriding paint and instead, which is generally recommended, override paintComponent, it limits your overall responsibility and reduces the risk of introducing further issues.

Next, call super.paintComponent before you do any custom painting...

public class Components extends JPanel implements ActionListener
{
    //....
    @Overrride
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if(em.equals("Rectangle"))
        {
            g.drawRect(50, 50, 50, 50);
        }
        else if(em.equals("Circle"))
        {
            g.drawOval(50, 50, 50, 50);
        }    
    }

But when I do this, what was previously paint is erased

Yes, that's how painting works. It's destructive. When ever a paint pass is executed, you are expected to repaint the entire state of the component as it is at that point in time.

If you wish to preserve what was previously painted, you have two basic choices.

  1. Draw everything to a backing buffer (image) and paint that
  2. Store the "commands" you need to redraw the context from scratch in some kind of List

There are plenty of examples of both

Upvotes: 2

Related Questions