How does paintComponent work?

JavaSwingPaintcomponent

Java Problem Overview


This might be a very noob question. I'm just starting to learn Java

I don't understand the operation of paintComponent method. I know if I want to draw something, I must override the paintComponent method.

public void paintComponent(Graphics g)
{
   ...
}

But when is it called? I never see anything like "object.paintComponent(g)" but still it is drawn when the program is running.

And what is the Graphics parameter? Where is it from? Parameter must be supplied when the method is called. But as I said before, it seems like this method is never be explicitly called. So who provides this parameter? And why do we have to cast it to Graphics2D?

public void paintComponent(Graphics g)
{
    ...
    Graphics2D g2= (Graphics2D) g;
    ...
}

Java Solutions


Solution 1 - Java

The (very) short answer to your question is that paintComponent is called "when it needs to be." Sometimes it's easier to think of the Java Swing GUI system as a "black-box," where much of the internals are handled without too much visibility.

There are a number of factors that determine when a component needs to be re-painted, ranging from moving, re-sizing, changing focus, being hidden by other frames, and so on and so forth. Many of these events are detected auto-magically, and paintComponent is called internally when it is determined that that operation is necessary.

I've worked with Swing for many years, and I don't think I've ever called paintComponent directly, or even seen it called directly from something else. The closest I've come is using the repaint() methods to programmatically trigger a repaint of certain components (which I assume calls the correct paintComponent methods downstream.

In my experience, paintComponent is rarely directly overridden. I admit that there are custom rendering tasks that require such granularity, but Java Swing does offer a (fairly) robust set of JComponents and Layouts that can be used to do much of the heavy lifting without having to directly override paintComponent. I guess my point here is to make sure that you can't do something with native JComponents and Layouts before you go off trying to roll your own custom-rendered components.

Solution 2 - Java

Two things you can do here:

  1. Read Painting in AWT and Swing
  2. Use a debugger and put a breakpoint in the paintComponent method. Then travel up the stacktrace and see how provides the Graphics parameter.

Just for info, here is the stacktrace that I got from the example of code I posted at the end:

Thread [AWT-EventQueue-0] (Suspended (breakpoint at line 15 in TestPaint))	
	TestPaint.paintComponent(Graphics) line: 15	
	TestPaint(JComponent).paint(Graphics) line: 1054	
	JPanel(JComponent).paintChildren(Graphics) line: 887	
	JPanel(JComponent).paint(Graphics) line: 1063	
	JLayeredPane(JComponent).paintChildren(Graphics) line: 887	
	JLayeredPane(JComponent).paint(Graphics) line: 1063	
	JLayeredPane.paint(Graphics) line: 585	
	JRootPane(JComponent).paintChildren(Graphics) line: 887	
	JRootPane(JComponent).paintToOffscreen(Graphics, int, int, int, int, int, int) line: 5228	
	RepaintManager$PaintManager.paintDoubleBuffered(JComponent, Image, Graphics, int, int, int, int) line: 1482	
	RepaintManager$PaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1413	
	RepaintManager.paint(JComponent, JComponent, Graphics, int, int, int, int) line: 1206	
	JRootPane(JComponent).paint(Graphics) line: 1040	
	GraphicsCallback$PaintCallback.run(Component, Graphics) line: 39	
	GraphicsCallback$PaintCallback(SunGraphicsCallback).runOneComponent(Component, Rectangle, Graphics, Shape, int) line: 78	
	GraphicsCallback$PaintCallback(SunGraphicsCallback).runComponents(Component[], Graphics, int) line: 115	
	JFrame(Container).paint(Graphics) line: 1967	
	JFrame(Window).paint(Graphics) line: 3867	
	RepaintManager.paintDirtyRegions(Map<Component,Rectangle>) line: 781	
	RepaintManager.paintDirtyRegions() line: 728	
	RepaintManager.prePaintDirtyRegions() line: 677	
	RepaintManager.access$700(RepaintManager) line: 59	
	RepaintManager$ProcessingRunnable.run() line: 1621	
	InvocationEvent.dispatch() line: 251	
	EventQueue.dispatchEventImpl(AWTEvent, Object) line: 705	
	EventQueue.access$000(EventQueue, AWTEvent, Object) line: 101	
	EventQueue$3.run() line: 666	
	EventQueue$3.run() line: 664	
	AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]	
	ProtectionDomain$1.doIntersectionPrivilege(PrivilegedAction<T>, AccessControlContext, AccessControlContext) line: 76	
	EventQueue.dispatchEvent(AWTEvent) line: 675	
	EventDispatchThread.pumpOneEventForFilters(int) line: 211	
	EventDispatchThread.pumpEventsForFilter(int, Conditional, EventFilter) line: 128	
	EventDispatchThread.pumpEventsForHierarchy(int, Conditional, Component) line: 117	
	EventDispatchThread.pumpEvents(int, Conditional) line: 113	
	EventDispatchThread.pumpEvents(Conditional) line: 105	
	EventDispatchThread.run() line: 90	

