禁用JFrame中的背景绘图以正确显示Aero(DWM)效果

我在Java窗口上使用Windows Vista / 7的DWMfunction时遇到问题。 我想让我的画面背景使用Aero风格。 执行此操作的Windows API由dwmapi库中的函数DwmExtendFrameIntoClientArea提供。 我已经设法通过JNA正确调用该程序,它完成了它应该做的事情(你可以看到,例如在调整框架大小时,在下一次重绘之前,你会看到尚未绘制的区域中的正确的空气动力效果,见附图)。

但某处(我无法弄清楚在哪里)背景被涂在Aero效果上并且效果丢失了。

我已经尝试过:

  • 使用不透明度设置为false的自定义ContentPane
  • LayeredPaneRootPane的不透明度设置为false
  • 使用Frame而不是JFrame
  • JFrame / ContentPane的背景颜色设置为黑色/完全透明
  • 使用setLayersOpaque及其自定义变体,请参阅第一个答案以获取更多详细信息

到目前为止,我无法成功删除该背景。 这是AWT / Swing的限制吗? 如何删除该背景或正确使用Aero效果?

非常感谢您的帮助。

截图

这里是没有任何内容的框架的屏幕截图,将RootPane,LayeredPane和ContentPane的不透明度设置为false。 我在resize时快速完成了。 您会看到效果已正确应用于Java尚未绘制的区域。

http://sofzh.miximages.com/java/v614qo.png (作为新用户,我无法直接发布图片…)

奇怪的行为

经过进一步调查,我发现了以下奇怪的行为。 如果窗口大小为150×150或更低,则内容将以透明方式显示。 这对于普通窗口组件来说非常小问题。 如果通过覆盖paint()方法直接在框架上绘制,则所有内容都是半透明的。 此外,坐标系似乎有点偏离,它显示为JFrame的零点设置为窗口的实际零点。 因此,Swing尝试绘制到实际上窗口边界所在的区域,然后当然不可见。

请参见此屏幕截图: http : //d-gfx.kognetwork.ch/java_aero_bug.png

示例代码

这是我使用的代码。

需要jna.jarplatform.jar 。 可从JNA主页获得。

 import com.sun.jna.Function; import com.sun.jna.Native; import com.sun.jna.NativeLibrary; import com.sun.jna.Structure; import com.sun.jna.platform.win32.WinDef.HWND; import com.sun.jna.platform.win32.WinNT.HRESULT; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.UIManager; public class AeroFrame extends JFrame { public AeroFrame() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JLabel label = new JLabel("Testlabel"); label.setOpaque(false); add(label); pack(); enableAeroEffect(); } private void enableAeroEffect() { NativeLibrary dwmapi = NativeLibrary.getInstance("dwmapi"); HWND aeroFrameHWND = new HWND(Native.getWindowPointer(this)); MARGINS margins = new MARGINS(); margins.cxLeftWidth = -1; margins.cxRightWidth = -1; margins.cyBottomHeight = -1; margins.cyTopHeight = -1; //DwmExtendFrameIntoClientArea(HWND hWnd, MARGINS *pMarInset) //http://msdn.microsoft.com/en-us/library/aa969512%28v=VS.85%29.aspx Function extendFrameIntoClientArea = dwmapi.getFunction("DwmExtendFrameIntoClientArea"); HRESULT result = (HRESULT) extendFrameIntoClientArea.invoke(HRESULT.class, new Object[] { aeroFrameHWND, margins}); if(result.intValue()!=0) System.err.println("Call to DwmExtendFrameIntoClientArea failed."); } /** * http://msdn.microsoft.com/en-us/library/bb773244%28v=VS.85%29.aspx */ public class MARGINS extends Structure implements Structure.ByReference { public int cxLeftWidth; public int cxRightWidth; public int cyTopHeight; public int cyBottomHeight; } public static void main(String[] args) { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); JFrame.setDefaultLookAndFeelDecorated(true); } catch (Exception e) { e.printStackTrace(); } new AeroFrame().setVisible(true); } } 

好问题。

最明显的答案是

 WindowUtils.setWindowOpaque(this, false); 

这为您提供了所需的视觉效果,但不幸的是,您无法点击窗口!

我尝试的第二件事是覆盖paint()方法以执行Window.paint()opaque标志设置为false时执行的相同操作。 那什么都没做。

然后我尝试使用Reflection。 将WindowUtilsreflection设置为true会产生与使用WindowUtils相同的结果。

最后,我尝试将其添加到enableAeroEffect()

 Method m = null; try { m = Window.class.getDeclaredMethod("setLayersOpaque", Component.class, Boolean.TYPE); m.setAccessible(true); m.invoke(null, this, false); } catch ( Exception e ) { //TODO: handle errors correctly } finally { if ( m != null ) { m.setAccessible(false); } } 

这有效! 窗口仍然可以正确响应鼠标事件,但不会绘制背景。 绘图有点毛躁,但应该让你顺利。

显然它很脆弱,因为它依赖于reflection。 如果我是你,我会看看Window.setLayersOpaque() 了什么,并尝试以不依赖于Reflection的方式复制它。

编辑 :在检查setLayersOpaque方法时,它似乎真的归结为禁用透明组件的双缓冲。 从您的enableAeroEffect()方法调用此方法,您就可以了:

 //original source: Sun, java/awt/Window.java, setLayersOpaque(Component, boolean) private static void setLayersTransparent(JFrame frame) { JRootPane root = frame.getRootPane(); root.setOpaque(false); root.setDoubleBuffered(false); Container c = root.getContentPane(); if (c instanceof JComponent) { JComponent content = (JComponent) c; content.setOpaque(false); content.setDoubleBuffered(false); } frame.setBackground(new Color(0, 0, 0, 0)); } 
Interesting Posts