Question

I am working on some application designed to be not 100% opaque, so it basically darkens the desktop of the user and my Swing interface is shown on top of this "dark veil".

It seems to me that, when some Swing components are being moved over that veil, my JFrame would need to be repainted for my moving components not to leave a trail behind them. The thing is that repainting the JFrame is too slow and my application wouldn't run smoothly anymore.

For your convenience, I created a SSCCE class that illustrates my issue, here it is:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class TransparentFrameSSCCE extends JFrame {
    private static final Dimension SCREEN_DIMENSIONS = Toolkit.getDefaultToolkit().getScreenSize();
    private final JPanel movingPanel;

    private TransparentFrameSSCCE() {
        super();

        this.setUndecorated(true);
        this.setResizable(false);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(TransparentFrameSSCCE.SCREEN_DIMENSIONS);
            // This makes my JFrame transparent (its alpha component is set to 0)
        this.setBackground(new Color(0, 0, 0, 0));

        this.movingPanel = new JPanel();
        this.movingPanel.setBounds(0, 0, 50, 50);
        this.movingPanel.setBackground(Color.RED);

        final JPanel contentPane = new JPanel();
            // This makes my panel semi-transparent (its alpha component is set to 128)
        contentPane.setBackground(new Color(0, 0, 0, 128));
        contentPane.setLayout(null);
        contentPane.add(this.movingPanel);

        this.setContentPane(contentPane);
    }

    @Override
    public void setVisible(final boolean isVisible) {
        super.setVisible(isVisible);

        new Thread(new Runnable() {
            @Override
            public void run() {
                int x, y;

                for(;;) {
                    x = TransparentFrameSSCCE.this.movingPanel.getLocation().x;
                    y = TransparentFrameSSCCE.this.movingPanel.getLocation().y;
                    TransparentFrameSSCCE.this.movingPanel.setLocation(x + 5, y);

                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    public static void main(final String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TransparentFrameSSCCE().setVisible(true);
            }
        });
    }
}

Would anyone know any other way to do so?

UPDATE: Following @MadProgrammer's directions about Swing components transparency behavior, this is how to deal with my "dark veil". It works perfectly. Many thanks to him :)

    final JPanel contentPane = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            final Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(new Color(0, 0, 0, 128));
            g2d.fill(new Area(new Rectangle(new Point(0, 0), getSize())));
            g2d.dispose();
        }
    };

    contentPane.setOpaque(false);    // Instead of: contentPane.setColor(new Color(0, 0, 0, 128)
Was it helpful?

Solution

Java components don't have a concept of transparency, they are either opaque or fully transparent (alright, the new transparency support for top level windows is an exception ;))

What you need to do is create a custom component that is fully transparent and the override it's paintComponent and fill the area of the component with your translucent color.

Also, don't modify the state of Swing components outside of the context of the Event Dispatching Thread, strange things begin to happen. A better solution might be to use a javax.swing.Timer

For example

You may also want to take a look at Concurrency in Swing

OTHER TIPS

Check out Backgrounds With Transparency for a simple explanation of the problem. Basically, you need to make sure your custom component paints the background.

Or instead of doing the custom painting you can take advantage of the AlphaContainer class which will do the painting for you:

//this.setContentPane( contentPane);
this.setContentPane( new AlphaContainer(contentPane) );
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top