paintComponent called multiple times after frame initialization

Tags: ,



Consider the example:

@SuppressWarnings("serial")
public static void main(final String[] args) {
    
    final var frame = new JFrame();
    frame.setPreferredSize(new Dimension(150, 150));
    frame.add(new JPanel() {
        @Override protected void paintComponent(final Graphics g) {
            super.paintComponent(g);
            System.out.println("painting panel");
        }
    });
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
    SwingUtilities.invokeLater(() -> frame.setVisible(true));
}

There is a JFrame with one JPanel with overridden paintComponent method, simply printing something to the console each time this method is called. Every time I run this program, I can see “painting panel” line 3 times in my console.

Why is method paintComponent of JPanel called multiple times instead of once? Is there a way to avoid unnecessary repainting of components?

Answer

Essentially, no. But it’s not as problematic as you think.

Painting requests are controlled by the underlying system’s graphics driver. Sometimes an application may be allocated only a small amount of video memory, in which case the system will paint a window one small piece at a time.

Which means that just because paintComponent is called multiple times doesn’t mean a full repaint of the component is taking place; before the method is called, the Graphics object may have a clip set to only paint part of the window. paintComponent methods don’t need to concern themselves with this, as most Graphics operations will return immediately if they can know they won’t affect any pixels outside of the current clip bounds. The point here is that multiple calls to paintComponent aren’t necessarily as wasteful as you think.

Also, as Gilbert pointed out, calling pack() will “realize” the Window; that is, the Window gets assigned an actual native desktop window. (Java refers to this as becoming displayable.) That will trigger a paint. Showing the window may also trigger a paint. Moving it may trigger a paint. Moving the mouse over it may trigger a paint.

It is never safe to make assumptions about how often painting occurs. To paint reliably, assume your paintComponent method may be called any number of times. Never change the data which is the basis for painting from within a painting method.

In general, there is no way to control when painting happens. Of course, if your program calls repaint, you do have control over how and when you call that.



Source: stackoverflow