java - 桌面窗面管理員已停用 - 禁用JFrame中的背景繪圖以正確顯示Aero(DWM)效果



windows 7 basic (1)

偉大的問題。

最明顯的答案是

WindowUtils.setWindowOpaque(this, false);

這給你的視覺效果,但不幸的是,你不能點擊窗口!

我嘗試的第二件事是重寫paint()方法來執行Window.paint()opaque標誌設置為false時執行的相同操作。 那沒有做任何事情。

然後我嘗試使用反射。 反射性地將WindowUtils設置為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);
    }
}

這工作! 窗口仍然響應鼠標事件,但沒有繪製的背景。 畫圖有點不好,但應該讓你走。

顯然它是脆弱的,因為它依賴於反射。 如果我是你,我會看看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));
}

我在Java窗口上使用Windows Vista / 7的DWM功能時遇到了問題。 我想讓我的框架的背景使用Aero風格。 這樣做的Windows API由dwmapi庫中的函數DwmExtendFrameIntoClientArea提供。 我已經設法通過JNA正確地調用程序,並且它做它應該做的事情(例如,當調整框架的大小時,在下一次重繪之前,您可以看到尚未繪製的區域中的適當的空氣效果,見附圖)。

但是某個地方(我無法弄清楚)Aero效果的背景是如何繪製的,而且效果會丟失。

我已經試過了:

  • 使用不透明度設置為false的自定義ContentPane
  • LayeredPaneRootPane的不透明度設置為false
  • 使用Frame而不是JFrame
  • 設置JFrame / ContentPane的背景顏色為黑色/完全透明
  • 使用setLayersOpaque及其自定義變體,請參閱第一個答案以獲取更多詳細信息

到目前為止,我無法取消這個背景。 這是AWT / Swing的限制嗎? 如何刪除該背景或正確使用Aero效果?

非常感謝您的幫助。

截圖

這裡有一個沒有任何內容的框架截圖,已經設置RootPane,LayeredPane和ContentPane的不透明度為false。 我在調整大小的時候很快就做到了。 您會看到該效果已正確應用於Java尚未繪製的區域。

http://i55.tinypic.com/v614qo.png (作為新用戶,我不能直接發布圖片...)

奇怪的行為

經過進一步的調查,我發現了以下奇怪的行為。 如果窗口大小為150x150或更低,內容將透明顯示。 這對正常的窗口組件非常​​不好。 如果通過覆蓋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);
    }

}