javaswingjframecontentpane

How to make the JFrame contentPane transparent but the JFrame visible?


I wanted to know how to make the JFrame's contentPane transparent but the JFrame visible. I saw many pages but all showed on how to make the JFrame border visible but the contentPane visible.

I tried using setOpacity() but that requires the JFrame to be undecorated.

Are there any methods that I can achieve this?


Solution

  • Swing was not designed to paint with transparent backgrounds. You cannot create a transparent contentPane. However, you can fake a transparent background.

    Here's a screenshot showing a fake transparent background. I cropped and reduced the image by 50% to get it to display in this answer.

    enter image description here

    So, how did I do this?

    I took a snapshot of the background and painted the background on the JPanel.

    This only works if you do not change the background while you're displaying your JFrame. If you change the background by opening or closing any other applications, this fake will not work.

    You can move the JFrame around. Do not move the JFrame close to the edge of the screen, or you'll ruin the illusion.

    Iconifying and deiconifying the JFrame works, but the illusion becomes obvious.

    You can resize, maximize, and restore the JFrame.

    It requires a lot of code to make this fake work properly.

    Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.

    import java.awt.AWTException;
    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.FlowLayout;
    import java.awt.Graphics;
    import java.awt.Image;
    import java.awt.Point;
    import java.awt.Rectangle;
    import java.awt.Robot;
    import java.awt.Toolkit;
    import java.awt.event.ComponentAdapter;
    import java.awt.event.ComponentEvent;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    import java.awt.image.BufferedImage;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.SwingUtilities;
    
    public class TransparentJPanelView implements Runnable {
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new TransparentJPanelView());
        }
        
        private DrawingPanel drawingPanel;
        
        private JFrame frame;
        
        private TransparentJPanelModel model;
        
        public TransparentJPanelView() {
            this.model = new TransparentJPanelModel();
            this.drawingPanel = new DrawingPanel(model);
        }
    
        @Override
        public void run() {
            this.frame = new JFrame("Fake Transparent JPanel");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            
            frame.addComponentListener(new FrameComponentListener(this, model));
            FrameListener listener = new FrameListener(this, model);
            frame.addWindowListener(listener);
            frame.addWindowFocusListener(listener);
            frame.addWindowStateListener(listener);
    
            frame.add(drawingPanel, BorderLayout.CENTER);
            
            frame.pack();
            frame.setLocationByPlatform(true);
            frame.setVisible(true);
        }
        
        public void repaint() {
            drawingPanel.repaint();
        }
        
        public JFrame getFrame() {
            return frame;
        }
    
        public class DrawingPanel extends JPanel {
    
            private static final long serialVersionUID = 1L;
            
            private TransparentJPanelModel model;
            
            public DrawingPanel(TransparentJPanelModel model) {
                this.model = model;
                this.setLayout(new FlowLayout());
                this.setPreferredSize(new Dimension(600, 300));
                
                JButton button = new JButton("Click me");
                this.add(button);
            }
            
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                
                Point point = new Point(this.getLocation()); 
                SwingUtilities.convertPointToScreen(point, this); 
                
                Image image = model.getSubImage(point.x, point.y, getWidth(), getHeight());
                g.drawImage(image, 0, 0, this);
            }
            
        }
        
        public class FrameComponentListener extends ComponentAdapter {
            
            private final TransparentJPanelView view;
            
            private final TransparentJPanelModel model;
            
            public FrameComponentListener(TransparentJPanelView view, 
                    TransparentJPanelModel model) {
                this.view = view;
                this.model = model;
            }
    
            @Override
            public void componentResized(ComponentEvent event) {
                view.repaint();
            }
    
            @Override
            public void componentMoved(ComponentEvent event) {
                view.repaint();
            }
    
        }
        
        public class FrameListener extends WindowAdapter { 
    
            private final TransparentJPanelView view;
            
            private final TransparentJPanelModel model;
            
            public FrameListener(TransparentJPanelView view, 
                    TransparentJPanelModel model) {
                this.view = view;
                this.model = model;
            }
            
            @Override
            public void windowDeiconified(WindowEvent event) {
                model.setBackground();
                view.repaint();
            }
            
        }
        
        public class TransparentJPanelModel {
            
            private BufferedImage background;
            
            private final Rectangle screenRect;
            
            private final Robot robot;
            
            public TransparentJPanelModel() {
                this.robot = createRobot();
                this.screenRect = new Rectangle(
                        Toolkit.getDefaultToolkit().getScreenSize());
                setBackground();
            }
            
            private Robot createRobot() {
                Robot robot = null;
                try {
                    robot = new Robot();
                } catch (AWTException e) {
                    e.printStackTrace();
                }
                return robot;
            }
    
            public void setBackground() {
                this.background = robot.createScreenCapture(screenRect);
            }
            
            public Image getSubImage(int x, int y, int width, int height) {
                if (x < 0) {
                    x = 0;
                    width = Math.min(width, screenRect.width);
                }
                
                if (y < 0) {
                    y = 0;
                    height = Math.min(height, screenRect.height);
                }
                
                if (x + width > screenRect.width) {
                    width = screenRect.width - x;
                }
                
                if (y + height > screenRect.height) {
                    height = screenRect.height - y;
                }
                
                return background.getSubimage(x, y, width, height);
            }
            
        }
        
    }