2

I've been trying to get this right for days, read so many explanations but as literally as I try to apply them, nothing seems to work.

What I need to do is create a custom shaped window. The way I want to do it is by setting the window background to an image like this one: Not like this one, but actually this one.

The method I'm using to try to accomplish this is, in general lines, the one described here https://stackoverflow.com/a/13594794/1387451

But it doesn't quite work. Instead, it looks like if java was interpreting my background as an alpha mask: White becomes opaque and black transparent. I think it's pretty awesome that java can do that, but it's not what I need.

I tried playing with the opacity of my JFrame's content pane. Nothing. I tried using a JLabel or a JPanel for the image to no avail.

Please, help me decipher this.

Here's the relevant code.

The JFrame:

public class LectorWindow extends JFrame {

    public LectorWindow() {
        Dimension winSize = new Dimension(440, 80);
        LectorDraggerListener dragger = new LectorDraggerListener(this);
        addMouseListener(dragger);
        addMouseMotionListener(dragger);

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        setUndecorated(true);
        setBackground(new Color(0f, 0f, 0f, 0f));
        setContentPane(new TransparentContentPane());

        setSize(winSize);
        int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
        setLocation(screenWidth/2 - getWidth()/2, 0);

        setLayout(new BorderLayout());

        JPanel background = new BackgroundPanel();
        background.setLayout(new BoxLayout(background, BoxLayout.X_AXIS));
        add(background);

        setResizable(false);
        setAlwaysOnTop(true);

        setUpSysTray();

        setVisible(true);
    }
}

The ContentPane:

public class TransparentContentPane extends JPanel {

    private static final long serialVersionUID = -386560825154911212L;

    public TransparentContentPane() {
        setOpaque(false);
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        g.setColor(Color.RED);
        g.fillRect(0, 0, getWidth(), getHeight());
    }
}

And the background image JPanel:

public final class BackgroundPanel extends JPanel {

    private static final long serialVersionUID = -5526340149401796784L;

    private BufferedImage background;

    public BackgroundPanel() {
        try {
            background = ImageIO.read(BackgroundPanel.class.getResource("/lector/resources/background.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        setOpaque(false);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(background.getWidth(), background.getHeight());
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);

        if (background != null) {
            Graphics2D g2d = (Graphics2D) g.create();
            int x = (getWidth() - background.getWidth()) / 2;
            int y = (getHeight() - background.getHeight()) / 2;
            g2d.drawImage(background, x, y, this);
            g2d.dispose();
        }
    }
}

I'm using OpenJDK on XFCE with window Compositing enabled. (I tried disabling it but then it all gets even weirder). I want this to work on Java 7, I don't care about 6.

Here are two screenshots of my program doing it's thing: Very wrong

With the ContentPane painted in red.

A little less wrong but still...

With the ContentPane being just a normal JPanel with setOpaque(false). (Why are we painting that red anyway?)

Community
  • 1
  • 1
Nirro
  • 746
  • 4
  • 18

1 Answers1

3

Really there's only two things that need to be done

  1. setOpaque(false) for the panel with the image.
  2. setBackground(new Color(0, 0, 0, 0)) for the frame, the last 0 being the alpha

Bonus: Just to spice things up, I added a MouseMotionListener so you can still move it around :)

enter image description here

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ImageFrame {
    private final JFrame frame = new JFrame();
    public ImageFrame() {
        frame.setUndecorated(true);
        frame.setBackground(new Color(0, 0, 0, 0));
        frame.add(new BackGroundPanel());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public class BackGroundPanel extends JPanel {

        BufferedImage image;
        int pX, pY;

        public BackGroundPanel() {
            try {
                image = ImageIO.read(new URL("https://i.stack.imgur.com/efS42.png"));
            } catch (IOException ex) {
                Logger.getLogger(ImageFrame.class.getName()).log(Level.SEVERE, null, ex);
            }
            setOpaque(false);

            addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent me) {
                    // Get x,y and store them
                    pX = me.getX();
                    pY = me.getY();
                }
            });
            addMouseMotionListener(new MouseAdapter() {
                public void mouseDragged(MouseEvent me) {
                    frame.setLocation(frame.getLocation().x + me.getX() - pX, 
                            frame.getLocation().y + me.getY() - pY);
                }
            });
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (image != null) {
                g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), this);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return image == null ? new Dimension(300, 300) : new Dimension(image.getWidth(), image.getHeight());
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ImageFrame();
            }
        });
    }
}

EDIT

I just actually tried your code, and it seems to work, given you get rid of the TransparentContentPane as the content pane . I don't know what that is for?

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • I think either XFCE's window compositing or OpenJDK are acting up on me. If it works for you it may just be platform-dependent. (I got rid of TransparentContentPane but I still see the "mask" effect.) Thank you very much for your answer, the code and the testing. :) – Nirro Mar 14 '14 at 00:13