2010-10-20 25 views
11

Je rencontre des problèmes lors de l'utilisation de la fonctionnalité DWM de Windows Vista/7 sur les fenêtres Java. Je veux que l'arrière-plan de mon cadre utilise le style Aero. L'API Windows pour ce faire est fournie par la fonction DwmExtendFrameIntoClientArea dans la bibliothèque dwmapi. J'ai réussi à appeler la procédure correctement via JNA, et il fait ce qu'il est censé faire (Vous pouvez voir que par exemple lors du redimensionnement du cadre, avant le repeindre suivant vous voyez les effets aérodynamiques appropriés dans la zone pas encore peinte, voir l'image ci-jointe).Désactiver le dessin d'arrière-plan dans JFrame pour afficher correctement les effets Aero (DWM)

Mais quelque part (je ne sais pas où) un fond est peint sur l'effet Aero et l'effet est perdu.

Ce que j'ai déjà essayé:

  • En utilisant une coutume ContentPane avec l'opacité réglée sur false
  • Réglage de l'opacité du LayeredPane et la RootPane false
  • L'utilisation d'un Frame au lieu d'un JFrame
  • Définir la couleur de fond du JFrame/ContentPane au noir/entièrement transparent
  • Utilisation setLayersOpaque et une de ses variantes sur mesure, voir première réponse pour plus de détails

Jusqu'à présent, je ne pouvais pas réussir retirer ce contexte. Est-ce une limitation de AWT/Swing? Comment puis-je supprimer cet arrière-plan ou utiliser correctement l'effet Aero?

Votre aide est grandement appréciée.

Screenshot

Voici une capture d'écran d'un cadre sans contenu, après avoir réglé l'opacité du RootPane, LayeredPane et ContentPane false. Je l'ai fait rapidement en redimensionnant. Vous voyez que l'effet est correctement appliqué à la zone sur laquelle Java n'a pas encore peint.

http://i55.tinypic.com/v614qo.png (En tant que nouvel utilisateur, je ne peux pas poster directement l'image ...)

comportement étrange

Après enquête, je suis tombé sur le comportement étrange suivant. Si la taille de la fenêtre est de 150 x 150 ou moins, le contenu est affiché de manière transparente. Ceci est très glitchy pour les composants de fenêtre normale. Si vous peignez directement sur le cadre en remplaçant la méthode paint(), tout est dessiné semi-transparent. De plus, le système de coordonnées semble être un peu éteint, il apparaît que le point zéro du JFrame est réglé sur le point zéro réel de la fenêtre. Ainsi, Swing essaie de peindre les zones où se trouve effectivement la bordure de la fenêtre, qui n'est bien sûr pas visible.

Voir cette capture d'écran: http://d-gfx.kognetwork.ch/java_aero_bug.png

code Exemple

Voici le code que j'utilise.

Nécessite jna.jar et platform.jar. Disponible sur la page d'accueil de la 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); 
    } 

} 
+0

Qu'advient-il lorsque vous utilisez le look & feel de Windows à la place du métal par défaut? – Jonathan

+0

J'utilise le look & feel de Windows. Mais c'est le même problème avec le Metal L & F et le Windows L & F. Aucune différence. –

Répondre

3

Bonne question.

La réponse la plus évidente serait

WindowUtils.setWindowOpaque(this, false); 

Cela vous donne les effets visuels que vous voulez, mais vous empêche malheureusement d'être en mesure de cliquer sur la fenêtre!

La deuxième chose que j'ai essayé était de remplacer la méthode paint() pour effectuer les mêmes actions que Window.paint() lorsque l'indicateur opaque est défini sur false. Cela n'a rien fait.

Puis j'ai essayé d'utiliser Reflection. Réfléchir en réglant Window.opaque à true a donné les mêmes résultats qu'en utilisant WindowUtils.

Enfin, j'ai essayé d'ajouter ceci à 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); 
    } 
} 

Cela a fonctionné! La fenêtre répond toujours correctement aux événements de souris, mais l'arrière-plan n'est pas dessiné. Le dessin est un peu gluant, mais devrait vous mettre sur votre chemin.

De toute évidence, il est fragile car il repose sur Reflection. Si j'étais vous, je regarderais ce que Window.setLayersOpaque()fait, et essaye de répliquer cela d'une manière qui ne compte pas sur Reflection.

Édition: Lors de l'examen de la méthode setLayersOpaque, il semble vraiment que l'on se contente de désactiver la double mise en mémoire tampon sur les composants transparents. Appelez cette méthode de votre méthode enableAeroEffect() et vous êtes sur votre chemin:

//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)); 
} 
+0

Ça sonne bien, je vais essayer ça. Merci pour votre réponse détaillée. –

+0

'WindowUtils.setWindowOpaque (this, false);' n'apparaît que pour donner l'effet. En fait, c'est un pépin, vous voyez que cela ne fonctionne pas si vous tabulez à une autre fenêtre, puis de nouveau à elle. Et 'setLayersOpaque' n'a pas vraiment changé quoi que ce soit. Par ailleurs, j'ai trouvé une autre bizarrerie avec le comportement Aero, je l'ai ajouté à ma question. Il semble de plus en plus comme on ne peut pas le faire avec swing. –

+0

Qu'est-ce qui n'a pas fonctionné à propos de 'setLayersOpaque'? Je me souviens avoir essayé d'aller de l'avant et de l'arrière et ça a marché. –