Set opacity of a decorated JFrame in Java 8
Asked Answered
V

3

8

I would like to know how to get a transparent JFrame in the latest version of Java.

Currently, you can only use

<JFrame>.setOpacity();

if the frame is not decorated.

I have no use for an undecorated frame, so I'd like to know how to go around this restriction and set the opacity of the frame to 0.5f while still keeping the title bar, resize options etc.

I have read the docs here: http://docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html. The code only worked on Java 6 and no longer runs. The error, as I said, is:

Exception in thread "AWT-EventQueue-0" java.awt.IllegalComponentStateException: The frame is decorated
    at java.awt.Frame.setOpacity(Frame.java:960)
    at TranslucentWindowDemo$1.run(TranslucentWindowDemo.java:53)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
    ...

I have also tried setting the background (setBackground : Color) using a Color with custom Alpha value (new Color(int, int, int, Alpha)) but it throws the exact same error. Setting the transaprency of a JPanel this way won't work, as it will still lay on the JFrame, which is not transparent.

I could find no other answer on Stack Overflow that correctly addressed this issue. In fact, a few suggested that this could be fixed with:

JFrame.setDefaultLookAndFeelDecorated(true);

But they were misinformed of perhaps referring to Java 7, as I have tested it and the result is just the same.

I have also tried to manually set the Look And Feel:

try {
    for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(info.getName())) {
            UIManager.setLookAndFeel(info.getClassName());
            break;
        }
    }

} catch [...] 

And combining this with the solution suggested above also did not work.

Please refer to the code over to the example I linked above (Oracle doc) for a MCVE, as that is the one I'm using.

Any way around this?

Ventral answered 16/9, 2016 at 19:18 Comment(12)
setUndecorated(true) and likely setDefaultLookAndFeelDecorated(false);Collative
I said in the question that I need the frame to be decorated..Ventral
Do you want the decoration to also have 0.5f opacity or should it stay fully visible?Darell
This answer of mine still works for me in Java8u101, but it uses setDefaultLookAndFeelDecorated(true);. The window is still decorated.Darell
Note that setOpacity was introduced in Java 7. Java 6 did not support transparency before that (at least with this API). The code in the Oracle tutorial works for me with Java 8 (on Ubuntu).Tarsometatarsus
@Lukas I think what the OP really wants is to keep the system decorations instead of the cross-platform ones, and that does not seem to be possible.Tarsometatarsus
@DidierL I can't see anything in the question that would indicate that. The only thing about system/default decoration was "In fact, a few suggested that this could be fixed with: JFrame.setDefaultLookAndFeelDecorated(true); But they were misinformed of perhaps referring to Java 7, as I have tested it and the result is just the same."Darell
@Lukas Well, that's a guess from the fact that I can reproduce the issue if I call UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());. Maybe he has set it in his swing.properties, which then prevents to run the examples?Tarsometatarsus
Related, or even a duplicate of Is The Java Tutorials Translucent Window example giving trouble to those playing with jdk7? (the accepted answer still applies to Java 8)Tarsometatarsus
I am fine with the image decoration being either translucent or opaque, as long as it is there. The code provided from the tutorial does not work.Ventral
@LukasRotter I tried the code in the answer you linked, same error -> pastebin.com/b4m7x0fe on Eclipse Neon 4.6, Window, jdk 8.Ventral
@DidierL This is only partially related, as the question you linked gives no actual answer, in fact, the answers point out what I begin with in my own question: you can't make the frame translucent directly with decorations. My question is about how one could go around that limitation. I believe it's also useful for SO, as translucent frames are not uncommon nowadays, and neither is swing (not as it used to though).Ventral
T
6

As far as I can tell, the basic answer is: it is not possible, at least with the System look and feel. As indicated in Is The Java Tutorials Translucent Window example giving trouble to those playing with jdk7?, the JavaDocs clearly indicate that “the window must be undecorated” for setOpacity() to work.

It is however possible to do it with the (ugly) Cross-platform look and feel, that you can progrmmatically set as follows:

UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

In fact, as the cross-platform look and feel could be overridden through configuration, the safest would actually be to set it explicitly to Metal as follows:

UIManager.setLookAndFeel(new MetalLookAndFeel());

