在 Java 8 中设置装饰 JFrame 的不透明度
Set opacity of a decorated JFrame in Java 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.run(TranslucentWindowDemo.java:53)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
...
我也尝试使用具有自定义 Alpha 值 (new Color(int, int, int, Alpha)
) 的 Color
设置背景 (setBackground : Color)
,但它会引发完全相同的错误。
以这种方式设置 JPanel
的透明性是行不通的,因为它仍然会位于 JFrame
上,这不是透明的。
我在 Stack Overflow 上找不到其他正确解决此问题的答案。事实上,一些人建议可以通过以下方式解决这个问题:
JFrame.setDefaultLookAndFeelDecorated(true);
但是他们被误导了,可能指的是Java7,因为我已经测试过了,结果是一样的。
我也试过手动设置外观:
try {
for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch [...]
将此与上面建议的解决方案结合起来也没有用。
请参阅我在上面链接的示例代码 (Oracle doc) 中的 MCVE,因为这是我正在使用的示例。
有什么解决办法吗?
据我所知,基本答案是:不可能,至少系统外观 .如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()
所示工作。
但是可以使用(丑陋的)跨平台外观,您可以通过编程方式将其设置为如下:
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
事实上,由于可以通过配置覆盖跨平台外观,最安全的实际上是将其显式设置为 Metal,如下所示:
UIManager.setLookAndFeel(new MetalLookAndFeel());
这样做的原因是 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
的 JDK 实现,表明装饰将由根窗格而不是框架提供。
据我在 JDK 中所见,Metal 外观和感觉是唯一 getSupportsWindowDecorations()
returns true
,因为它是唯一一个覆盖它,默认实现只是 returns false
.
但是,一些第三方外观也支持它。 Tiny Look and Feel 就是这种情况,正如我刚刚尝试的那样:
(请注意,我在 Ubuntu 上截取了此屏幕截图,TinyLAF 恰好有一个看起来像 Windows XP 的默认主题!)
另请参阅 this question 以获取已知的第三方外观列表。
尝试在创建 JFrame 之前添加此行 window:
JFrame.setDefaultLookAndFeelDecorated(true);
就是那一行,不要在开头替换JFrame,需要是JFrame。
(您也可以在创建 window 之前提到的教程中发现这一行)。
其实这个是可以的,使用reflect的dirty方案。如果我们深入研究 setOpacity
方法(继承自 java.awt.Frame
class),我们将看到以下代码:
@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 到名为 undecorated
的字段(在 java.awt.Frame
class 内)。
更改此字段的值即可解决问题,不会抛出此异常。
查看我制作的这个示例:
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
方法时添加的注释。我对此做了一些测试,我意识到如果你在它至少一次可见之前取消装饰框架,当你让它可见时它会被取消装饰 - 它不会有这个标题栏和东西。
我不确定这是否会给应用程序带来其他问题,但您始终可以更改字段的值,更改其不透明度,然后将其设置回来。
我想知道如何在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.run(TranslucentWindowDemo.java:53)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
...
我也尝试使用具有自定义 Alpha 值 (new Color(int, int, int, Alpha)
) 的 Color
设置背景 (setBackground : Color)
,但它会引发完全相同的错误。
以这种方式设置 JPanel
的透明性是行不通的,因为它仍然会位于 JFrame
上,这不是透明的。
我在 Stack Overflow 上找不到其他正确解决此问题的答案。事实上,一些人建议可以通过以下方式解决这个问题:
JFrame.setDefaultLookAndFeelDecorated(true);
但是他们被误导了,可能指的是Java7,因为我已经测试过了,结果是一样的。
我也试过手动设置外观:
try {
for (final LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch [...]
将此与上面建议的解决方案结合起来也没有用。
请参阅我在上面链接的示例代码 (Oracle doc) 中的 MCVE,因为这是我正在使用的示例。
有什么解决办法吗?
据我所知,基本答案是:不可能,至少系统外观 .如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()
所示工作。
但是可以使用(丑陋的)跨平台外观,您可以通过编程方式将其设置为如下:
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
事实上,由于可以通过配置覆盖跨平台外观,最安全的实际上是将其显式设置为 Metal,如下所示:
UIManager.setLookAndFeel(new MetalLookAndFeel());
这样做的原因是 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
的 JDK 实现,表明装饰将由根窗格而不是框架提供。
据我在 JDK 中所见,Metal 外观和感觉是唯一 getSupportsWindowDecorations()
returns true
,因为它是唯一一个覆盖它,默认实现只是 returns false
.
但是,一些第三方外观也支持它。 Tiny Look and Feel 就是这种情况,正如我刚刚尝试的那样:
(请注意,我在 Ubuntu 上截取了此屏幕截图,TinyLAF 恰好有一个看起来像 Windows XP 的默认主题!)
另请参阅 this question 以获取已知的第三方外观列表。
尝试在创建 JFrame 之前添加此行 window:
JFrame.setDefaultLookAndFeelDecorated(true);
就是那一行,不要在开头替换JFrame,需要是JFrame。
(您也可以在创建 window 之前提到的教程中发现这一行)。
其实这个是可以的,使用reflect的dirty方案。如果我们深入研究 setOpacity
方法(继承自 java.awt.Frame
class),我们将看到以下代码:
@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 到名为 undecorated
的字段(在 java.awt.Frame
class 内)。
更改此字段的值即可解决问题,不会抛出此异常。
查看我制作的这个示例:
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
方法时添加的注释。我对此做了一些测试,我意识到如果你在它至少一次可见之前取消装饰框架,当你让它可见时它会被取消装饰 - 它不会有这个标题栏和东西。
我不确定这是否会给应用程序带来其他问题,但您始终可以更改字段的值,更改其不透明度,然后将其设置回来。