11

Java ウィンドウで Windows Vista/7 の DWM 機能を使用すると問題が発生します。フレームの背景に Aero スタイルを使用したいと考えています。DwmExtendFrameIntoClientAreaそのための Windows APIは、dwmapiライブラリ内の関数によって提供されます。私は JNA を介して適切にプロシージャを呼び出すことができました。それは本来の動作を実行します (たとえば、フレームのサイズを変更すると、次の再ペイントの前に、まだペイントされていない領域に適切なエアロ エフェクトが表示されることがわかります。添付画像をご覧ください)。

しかし、どこか (どこかわかりません) で、Aero 効果の上に背景がペイントされ、効果が失われます。

私がすでに試したこと:

  • ContentPane不透明度を設定してカスタムを使用するfalse
  • LayeredPaneとの不透明度RootPaneを falseに設定する
  • a のFrame代わりに aを使用する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.jarとが必要platform.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);
    }

}
4

1 に答える 1

3

素晴らしい質問です。

最も明白な答えは

WindowUtils.setWindowOpaque(this, false);

これにより、必要な視覚効果が得られますが、残念ながら、ウィンドウをクリックすることができなくなります!

2 番目に試みたのは、paint() メソッドをオーバーライドして、フラグが false に設定されているWindow.paint()場合と同じアクションを実行することでした。opaqueそれは何もしませんでした。

次に、リフレクションを使用してみました。反射的Window.opaqueに 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 は引き続きマウス イベントに適切に応答しますが、背景は描画されません。図面は少しグリッチですが、理解できるはずです。

リフレクションに依存しているため、明らかに脆弱です。もし私があなただったら、 が何をするかを見て、リフレクションに依存しない方法でそれを再現しようとしますWindow.setLayersOpaque()

編集:メソッドを調べると、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));
}
于 2010-11-03T14:46:42.863 に答える