Search code examples
user-interfaceswingcomponentstransparencytranslucency

Java Swing - Translucent Components


I recently asked a question about translucent components causing odd artifacts from seemingly not updating properly. The answer I received caused the artifacts to go away, but at the cost of translucency.

The solution was to- for every translucent component- also call the setOpaque(false) function. This way, Swing knew that it needed to redraw the background behind those components.

However, this came at the cost of the translucency that I was trying to achieve. It caused the components to become transparent instead.

The premise is this: I am designing the GUI for a chat client, and a feature request was to have a background. I successfully got the background working by following a code snippet for extending the JPanel class, but then I wanted the components to allow the background to show. After setting their translucency, remnants of updated components were being displayed where they shouldn't have been. I came here and got my problem solved, but now I've got a new problem. So here we are.

So, here is what I've surmised:

-Calling the setOpaque(false) function for each desired component and NOT setting a translucent color does not achieve what I want.

-Setting a translucent color and NOT calling setOpaque(false) allows the translucent background to show, but causes artifacts, putting me back at square one.

So I need some middle ground between transparent with no artifacts, and translucent with artifacts. Namely, I want a translucent background (not completely transparent) that has no artifacts.

It seems like I'm going to need to override the JFrame to cause it to repaint all its components, regardless of the opacity. Unless there's something I'm missing.. which is why I'm here!

Thanks!

(Here's a link to the original question, with a picture for reference: Java Swing - Translucent Components causing Artifacts)


Solution

  • One option would be to override the components and draw the background yourself:

    class TranslucentLabel extends JLabel {
        public TranslucentLabel(String text) {
            super(text);
            setOpaque(false);
        }
    
        @Override
        public void paintComponent(Graphics g) {
            g.setColor(new Color(255, 0, 0, 64));
            Insets insets = getInsets();
            g.fillRect(insets.left, insets.top, 
                    getWidth() - insets.left - insets.right, 
                    getHeight() - insets.top - insets.bottom);
            super.paintComponent(g);
        }
    }
    

    EDIT: Alternatively you could draw the translucent background colour for the child components directly onto the panel, then you would not have to override components:

    class YourPanel extends JPanel {
        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D)g.create();
    
            // Draw your background image here to g2d.
    
            g2d.setColor(new Color(255, 0, 0, 64));
            Area area = new Area();
            for (Component component : getComponents()) {
                area.add(new Area(component.getBounds()));
            }
            g2d.fill(area);
            g2d.dispose();
        }
    }
    

    There is a disadvantage to this approach. If there is a genuinely transparent part of a component (such as a rounded border), then its entire background will be coloured.