Search code examples
javaswingtransparentpaintcomponentjtabbedpane

JTabbedPane and it's tab transparency won't work


I've done several inspection and done research on how to make a single panel transparent so that the image underneath it will show but has been unsuccessful with panels on a JTabbedPane. I have setOpaque(false) and setBackground(new Color(0,0,0,20)) on both the panels of the JTabbedPane and also did the same on the JTabbedPane itself. However, I still can't get the image on the back of the tabbed pane to show. What else am I missing here?

tabbePane.java

package tabbedpanetransparencypractice;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.*;

public class tabbedPane extends JTabbedPane{

JPanel tab1panel = new JPanel();
JPanel tab2panel = new JPanel();

public tabbedPane(){
    this.setPreferredSize(new Dimension(400,400));
    this.setBackground(new Color(0,0,0,20));
    this.setOpaque(false);
    tab1panel.setBackground(new Color(0,0,0,20));
    tab2panel.setBackground(new Color(0,0,0,20));
    tab1panel.setOpaque(false);
    tab2panel.setOpaque(false);
    this.addTab("Tab 1", tab1panel);
    this.addTab("Tab 2", tab2panel);
}
}

topPanel.java

package tabbedpanetransparencypractice;
import javax.swing.*;
import java.awt.*;

public class topPanel extends JPanel{
Image myBG = new ImageIcon(getClass().getClassLoader().getResource("Assets/loginBg.jpg")).getImage();
    @Override
    public void paintComponent(Graphics g){
        g.drawImage(myBG,0,0,getWidth(),getHeight(),this);
    }

public topPanel(){
    addTabbedPane();
}

public void addTabbedPane(){
    tabbedPane tabbedpane = new tabbedPane();
    this.add(tabbedpane);
}

}

frame.java

package tabbedpanetransparencypractice;
import java.awt.Dimension;
import javax.swing.*;

public class frame extends JFrame{

topPanel myPanel = new topPanel();

public frame(){
    this.setPreferredSize(new Dimension(600,600));
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setVisible(true);
    this.pack();
    this.setLocationRelativeTo(null);

    addComponents();

}

final void addComponents(){

    this.setContentPane(myPanel);
}
}

main.java

package tabbedpanetransparencypractice;

public class main {

public static void main(String[] args) {
    frame myFrame = new frame();
}
}

This is the output I get (I want the tab1 and tab2 to be transparent to reveal the bg image underneath)

enter image description here

I'd appreciate any help. Thanks.


Solution

  • First, Swing DOES NOT know how to deal with component's whose background colors are alpha based but which are opaque. Swing only knows how to deal with fully opaque and fully transparent components.

    Using alpha based colors will generate weird and random paint artefacts. The simple answer is, you should never apply a alpha based color to a component's background (the only exception is JFrame - thanks Sun :P)

    The primary solution is, fake it.

    That is, make the component transparent (setOpaque(false)) and paint the background at a reduced alpha level. You can then use a alpha based color (because the component is no longer reliant on the color, as it's transparent), but I tend to just use a AlphaComposite as it's generally easier to control and manipulate.

    Tabbed Pane

    import java.awt.AlphaComposite;
    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.GridBagLayout;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JTabbedPane;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.border.EmptyBorder;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        public Test() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                        ex.printStackTrace();
                    }
    
                    BackgroundPane bp = new BackgroundPane();
                    bp.setLayout(new BorderLayout());
                    bp.setBorder(new EmptyBorder(20, 20, 20, 20));
    
                    SeeThroughTabbedPane tp = new SeeThroughTabbedPane();
                    tp.setAlpha(0.5f);
                    tp.addTab("Tab 1", new TestPane("I be see through"));
                    tp.addTab("Tab 2", new TestPane("But you can't see me"));
    
                    JFrame frame = new JFrame("Testing");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setContentPane(bp);
                    frame.add(tp);
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class TestPane extends JPanel {
    
            public TestPane(String text) {
                setOpaque(false);
                setLayout(new GridBagLayout());
                JLabel label = new JLabel(text);
                label.setForeground(Color.WHITE);
                add(label);
            }
    
        }
    
        public class BackgroundPane extends JPanel {
    
            private BufferedImage bg;
    
            public BackgroundPane() {
                try {
                    bg = ImageIO.read(new File("/Volumes/Disk02/Dropbox/MegaTokyo/megatokyo_omnibus_1_3_cover_by_fredrin-d4oupef 50%.jpg"));
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
    
            @Override
            public Dimension getPreferredSize() {
                return bg == null ? new Dimension(200, 200) : new Dimension(bg.getWidth(), bg.getHeight());
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                if (bg != null) {
                    Graphics2D g2d = (Graphics2D) g.create();
                    int x = (getWidth() - bg.getWidth()) / 2;
                    int y = (getHeight() - bg.getHeight()) / 2;
                    g2d.drawImage(bg, x, y, this);
                    g2d.dispose();
                }
            }
    
        }
    
        public class SeeThroughTabbedPane extends JTabbedPane {
    
            private float alpha;
    
            public SeeThroughTabbedPane() {
                setOpaque(false);
                setAlpha(1f);
            }
    
            public void setAlpha(float value) {
                if (alpha != value) {
                    float old = alpha;
                    this.alpha = value;
                    firePropertyChange("alpha", old, alpha);
                    repaint();
                }
            }
    
            public float getAlpha() {
                return alpha;
            }
    
            @Override
            protected void paintComponent(Graphics g) {
                Graphics2D g2d = (Graphics2D) g.create();
                g2d.setColor(getBackground());
                g2d.setComposite(AlphaComposite.SrcOver.derive(getAlpha()));
                g2d.fillRect(0, 0, getWidth(), getHeight());
                g2d.dispose();
                super.paintComponent(g);
            }
    
        }
    
    }
    

    Remember, anything you added to the JTabbedPane which is still opaque will remain so, not in my TestPane's constructor, I set the panel's opaque state to false

    You might also like to take a look at Painting in AWT and Swing and Performing Custom Painting