The reason this works, is that the JDK implementation of Frame.setOpacity() throws an exception when !isUndecorated(), and JFrame.frameInit() sets itself as undecorated when the look and feel's getSupportsWindowDecorations() returns true. It then calls getRootPane().setWindowDecorationStyle() with JRootPane.FRAME, indicating that the decorations will be provided by the root pane instead of the frame.

From what I can see in the JDK, the Metal look and feel is the only one for which getSupportsWindowDecorations() returns true, as it is the only one which overrides it, and the default implementation simply returns false.

However, some third-party look and feels support it too. This is the case for instance for the Tiny Look and Feel, as I just tried:

TranslucentWindowDemo with TinyLAF

(Note that I took this screenshot on Ubuntu, TinyLAF just so happens to have a default theme that looks like Windows XP!)

See also this question for a list of known third-party look and feels.

Tarsometatarsus answered 17/9, 2016 at 15:11 Comment(1)
Nice, thank you very much, exactly what I was trying to figure out, and well explained.Ventral
E
4

Try adding this line before creating the JFrame window:

JFrame.setDefaultLookAndFeelDecorated(true);

Exactly that line, don't replace JFrame in the beggining, it needs to be JFrame.

(You can also spot this line in the tutorials you mentioned precicely placed before creating the window).

Essential answered 17/8, 2017 at 16:35 Comment(1)
Here's a clarification, since I just tried this successfully: 1) JFrame.setDefaultLookAndFeelDecorated(true); 2) UIManager.setLookAndFeel(new MetalLookAndFeel()); 3) Create JFrame instance. Any further calls for that instance can come later, not necessary now. 4) I set the LookAndFeel "Windows". 5) Build GUI, set JFrame to visible, set the opacity to half. This worked! The JFrame's decorations are Metal, but any components added after "4)" have the Windows L&F. "1)" IS NECESSARY, else you get the Exception "java.awt.IllegalComponentStateException: The frame is decorated"Overline
O
4

Actually this is possible, using the dirty solution of reflect. If we dig into setOpacity method (which is inherited from java.awt.Frame class) we will see the following code:

@Override
public void setOpacity(float opacity) {
    synchronized (getTreeLock()) {
        if ((opacity < 1.0f) && !isUndecorated()) {
            throw new IllegalComponentStateException("The frame is decorated");
        }
        super.setOpacity(opacity);
    }
}

where isUndecorated is a simple getter to the field named undecorated (inside java.awt.Frame class).

Changing the value of this field will do the trick and this exception won't be thrown.

Check this example i have made:

public class JFrameOpacity {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            setSystemLookAndFeel();
            JFrame frame = new JFrame("Opacity to decorated Frame");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new FlowLayout());

            JButton decreaseOpacity = new JButton("Reduce Opacity");
            decreaseOpacity.addActionListener(e -> {
                if (frame.getOpacity() - 0.1f <= 0.1f)
                    frame.setOpacity(0.1f);
                else
                    frame.setOpacity(frame.getOpacity() - 0.1f);
            });
            frame.add(decreaseOpacity);

            JButton increaseOpacity = new JButton("Increase Opacity");
            increaseOpacity.addActionListener(e -> {
                if (frame.getOpacity() + 0.1f >= 1f)
                    frame.setOpacity(1f);
                else
                    frame.setOpacity(frame.getOpacity() + 0.1f);
            });
            frame.add(increaseOpacity);

            frame.setSize(300, 300);
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            try {
                undecorate(frame); //Change it after frame is visible
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        });
    }

    private static void undecorate(Frame frame) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        Field undecoratedField = Frame.class.getDeclaredField("undecorated");
        undecoratedField.setAccessible(true);
        undecoratedField.set(frame, true);
    }

    private static void setSystemLookAndFeel() {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
            e.printStackTrace();
        }
    }

}

Preview:

preview


I have tested this in The Microsoft Windows Look and Feel (Windows 7 x64) and it works. Note the comment i added when i call undecorate method. I made some tests on that and i realized that if you undecorate the frame before it gets at least one time visible, when you will make it visible it will be undecorated - it will not have this title bar and stuff.

I am not sure though if this is going to give other problems to the application but you can always change the value of the field, change its opacity and the set it back.

Opportune answered 9/7, 2019 at 22:8 Comment(1)
I ran with this, but instead of using reflection just overrode the isUndecorated() in my class which extends JFrame. I get the Windows title bar and opacity. Not sure if there would be any side effects, but it works for my simple use case.Continuate

© 2022 - 2024 — McMap. All rights reserved.