在Java 8中设置带装饰的JFrame的透明度

8
我希望了解如何在最新版本的Java中获取一个透明的JFrame。目前,您只能使用
<JFrame>.setOpacity();

如果窗口框架没有装饰,我对它没有用处,因此我想知道如何绕过此限制并将框架的不透明度设置为0.5f,同时保留标题栏、调整大小选项等。
我已阅读此处的文档:http://docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html。该代码仅适用于Java 6,不再运行。错误如上所述。
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)
    ...

我还尝试使用自定义Alpha值的Color(new Color(int,int,int,Alpha))设置背景(setBackground:Color),但是它抛出了完全相同的错误。用这种方式设置JPanel的透明度不起作用,因为它仍然在不透明的JFrame上方。
我在Stack Overflow上找不到其他正确解决此问题的答案。实际上,有些人建议可以通过以下方式解决:
JFrame.setDefaultLookAndFeelDecorated(true);

但是他们可能误解了,可能是指Java 7版本,我已经测试过了,结果完全一样。
我还尝试手动设置外观:
try {
    for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(info.getName())) {
            UIManager.setLookAndFeel(info.getClassName());
            break;
        }
    }

} catch [...] 

将此与上述建议的解决方案相结合也没有起作用。

请参考我链接的示例(Oracle文档)中的代码以获得MCVE,因为那是我正在使用的代码。

有什么其他方法吗?


1
setUndecorated(true) 和可能的 setDefaultLookAndFeelDecorated(false); - Joop Eggen
我的这个回答在Java8u101中仍然适用,但它使用了setDefaultLookAndFeelDecorated(true);。窗口仍然有装饰。 - Lukas Rotter
请注意,setOpacity 是在Java 7中引入的。在此之前,Java 6不支持透明度(至少使用此API)。Oracle教程中的代码在我使用Java 8(在Ubuntu上)时有效。 - Didier L
@LukasRotter 我尝试了你链接中的代码,但是出现了相同的错误 -> http://pastebin.com/b4m7x0fe 在Eclipse Neon 4.6、Windows、jdk 8上。 - Joey Allieston
@DidierL 这只是部分相关的问题,因为你链接的问题并没有给出实际答案,事实上,答案指出了我在自己的问题中开始的内容:你不能直接使用装饰使框架半透明。我的问题是关于如何绕过这个限制。我认为这对SO也很有用,因为半透明框架现在很常见,而swing也不再像以前那样。 - Joey Allieston
显示剩余7条评论
3个回答

6
据我所知,基本的答案是:不可能,至少在使用系统外观时是这样。正如Is The Java Tutorials Translucent Window example giving trouble to those playing with jdk7?中所指出的,JavaDocs明确指出“窗口必须是无装饰的”,才能使setOpacity()生效。
然而,你可以通过编程方式设置跨平台外观(丑陋的外观)来实现它,如下所示:
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());

事实上,由于跨平台的外观和感觉可以通过配置进行覆盖,因此最安全的做法实际上是将其明确设置为金属,如下所示:

UIManager.setLookAndFeel(new MetalLookAndFeel());

这种方法有效的原因是,JDK实现的Frame.setOpacity()!isUndecorated()时会抛出异常,而JFrame.frameInit()在外观的getSupportsWindowDecorations()返回true时将其设置为无装饰。undecorated。然后它调用getRootPane().setWindowDecorationStyle()JRootPane.FRAME,表示装饰将由根窗格而不是框架提供。
就我在JDK中看到的内容,Metal外观是唯一一个getSupportsWindowDecorations()返回true的外观,因为它是唯一一个覆盖它的外观,而默认实现仅返回false
但是,某些第三方外观也支持它。例如,这就是Tiny Look and Feel的情况,我刚试过: TranslucentWindowDemo with TinyLAF (请注意,我在Ubuntu上截取了此屏幕截图,TinyLAF碰巧有一个看起来像Windows XP的默认主题!)
另请参见此问题以获取已知的第三方外观列表。

很好,非常感谢,正是我在努力找答案的内容,并且讲解得很清楚。 - Joey Allieston

4

在创建JFrame窗口之前,尝试添加以下这行代码:

JFrame.setDefaultLookAndFeelDecorated(true);

准确地说,不要在开头替换JFrame,它需要是JFrame。

(您也可以在您提到的教程中发现这一行,正好放在创建窗口之前)。


这里有一个澄清,因为我刚刚成功尝试了这个方法:1) JFrame.setDefaultLookAndFeelDecorated(true); 2) UIManager.setLookAndFeel(new MetalLookAndFeel()); 3) 创建JFrame实例。任何进一步对该实例的调用都可以稍后进行,现在不是必需的。4) 我将LookAndFeel设置为“Windows”。5) 构建GUI,将JFrame设置为可见,将不透明度设置为一半。这起作用了!JFrame的装饰是Metal,但是在“4)”之后添加的任何组件都具有Windows L&F。“1)”是必需的,否则您会收到异常“java.awt.IllegalComponentStateException: The frame is decorated”。 - Dreamspace President

4

实际上这是可能的,可以使用反射的不可取方法。如果我们深入挖掘setOpacity方法(它继承自java.awt.Frame类),我们会看到以下代码:

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

isUndecorated是一个简单的getter方法,用于访问java.awt.Frame类中名为undecorated的字段。

改变该字段的值将解决此异常问题。

请查看我创建的示例

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();
        }
    }

}

预览:

预览


我在 The Microsoft Windows Look and Feel (Windows 7 x64) 上进行了测试,它可以正常工作。注意当我调用 undecorate 方法时添加的注释。我在上面进行了一些测试并意识到,如果在框架至少可见一次之前将其去装饰化,那么当您使其可见时,它将被去装饰 - 它将没有这个标题栏和其他装饰。

不过,我不确定这是否会给应用程序带来其他问题,但是您总是可以更改字段的值、更改其透明度然后再将其设置回来。


我尝试了这个方法,但是我并没有使用反射,而是在我的继承自JFrame的类中重写了isUndecorated()方法。我得到了Windows标题栏和不透明度。不确定是否会有任何副作用,但对于我的简单应用程序来说它起作用了。 - John C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接