2

When I try to draw an oval in JFrame, the paint() method doesn't work, but when I change JFrame to Frame it's fine. Can you please explain this problem?

import java.awt.Graphics;
import java.awt.Window;

import javax.swing.JFrame;

public class Oval extends JFrame
{
    public Oval()
    {
    //  this.setLocationRelativeTo(null);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.setSize(400, 400);
    }

    public void paint(Graphics g)
    {
        g.drawOval(40, 40, 40, 40);
    }

    public static void main(String []args)
    {
        new Oval();
    }
}
Anto
  • 4,265
  • 14
  • 63
  • 113
john
  • 95
  • 6

2 Answers2

3
  1. Don't override paint(). Instead add a JPanel to the JFrame and override paintComponent(g)

  2. Call super method. Even if you override paint() call super.paint() (Of course it's better to use paintComponnet())

StanislavL
  • 56,971
  • 9
  • 68
  • 98
2

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.
Aman Agnihotri
  • 2,973
  • 1
  • 18
  • 22
  • 1
    a coupe of comments: a) unrelated: [don't use setXXSize, ever](http://stackoverflow.com/a/7229519/203657) - instead, override the getters to return sizing hints that are more appropriate than those calculated by default b) the implementation of paintComponent is violation the panel's contract to fill all its area with a fully opaque color – kleopatra Mar 25 '14 at 13:27
  • @kleopatra: I've updated my answer based on your comment, and hopefully they satisfy both your mentioned coding practices. Thanks. – Aman Agnihotri Mar 25 '14 at 13:39
  • +1, thanks! Though (nitpicking a bit :-): strictly speaking, your getXXSize implementations violate the method contract (in not returning a potentially previously set xx) – kleopatra Mar 25 '14 at 15:39
  • @kleopatra: Updated the answer now. I don't think that any nitpicking will work on it now. :D +1 for your answer in this [don't setXXSize, ever](http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi/7229519#7229519) link and thanks. – Aman Agnihotri Mar 25 '14 at 17:19
  • this.setVisible(true); this.setSize(400, 400); its work ok when i use frame instead of Jframe?? – john Mar 26 '14 at 08:26
  • @user3429135: Well they'll work even with JFrame. Did you try the program snippet that I have written above? – Aman Agnihotri Mar 26 '14 at 10:05