How Swing Components Are Displayed When the GUI for SwingApplication is painted, here’s what happens:
- The top-level container, JFrame, paints itself.
- The content pane first paints its background, which is a solid gray rectangle. It then tells the JPanel to paint itself. In most look and feels, the JPanel is opaque by default and the content pane’s background rectangle doesn’t actually appear in the finished GUI, being completely obscured by the JPanel. Note: It’s important that the content pane be opaque. Otherwise, messy repaints will result. We could invoke setOpaque(true) on the JPanel and then make it the content pane. This would slightly simplify the containment hierarchy and painting by removing an unnecessary container.
- In most look and feels, the JPanel is opaque and the first painting it does fills its background. Next, it paints its border. The border is an EmptyBorder, which has no effect except for increasing the JPanel’s size by reserving some space at the edge of the panel. Finally, the panel asks its children to paint themselves.
- To paint itself, the JButton paints its background rectangle, if necessary, then the text that the button contains, and then its border. If the button has the keyboard focus, meaning that any typing goes directly to the button for processing, the button does some look-and-feel-specific painting to make clear that it has the focus.
- To paint itself, the JLabel paints its text.
In this way, each component paints itself before any of the components it contains. This ensures that the background of a JPanel, for example, is visible only where it isn’t covered by painting performed by one of the components it contains.
If this is really how Swing paints, they really do need double-buffered graphics - to hide their sins.
The algorithm for efficient GUI paints is very simple, efficient … and this isn’t it. Puzzled out this particular problem out in the early 1980’s (prior to any version of Microsoft Windows) when writing a GUI framework to run on PCs (i.e. the 5Mhz 8088 CPU).
First you set a clipping region, as often only a portion needs repaint. Next you do a depth-first walk of the visual objects, first asking front-most children to paint, then asking only the exposed fragments of the parent to paint (i.e. only the portions not covered by a child). Paint children before parents, and pass a clipping rectangle (which could be used by the component to optimize the repaint processing) for just the exposed area in need of update.
With the exception of painting characters over a background, you want to paint a pixel exactly once. If the parent is painted before the child and they have different colors you can get rather nasty flicker (aside from the wasted processing time). You can hide the flicker by using double-buffered graphics (and waste huge chunks of memory and compute time).