Backgrounds With Transparency
Posted by Rob Camick on May 31, 2009
Swing components allow you to change the background color of a component simply by using the setBackground() method. It is also possible to use a Color created with an “alpha” value. The alpha value defines the transparency of a Color. Unfortunately, once you start using alpha values in your background color you may encounter some undesireable painting artifacts.
To understand the problem we need to look at the setOpaque() method of Swing components. When the opaque value is:
- true – The component agrees to paint all of the pixels contained within its rectangular bounds. This is done by painting the background before doing the custom painting of the component.
- false – The component makes no guarantees about painting all the pixels within its rectangular bounds. Therefore the RepaintManager needs to do extra work. It needs to find an opaque ancestor of the component. Once it does, it then repaints the ancestor and all its children to make sure the rectangular bounds of the component is completely repainted.
Either way you are assured that all the pixels in the bounds of the component are painted every time the component is repainted.
So what happens when you introduce an alpha value in the background color of the component? Well, if the opaque value is
- true – all the pixels are repainted, but because they are now transparent, you may see unexpected painting artifacts
- false – the ancestor component and children are painted, but the components background is not painted so you lose the transparency effect.
Either way you don’t get the result you expect.
In the image below, the checkboxes are transparent and the panel uses a transparent background color. Everytime the mouse moves over a checkbox the transparency is reapplied resulting in an increasingly less transparent and darker background color:
The solution is to make sure the ancestor of the component is painted before the components background is painted. This is done in two steps:
- first, the component is made non-opaque, by using setOpaque(false)
- next, you add custom painting to the component to paint its background, by overriding the paintComponent() method. Note, when the alpha value is 0, then you can skip this step since the color is completely transparent so there is nothing to paint.
An example of applying the above two steps to a JPanel is shown below:
JPanel panel = new JPanel() { protected void paintComponent(Graphics g) { g.setColor( getBackground() ); g.fillRect(0, 0, getWidth(), getHeight()); super.paintComponent(g); } }; panel.setOpaque(false); // background of parent will be painted first panel.setBackground( new Color(255, 0, 0, 20) ); frame.add(panel);
Eventually you may get tired of continually extending components to do this type of custom painting. Using the AlphaContainer class provides a reasonably easy workaround. It works by wrapping the transparent component and by doing the painting of the background using the components background color. The AlphaContainer will also manage the opaque property of the component. To use the AlphaContainer, the above code can be changed to:
JPanel panel = new JPanel();
panel.setBackground( new Color(255, 0, 0, 20) );
frame.add( new AlphaContainer(panel));
Next time you notice painting artifacts when using a transparent background Color try one of the above suggestions.
Note, an alpha value of 0 implies complete transparency which is equivalent to making the component non-opaque by using component.setOpaque(false). If complete transparency is your goal then make the component non-opaque instead of setting the background color to be transparent to take advantage of normal Swing painting. In the cases that you want to make a scrollpane transparent then you need to make the scrollpane, the viewport and the component all non-opaque.
Try The Demo
– Using Java™ Web Start (JRE 6 required)
Get The Code
Related Reading
Painting in AWT and Swing – Additional Paint Properties
Java API: java.awt.Color
Jeff said
Awesome. Exactly what I needed! Thank you very, very much!
Rob Camick said
Glad it solved your problem.
:)) said
Wohoo! Great, it works! :)
Rob Camick said
Nice to get such an enthusiastic response :)
:)) said
yeah, it was a long way until i found your blog post! ;-)
Jan Wehmeyer said
Works Great on a JEditorPane. But not when I put that JEditorPane in a ScrollPane.
Anything Special I need to think of when I stack things?
Rob Camick said
An alpha value of 0, implies complete transparency so there is no need to set the background. All you need to do is make all the component non-opaque to take advantage of Swing painting. But you must do this for all components, the editor pane, the viewport and the scrollpane.
Jan Wehmeyer said
Thanks that worked. Great Job!
prem ambodkar said
its nice to set panel as transparent bt i also want to makes my tabbed pane invisible how can i do it???
Rob Camick said
The UI for the tabbed pane controls this. You can override this property for the tabbed pane by using the following before creating the tabbed pane:
Chamz Dee said
Perfect..! Thank you very much :)
Rob Camick said
No problem. Glad it helped.
goctamhon said
Thank you! I’m looking for this :)
Rob Camick said
Glad you found what you where looking for.
Musa said
Reblogged this on A big picture of Hadi Waseem.
Danniss10 said
How does this work if you are painting directly onto a frame??
Rob Camick said
You shouldn’t be painting directly onto a frame. You should do your custom painting on JPanel or JComponent and then add the panel to the frame. So I’m not sure how to answer your question.
Anonymous said
Thank you so much. This really helped. Well explained!
Anonymous said
Thank you so much this is really helpful . :)
Rob Camick said
Nice to see this helped.
Final Kill said
Windows 8 does not like this, because whenever I hover over the checkbox, it leaves the last thing that was hovered over by the mouse, resulting in a really buggy background, and many glitches.
Danny said
Thank you very much !!!!
e7aso said
Everythings work fine in case of using solid color as a background. I’m trying to use transparent image in background. I use paintComponent method and first I draw image with my background and then I call super.paintComponent(). Although I do these things I still get anoying white artifacts around JLabel text displaying on my JPanel with custom background. Is there something I do wrong?
Here is my overrided paintComponent function in my JPane, which I simply set as a content pane in my JFrame.
Additionaly I have set opaque of JPanel and JLabel to false.
Please I really need help.
Rob Camick said
Not sure I understand exactly what you are tying to do. The super.paintComponent() doesn’t do anything since you made the panel non-opaque, so the background does not get repainted. I would suggest that you check out the Alpha Icon blog entry. This will allow you to add a transparent Icon to a JLabel. Then you can use the label as a container just by setting the layout manager of the label. Now you can add other components to this label. If you have further questions, I suggest you try the forums, since this question doesn’t deal with the suggested solutions presented in this blog entry.
micrococo said
Hi.
First, thanks for the article. It works fine except in one case: if the main container (JFrame, JWindow, JDialog) is transparent too. In this case the problem reappears and there are other issues. For example, if you drag a JSlider leaves a trail behind. Do you know any solution for this? I’v tried setting a “new AlphaContainer (myPanel)” as the content pane but it doesn’t work.
public static void main (String[] args)
{
JDialog dialog = new JDialog ();
dialog.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
// It doesn’t matter if the root pane is opaque or not
dialog.getRootPane ().setOpaque (false);
dialog.setUndecorated (true);
// Without the following line it works fine
// The color and the alpha value seem to be irrelevant
dialog.setBackground (new Color (0, 0, 0, 0));
JPanel panel = new JPanel (new BorderLayout ());
panel.setBackground (new Color (0, 0, 0, 64));
dialog.add (new AlphaContainer (panel));
JSlider slider = new JSlider ();
slider.setBackground (new Color (255, 0, 0, 32));
// If the JSlider is not added with AlphaContainer it leaves trails always
panel.add (new AlphaContainer (slider), BorderLayout.NORTH);
JButton button = new JButton (“Label text”);
// If dialog background is transparent button’s background gets darken
button.setContentAreaFilled (false);
panel.add (button, BorderLayout.SOUTH);
dialog.setSize (320, 200);
dialog.setLocationRelativeTo (null);
dialog.setVisible (true);
}
By the way, I’m using openJDK 7 on Linux. Translucency is not supported, at least on my system. I think I’ve tried every combination of color transparency and opacity in the JDialog root pane.
Thanks in advance.
Greetings.
fudaraku said
I had the same problem with a transparent JFrame. Removing content did not work under Linux but was ok under Windows. Having an explicit contentPane for the JFrame and add clearRect to its paintComponent override solved my problem.
*__________* said
SuperAwesooooooooooooome