Subscribe to Dogstopper's Code Mania        RSS Feed
***** 3 Votes

paint() vs. paintComponent(): A Resolution

Icon 7 Comments
Well, time to wrap up this "feud" (edit:not a feud, just a stance) that pbl and I have had regarding paint() vs. paintComponent(). I have been a little "childish" about this whole affair, but I have decided to redeem myself. This article is organized to show my old argument, pbl's argument, and then the conclusion that I made based on testing. I hope you find it thoroughly educational.

My Argument


My argument (up until the discovery that I made) was that paintComponent() was better hands-down, and being my cocky self, I went to prove it. Here is a portion of the paper that I wrote:

Quote

One should not override paint() on Swing components. Actually, after some research (citations at end), I have established that actually in a Swing program, one should override the paintComponent() method instead. That is due to the fact that paint() does many things behind the scenes including the calls of multiple other methods.

These calls include:
paintComponent()
paintBorder()
paintChildren()

I am going to quote Sun here:

Quote

The rules that apply to AWT's lightweight components also apply to Swing components -- for instance, paint() gets called when it's time to render -- except that Swing further factors the paint() call into three separate methods, which are invoked in the following order:

protected void paintComponent(Graphics g)
protected void paintBorder(Graphics g)
protected void paintChildren(Graphics g)

