Well you shouldn't override the paint()
method, instead add a JPanel
to your JFrame
and override the JPanel's
paintComponent()
method and draw your stuff on the JPanel instead.
Here's a demonstration:
import javax.swing.*;
import java.awt.*;
public class Oval extends JFrame
{
public Oval()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new GridBagLayout());
OvalPanel ovalPanel = new OvalPanel(100); // SIZE of oval is 100
ovalPanel.setPreferredSize(new Dimension(400, 400));
add(ovalPanel);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
new Oval();
}
});
}
}
class OvalPanel extends JPanel
{
private final int SIZE;
public OvalPanel(final int size)
{
this.SIZE = size;
}
@Override
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Call the super class's paintComponent so that it does its appropriate rendering first.
g.drawOval((getWidth() - SIZE) >> 1, (getHeight() - SIZE) >> 1, SIZE, SIZE); // Center the oval
}
}
Also if you notice, I used SwingUtlities.invokeLater()
method and there I called the Oval constructor. That I did because Swing is thread unsafe and so you should call any Swing related methods in the Event Dispatcher Thread which is responsible for handling any Swing related stuff, and that's what the invokeLater()
method does.
Note: if you're using JDK 8, you can write the SwingUtilities.invokeLater()
statement as:
SwingUtilities.invokeLater(Oval::new); // That's JDK 8 Method reference feature.
You might wonder as to why you shouldn't do stuff directly in the paint()
method. The reason has to do with the way paint()
method is called. You see, the RepaintManager
is responsible for handling the painting of all the Swing based components, and when the paint()
method is called, it in turn calls the paintComponent()
, paintChildren()
and paintBorder()
methods. paintComponent()
is the best place to handle any component based rendering and that's why its the suggestion. Though, even the following code works:
import javax.swing.*;
import java.awt.*;
public class Oval extends JFrame
{
public Oval()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 400);
setLocationRelativeTo(null);
setVisible(true);
}
public void paint(Graphics g)
{
super.paint(g); // Calling the super class's paint method first.
// so that it handles its rendering.
g.drawOval(40, 40, 40, 40);
}
public static void main(String[] args)
{
new Oval();
}
}
Even the order in which methods are called is important in Swing. Like:
setSize(400, 400); // Setting the size of JFrame first.
setLocationRelativeTo(null); // Locating the JFrame based on its size then.
setVisible(true); // Finally its made visible
All the above work as expected. But the following won't display the results properly:
setVisible(true); // JFrame is already made visible
setLocationRelativeTo(null); // This first locates the JFrame based on its (0, 0) size
setSize(400, 400); // Then its size is increased. Not as expected as the JFrame appears shifted from center.