Understanding Graphics Context and Painting
A custom drawing surface is built by defining a subclass of JPanel and providing a paintComponent(Graphics g) method that describes the picture.
Testing a Belief About paintComponent()
Suppose you have written a custom panel whose paintComponent() method draws a shape. Your program creates the panel object and puts it to use. Many learners assume that because they wrote paintComponent(), they are in charge of calling it, in the same way they call a helper method that computes a total. This article tests that belief from several angles and corrects it. The method is real and important, but its role in a program is different from the role of the methods you call on purpose.
What do you think happens?
Your program creates a custom panel and adds it to a window. Which part of the program is responsible for invoking paintComponent() when the panel appears on the screen?
Reveal answer
The system calls paintComponent() when the component needs to be drawn. Your code writes the method, but it does not call it directly.
The source states that the system calls paintComponent() when the component needs to be drawn, so the programmer writes the method but does not call it directly. The code inside the method runs at the moment the system needs the panel drawn, not at the point in your program where you created the object.
Why Build a Custom Drawing Surface from JPanel
Standard component classes cover many common needs, but some content is not provided by the predefined classes. For that case you must define your own component class and give it a method that draws the component. The recommended route is to create a subclass of JPanel whenever you need a drawing surface of this kind.
JPanel is a convenient base because, like any JComponent, it draws its content in the paintComponent() method. Your subclass inherits that drawing structure, so your job reduces to supplying the one method that describes the picture. In other words, the panel already knows how to be drawn; you supply the description of what it should show.
A custom drawing surface is a subclass of JPanel that provides a paintComponent(Graphics g) method describing the picture the panel should show.
The source gives the same pattern in its HelloWorldDisplay class, defined in HelloWorldGUI2.java, which is a JPanel subclass used as a drawing surface. Once such a class is defined, you create an object of that class and use it in your program in the usual way.
The Shape of the paintComponent Method
The method you override has a fixed signature. It is public, it returns nothing, and it takes exactly one parameter of type Graphics. That parameter is the graphics object through which your drawing commands are issued. You do not create this object yourself; the system supplies it when it calls the method.
Most paintComponent() methods you write follow a particular form. They begin by calling super.paintComponent(g), and the drawing commands come after that line. The skeleton below shows that shape. The comment is a placeholder for whatever picture you want the panel to show.
The next example fills in the skeleton with a concrete picture. It is an illustrative example written for this article, not code from the source. It defines a panel that draws a blue circle.
A panel that draws one blue circle
Write a JPanel subclass whose paintComponent(Graphics g) method first lets the superclass paint the background and then draws a filled blue circle.
- 1Subclass JPanel
The class extends
JPanel, so it inherits the drawing structure that everyJComponentuses. - 2Write the required signature
The method is public, returns void, and takes one Graphics parameter, matching the fixed signature the system expects.
- 3Call super.paintComponent(g) first
This line lets the
JPanelversion fill the panel with its background color before any other content is drawn. - 4Issue the drawing commands
setColorselects blue, andfillOvaldraws a filled circle using the Graphics object g. Because these calls come after the super call, the circle is painted on top of the background.
When the system draws the panel, the background is filled first and a blue circle appears on top of it. Your code describes this picture but never calls paintComponent() itself.
- extends
JPanelmakes this class a drawing surface super.paintComponent(g)fills the background firstsetColorandfillOvalissue the drawing commands through g
Who Calls paintComponent and When
The prediction you made earlier is confirmed and made precise. Your program creates the panel object and uses it. Later, when the panel needs to appear on the screen, the system calls its paintComponent() method, and the code you placed inside that method runs at that moment. It does not run at the point in main() where you created the object.
The diagram shows that the call comes from the system, not from your main() routine. The source makes this comparison explicitly: you are not likely to call a paintComponent() method any more than you are likely to call a main() routine. The system calls the method, and you write the method to say what will happen when it does.
Where super.paintComponent Belongs
The JPanel version of paintComponent() simply fills the panel with the panel's background color. When you define a subclass for use as a drawing surface, you usually want that fill to happen before any other content is drawn. This is traditionally done by calling super.paintComponent(g), which is why most paintComponent() methods start with that line.
Order matters. If the background fill ran after your shapes, it would cover the content you had just drawn. Calling the superclass version first lets the background sit underneath, and your commands then paint on top of it. The two placements below compare what ends up visible.
In the first arrangement the background sits underneath and the shapes remain visible on top. In the second arrangement the background is painted last, so it hides the shapes that were drawn earlier. The fix in that case is to move the super call to the start of the method, not to change the shape code.
Mistakes Beginners Make with paintComponent
The following mistakes come directly from the misconception this article corrects and from the placement rules above.
Common mistake Calling paintComponent(g) directly from main() to make the picture update
Why it failsThe system calls
paintComponent()when the component needs to be drawn. Calling it yourself treats it like an ordinary helper method, which the source explicitly warns against.Do this insteadLeave the invocation to the system and change the data or the drawing body so that the next draw shows the new picture.
Common mistake Putting super.paintComponent(g) after the drawing commands
What it looks likeDrawing a blue circle and then calling super.paintComponent(g) at the end of the method
Why it failsThe background fill would run after the circle and cover it.
Do this insteadCall super.paintComponent(g) as the first line of the method, then issue the drawing commands.
Common mistake Forgetting super.paintComponent(g) entirely
Why it failsThe
JPanelversion of the method is responsible for the background fill, so skipping it leaves the background to whatever was there before.Do this insteadStart the method with super.paintComponent(g) unless you have a specific reason to replace the fill.
Common mistake Writing the method with a different signature
What it looks likepublic void paintComponent() with no parameterWhy it failsThe method you override must take one Graphics parameter. Without it, your method does not replace the one the system calls.
Do this insteadUse public void paintComponent(Graphics g) exactly, and add the @Override annotation to make the compiler check it.
Common mistake Editing the data but expecting the panel to redraw without the system being asked to draw it again
Finding the Origin of a Wrong Picture
When a panel does not look as expected, the source recommends a specific order of checks. First ask whether the method is being invoked as the system intends. Only then check the drawing body. The flowchart below turns that advice into a path you can follow.
A learner's CirclePanel shows a plain gray panel with no circle. The paintComponent method is written as shown in the worked example, but the fillOval line is placed above the super.paintComponent(g) call. Using the corrected picture, identify where the bug most likely lies, and explain which line you would move and why.
Hints
- Ask first whether the method is being invoked as the system intends, then consider what the background fill does to earlier drawing.
- Think about which drawing is painted first and which is painted last.
Key Takeaways
- A custom drawing surface is a subclass of
JPanelthat overridespaintComponent(Graphics g)to describe the picture it should show. - The system calls
paintComponent()when the component needs to be drawn; the programmer writes the method but should not call it directly. - Most
paintComponent()methods begin withsuper.paintComponent(g), which fills the background so that the drawing commands that follow paint on top of it. - When a panel looks wrong, first check whether the method is invoked as the system intends, and then check the drawing body.