Swing programs should override paintComponent() instead of overriding paint(). Although the API allows it, there is generally no reason to override paintBorder() or paintComponents() (and if you do, make sure you know what you're doing!). This factoring makes it easier for programs to override only the portion of the painting which they need to extend. For example, this solves the AWT problem mentioned previously where a failure to invoke super.paint() prevented any lightweight children from appearing.


Citations:
http://java.sun.com/...ting/#callbacks
http://www.apl.jhu.e...ial-JPanel.html
http://java.sun.com/...a.awt.Graphics)
http://java.sun.com/...a.awt.Graphics)
http://www.d.umn.edu...g/graphics.html
http://pirlwww.lpl.a...g/overview.html


And this is correct, pbl even proved that it is far more efficient to use paintComponent(). So, so far, paintComponent() is looking like it is winning because it works and Sun backs it up.

Pbl's Argument


I was confronted with a post (http://www.dreamincode.net/forums/index.php?showtopic=161233) the other day in which paintComponent() did not work...as expected. pbl said that it was because paintComponent() didn't work and paint() should be used, like he has for 20 years.

Here is a version of the code I was confronted with...Shortened to get the picture:
public class TileSetPanel extends JPanel implements ActionListener
{

        private int mouse_X_Location = 300;
        private int mouse_Y_Location= 150;
        private int tile_Size = 32;

        public TileSetPanel()
        {
            this.setLayout(null);

            JScrollPane jsp = new JScrollPane();
            jsp.setBounds(250,0,276,498);
       

            this.add(jsp);

            Timer time = new Timer(1000, this);
            time.start();
        }

        public void actionPerformed(ActionEvent e) {
            repaint();
        }

        public void paint(Graphics g)
        {
            	super.paint(g);
                // Was originally yellow, but changed
                // to make easier to see
                g.setColor(Color.magenta);
                g.drawRect(mouse_X_Location, mouse_Y_Location, tile_Size, tile_Size);
        }

}



Ok, so paint() works as we expected, but if you change the calls to paintComponent() it doesn't...right?
        public void paintComponent(Graphics g)
        {
            	super.paintComponent(g);
                g.setColor(Color.magenta);
                g.drawRect(mouse_X_Location, mouse_Y_Location, tile_Size, tile_Size);
        }



This doesn't seem to work, but it actually does. If you change the coordinate of the mouse_x_Location and mouse_Y_Location to beyond the ScrollPane, you will infact see the rectangle being painted. In the post, the OP had expected it to do the same thing as paint(), which is not the case. So, pbl is absolutely right here.

The Conclusion


So, with these results, I ran a little bit more testing and found that with different components (JPanels, JScrollPanes, JLabels), paint() paints on top of them, paintComponent() does not. As I said in my paper, paint calls three methods, one of which is paintChildren(). This shows that paint() has the "authority" to paint on top of children components (components added to it), whereas paintComponent() can only paint itself, so if something is on top, then you don't see it.

So, which to use? I guess it depends on the application. I games, I suggest using only paintComponent() as the last thing that you need are "ghost images" floating around everywhere, but if doing something like the user did in his post, then paint() is better because you want that functionality. Sun says it works, but isn't advised, but the conclusion that I have come to is that if necessary, one should use it, otherwise paintComponent() is faster and safer to use.

Hope this has proved educational. It was to me.
:)

~ Stephen Schwahn

7 Comments On This Entry

Page 1 of 1

pbl 

14 March 2010 - 08:51 PM
Surely not 20 years
Java was not there 20 yeras ago
But really good resume
I'll try your way first because it is a lot cleaner, and if it does not work, go back to my way.
0

pbl 

14 March 2010 - 08:52 PM
Any how really good show
0

toshiro 

15 March 2010 - 08:48 PM
i've ran into this problem countless times in my ill-conceived attempts at a clean and functional Java GUI - i feel so enlightened.
0

Mercurial 

26 August 2010 - 08:14 AM
Very educational. Good job and thanks!
0

Mercurial 

01 September 2010 - 04:11 PM
I don't get one thing. Lets say I got something like this
Class Something extends JPanel{
JPanel firstPanel;
Jpanel secondPanel;
//...
this.add(firstPanel);
//...


Now I override the paint method.
public void paint(Graphics g){
super.paint(g);
//draw some crap
}

super.paint(g) does what it does (borders, component, children). So, drawing code that comes afterwards should draw... to the panel(Something) which has panel(s) over it. I mean, I'm not drawing to the Panel itself then ? Since it has children and drawing directly on it wouldn't be visible (just like Dog explained, the rectangle thing... If I'd switch super.paint() and drawCrapCode, the drawing wouldn't be visible since there'd be it's children over it). What am I drawing to then ? Not to a specific component, just to the "screen" ?
0

Dogstopper 

01 September 2010 - 05:23 PM
Actually, it's the other way around. paint() paints all of its children first, THEN does the custom stuff, thus being on top. However, paintComponent() worries only about itself...and because of that, child components make it not visible. Take a look at this program and experiment with it some:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main extends JPanel {
	
	public Main() {
		setLayout(new GridLayout(2,1));
		
		JPanel pan1 = new JPanel();
		pan1.setBackground(Color.black);
		
		JPanel pan2 = new JPanel();
		pan2.setBackground(Color.red);
		
		add(pan1);
		add(pan2);
	}
	
	public void paint(Graphics g) {
		super.paint(g);
		g.fillRect(0, 0, 20, 20);
	}
	
	/*public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.fillRect(0, 0, 20, 20);
	}*/

    public static void main(String args[])
	{
	 	Main main = new Main();
	 	
	 	JFrame frame = new JFrame();
	 	frame.setSize(60,50);
	 	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	 	frame.add(main);
	 	frame.setVisible(true);
	}
}

0

Mercurial 

01 September 2010 - 11:23 PM
Yeah I know that, I've already experimented as soon as I saw the post a week ago :smile2:. The thing I wrote in parentheses talks about what you just said - I realized what was it all about, almost, when I switched
super.paint(g) with drawStuffCode... the order changed. What seems odd to me is that the drawing you do on the screen isn't necessairly affected by order of the panels. I can draw stuff on the most bottom JPanel and it could still appear on top (if I paint his children first).
0
Page 1 of 1

December 2018

S M T W T F S
      1
2345678
91011121314 15
16171819202122
23242526272829
3031     

Recent Entries

Search My Blog

Recent Comments

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)