The Graphics parameter comes from here:

RepaintManager.paintDirtyRegions(Map) line: 781 

The snippet involved is the following:

Graphics g = JComponent.safelyGetGraphics(
                        dirtyComponent, dirtyComponent);
                // If the Graphics goes away, it means someone disposed of
                // the window, don't do anything.
                if (g != null) {
                    g.setClip(rect.x, rect.y, rect.width, rect.height);
                    try {
                        dirtyComponent.paint(g); // This will eventually call paintComponent()
                    } finally {
                        g.dispose();
                    }
                }

If you take a look at it, you will see that it retrieve the graphics from the JComponent itself (indirectly with javax.swing.JComponent.safelyGetGraphics(Component, Component)) which itself takes it eventually from its first "Heavyweight parent" (clipped to the component bounds) which it self takes it from its corresponding native resource.

Regarding the fact that you have to cast the Graphics to a Graphics2D, it just happens that when working with the Window Toolkit, the Graphics actually extends Graphics2D, yet you could use other Graphics which do "not have to" extends Graphics2D (it does not happen very often but AWT/Swing allows you to do that).

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

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

class TestPaint extends JPanel {

	public TestPaint() {
		setBackground(Color.WHITE);
	}

	@Override
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.drawOval(0, 0, getWidth(), getHeight());
	}

	public static void main(String[] args) {
		JFrame jFrame = new JFrame();
		jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jFrame.setSize(300, 300);
		jFrame.add(new TestPaint());
		jFrame.setVisible(true);
	}
}

Solution 3 - Java

The internals of the GUI system call that method, and they pass in the Graphics parameter as a graphics context onto which you can draw.

Solution 4 - Java

Calling object.paintComponent(g) is an error.

Instead this method is called automatically when the panel is created. The paintComponent() method can also be called explicitly by the repaint() method defined in Component class.

The effect of calling repaint() is that Swing automatically clears the graphic on the panel and executes the paintComponent method to redraw the graphics on this panel.

Solution 5 - Java

You might have to redefine the method void paintComponent(Graphics g){} if you want any previous drawing to be permanent on a component. You need to do this by calling the ascending class's method explicitly like super.painComponent();. This way, any time java will need to use that paintComponent method you are keeping changes made.

This is explained by the fact that if you don't, the superclass will undo everything you've done by simply calling its own method, totally ignoring any change.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHải PhongView Question on Stackoverflow
Solution 1 - JavaSeKaView Answer on Stackoverflow
Solution 2 - JavaGuillaume PoletView Answer on Stackoverflow
Solution 3 - JavaGianView Answer on Stackoverflow
Solution 4 - JavaSaumya SharmaView Answer on Stackoverflow
Solution 5 - Javajfo420View Answer on Stackoverflow