Java: Swing: Components: Opacity and background painting
Component defines a method isOpaque(). If this method returns true, the component guarantees it completely fills its area1, so that non of its ancestors (or siblings, if components are placed on top of each other) can possibly be visible "behind" it. Opaque components repaint more efficiently: only subcomponents have to be painted if such a component is asked to repaint.
JComponent turn this into a(n) (observable) property that can be set from the outside. This does not make sense in general: only the component itself knows whether it is "naturally" (or depending on other settings) opaque or not, and there is no general way to "become" opaque, short of hardcoding a Color that is known to be non-opaque (the background color may be transparent). The default value of this property is false, but subclasses and LAF delegates change this (most of the time with the intention of causing automatic background painting).
I wouldn't take setOpaque() to seriously; the component should do what it thinks it right (so often the suggestion of a value will be ignored). On the other hand, isOpaque() should always return the correct value, and a PropertyChangeEvent should still be sent the value changes (though there is no standard listener that is interested). While it isn't critical if it returns false although the component is opaque, the other way around repainting will not work properly (This happens with standard Swing components if you set a non-opaque background.) Probably changes of the status should also cause a repaint (but typically that will happen anyway due to the changes of the property(-ies) that caused the change.
Instead there should have been a separate property
backgroundPainted that controlled automatic background painting (and, depend on the the background color and whether the "foreground" is already opaque, would update/determine the
opaque property). This was recognized already in 2000 (#4334812), but still nothing has been done about it).
By default, the UI fills the complete area of the component with its background color if isOpaque() returns true. This ensures opacity only iff the background color has no alpha value. There are three kinds of problems:
This happens if you want to use non-opaque background colors. In (#4297006) this is noticed, but not considered an error.
For example, if you want a JLabel to paint its background color (which may happen to be partly transparent), you have to setOpaque() to true, but then isOpaque() does lie, and (re)painting will produce strange results. To fix this you can leave isOpaque() at false and do one of these:
override paintComponent() and paint the background directly there, then call super.paintComponent().
override paintComponent(), set opaque to true, then call super.paintComponent(), then set it to false again (ugly).
Background filling may be unnecessary because the paint method already fills every pixel. Especially with custom components, this may be the case; but these components may not, like specific UI classes, turn off the provided background painting because they have no way to change the UI directly (if they have an UI at all):
Then you can
avoid calling super.paintComponent(). This can be done if they don't need the UI to do any painting at all, or know don't have an UI at all (often happens with completely new components).
override paintComponent(), set opaque to false, then call super.paintComponent(), then set it to true again (the other way around as above).
reproducing the implementation of JComponent.paintComponent(), and not calling ui.update(), but ui.paint() there; background painting is done most of the time in update(); this only works if you know UI.paint() does not paint the background. This is not the case for some BasicUIs, so this probably isn't an option.
This is a consequence of the confusion between opacity and background painting and as such is not a bug (as it is sometimes considered to be, (#4789187), (#4685800), even (#4615385)).
First, only because
setOpaque is most of the time interpreted as
setBackgroundPainted do people except components to be "as transparent as possible" if opaque is false.
Second, even if background painting were turned off, there is nothing wrong with partial "background" (or what appears to be background) painting because even
backgroundPainted would be low-level for automatic background painting. The requested kind of "becoming" transparent lives on a much higher level (such are also requested, see (#4261116))
Borders may be partly transparent (isBorderOpaque). But it never matters because either the component is opaque (even without the border), or it is not (no matter whether the border is). cf. (#4189266). Automatic background painting is done also behind the border.
AbstractButton: setOpaque() should not be used, instead setContentAreaFilled(). This is the proper way to suggest background painting from the outside (isOpaque() still breaks with transparent background colors).
JLayeredPane: has no UI delegate; thus paints background itself if isOpaque().
JDesktopPane: isOpaque() is overridden and always returns true, thus it always has its background filled. That means the implementation of setOpaque() is broken (fires events although nothing changed).
Things are different for (non-exotic) renderer components: isOpaque() is never used except for determining whether the background should be filled.
The renderers can use setOpaque() on their JComponents (which they are typically themselves) to turn the automatic background painting on/off, independent from the fact that the background color may not be opaque at all..
DefaultTableCellRenderer even determines isOpaque (meaning isBackgroundPainted dynamically based on the background color of the table (in general this is broken)).
I think they should not turn background painting off just because the component they render for (JList/JTable/JTree) returns true from isOpaque (and they would paint in the component background color). That does not (in general) mean that their area is already filled by that color. First, the main component may return true only because it assumes its renderers fill the whole component. Second, isOpaque() may return false only because the component has discovered its background color has an alpha value, even though the background is already prepared, so it is misleading to assume isOpaque() returning false is a sign for the renderer to turn background painting on.
1 More specifically, assuming a standard Composite, paint(Graphics) must set every pixel value independent from the pixel values before painting.
(C) 2001-2009 Christian Kaufhold (firstname.lastname@example.org)