2016-01-06 11 views
28

Wenn ich eine JFrame minimieren, die Aero-Snap wurde auf der linken Seite des Bildschirms durch Klicken auf die Minimieren-Schaltfläche der Windows WindowDecoration und unminimize es durch Alt-Tabbing oder klicken Sie auf es in der Windows-Taskleiste, wird der Rahmen ordnungsgemäß wiederhergestellt wiederhergestellt nach links. Gut!Ist dies die einzige Möglichkeit, einen Java-Frame etwas über die Aero Snap-Funktion von Windows beizubringen?

Aber wenn ich von

setExtendedState(getExtendedState() | Frame.ICONIFIED); 

und Blick auf die Vorschau von schwebt über den Windows-Taskleiste den Rahmen minimieren, es zeigt den Rahmen eine falsche Position. Nachdem Sie es mit der Alt-Tab-Taste oder durch Klicken in der Windows-Taskleiste deaktiviert haben, wird der Rahmen an dieser falschen Position und Größe wiederhergestellt. Die Frame-Grenzen sind die "nicht napped" Werte, die Windows normalerweise wiederherstellen kann, wenn Sie den Frame vom ScreenBorder wegziehen.

Eine Bildschirmaufzeichnung des Bug:

enter image description here

Meine Schlussfolgerung ist, dass Java nicht über AeroSnap kennt und liefert die falschen Grenzen auf Windows. (Zum Beispiel Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_VERT)); gibt false zurück.)

Das ist mein Fix für den Fehler:

import java.awt.Dimension; 
import java.awt.FlowLayout; 
import java.awt.Frame; 
import java.awt.Point; 
import java.awt.event.ActionListener; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 

/** 
* Fix for the "Frame does not know the AeroSnap feature of Windows"-Bug. 
* 
* @author bobndrew 20160106 
*/ 
public class SwingFrameStateWindowsAeroSnapBug extends JFrame 
{ 
    Point  location = null; 
    Dimension size  = null; 


    public SwingFrameStateWindowsAeroSnapBug(final String title) 
    { 
    super(title); 
    initUI(); 
    } 

    private void initUI() 
    { 
    setDefaultCloseOperation(EXIT_ON_CLOSE); 
    setLayout(new FlowLayout()); 
    final JButton minimize = new JButton("Minimize"); 
    final JButton maximize = new JButton("Maximize"); 
    final JButton normal = new JButton("Normal"); 
    add(normal); 
    add(minimize); 
    add(maximize); 
    pack(); 
    setSize(200, 200); 


    final ActionListener listener = actionEvent -> 
    { 
     if (actionEvent.getSource() == normal) 
     { 
     setExtendedState(Frame.NORMAL); 
     } 
     else if (actionEvent.getSource() == minimize) 
     { 
     //Size and Location have to be saved here, before the minimizing of an AeroSnapped WindowsWindow leads to wrong values: 
     location = getLocation(); 
     size = getSize(); 
     System.out.println("saving location (before iconify) " + size + " and " + location); 

     setExtendedState(getExtendedState() | Frame.ICONIFIED);//used "getExtendedState() |" to preserve the MAXIMIZED_BOTH state 

     //does not fix the bug; needs a Window-Drag after DeMinimzing before the size is applied: 
     //   setSize(size); 
     //   setLocation(location); 
     } 
     else if (actionEvent.getSource() == maximize) 
     { 
     setExtendedState(getExtendedState() | Frame.MAXIMIZED_BOTH); 
     } 
    }; 

    minimize.addActionListener(listener); 
    maximize.addActionListener(listener); 
    normal.addActionListener(listener); 

    addWindowStateListener(windowEvent -> 
    { 
     System.out.println("oldState=" + windowEvent.getOldState() + " newState=" + windowEvent.getNewState()); 

     if (size != null && location != null) 
     { 
     if (windowEvent.getOldState() == Frame.ICONIFIED) 
     { 
      System.out.println("Fixing (possibly) wrong size and location on de-iconifying to " + size + " and " + location + "\n"); 
      setSize(size); 
      setLocation(location); 

      //Size and Location should only be applied once. Set NULL to avoid a wrong DeMinimizing of a following Windows-Decoration-Button-Minimize! 
      size = null; 
      location = null; 
     } 
     else if (windowEvent.getOldState() == (Frame.ICONIFIED | Frame.MAXIMIZED_BOTH)) 
     { 
      System.out.println("Set size and location to NULL (old values: " + size + " and " + location + ")"); 
      //Size and Location does not have to be applied, Java can handle the MAXIMIZED_BOTH state. Set NULL to avoid a wrong DeMinimizing of a following Windows-Decoration-Button-Minimize! 
      size = null; 
      location = null; 
     } 
     } 

    }); 
    } 


    public static void main(final String[] args) 
    { 
    SwingUtilities.invokeLater(new Runnable() 
    { 
     @Override 
     public void run() 
     { 
     new SwingFrameStateWindowsAeroSnapBug("AeroSnap and the Frame State").setVisible(true); 
     } 
    }); 
    } 
} 

Dies scheint für alle Situationen unter Windows7 zu arbeiten, aber es fühlt sich an wie zu viel Herumspielen mit dem Fenster-Management . Und ich habe es aus irgendeinem Grund vermieden, dies unter Linux oder MacOS zu testen ;-)

Gibt es eine bessere Möglichkeit, AeroSnap und Java Frames zusammenarbeiten zu lassen?


Edit:

ich einen Fehler bei Oracle angemeldet haben: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8147840

+1

Sie sollten dies als JDK-Bug einreichen, wenn es nicht schon da ist. – user1803551

+1

Kleiner Hinweis: Wenn Sie 'JFrame.ICONIFIED' anstelle von' Frame.ICONIFIED' usw. verwenden, können Sie den 'Frame'-Import entfernen. – user1803551

+1

Sieht so aus, als wäre dies in JDK 9 behoben worden (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8037575). – user1803551

Antwort

6

Gibt es eine bessere Art und Weise AeroSnap und Java Frames zusammenarbeiten zu lassen?

Nicht viel besser. Das direkte Einstellen des erweiterten Zustands umgeht die Einstellung des Betriebssystems.

Wenn Sie sich den Quellcode von JFrame#setExtendedState ansehen, werden Sie sehen, dass er die FramePeersetState Methode aufruft. Die JFrame Implementierung des JDK FramePeer Schnittstelle ist die WFramePeer Klasse, die setState Methode als native deklariert.Sie haben also kein Glück, bis Oracle etwas dagegen tut oder Sie nativen Code verwenden (siehe unten).

Glücklicherweise müssen Sie nicht unbedingt mit Event Listeners und Caching Grenzen gehen. Ein- und Ausblenden des Rahmens zeigt ist genug, um „Reset“, um die Größe zu, was sie vor der Minimierung war:

public class AeroResize extends JFrame { 

    public AeroResize(final String title) { 

     super(title); 
     initUI(); 
    } 

    private void initUI() { 

     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setLayout(new FlowLayout()); 
     final JButton minimize = new JButton("Minimize"); 
     final JButton maximize = new JButton("Maximize"); 
     final JButton normal = new JButton("Normal"); 
     add(normal); 
     add(minimize); 
     add(maximize); 
     pack(); 

     minimize.addActionListener(e -> { 
      setVisible(false); 
      setExtendedState(getExtendedState() | JFrame.ICONIFIED); 
      setVisible(true); 
//   setLocation(getLocationOnScreen()); // Needed only for the preview. See comments section below. 
     }); 
    } 

    public static void main(final String[] args) { 

     SwingUtilities.invokeLater(() -> new AeroResize("AeroSnap and the Frame State").setVisible(true)); 
    } 
} 

Obwohl dies hat einen Nebeneffekt der nicht eine detaillierte Vorschau des Rahmens um den Inhalt zu geben:

enter image description here

Lösung nativen Code verwenden

Wenn Sie JNA verwenden würde interessieren, dann können Sie vollständig die native Plattform der Minimierung imitieren. Sie müssen jna.jar und jna-platform.jar in Ihrem Buildpfad einschließen.

import java.awt.FlowLayout; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.SwingUtilities; 

import com.sun.jna.Native; 
import com.sun.jna.platform.win32.User32; 
import com.sun.jna.platform.win32.WinDef.HWND; 

public class AeroResize extends JFrame { 

    public AeroResize(final String title) { 

     super(title); 
     initUI(); 
    } 

    private void initUI() { 

     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setLayout(new FlowLayout()); 
     final JButton minimize = new JButton("Minimize"); 
     final JButton maximize = new JButton("Maximize"); 
     final JButton normal = new JButton("Normal"); 
     add(normal); 
     add(minimize); 
     add(maximize); 
     pack(); 

     minimize.addActionListener(e -> { 
      HWND windowHandle = new HWND(Native.getComponentPointer(AeroResize.this)); 
      User32.INSTANCE.CloseWindow(windowHandle); 
     }); 
    } 

    public static void main(final String[] args) { 

     SwingUtilities.invokeLater(() -> new AeroResize("AeroSnap and the Frame State").setVisible(true)); 
    } 
} 

Es ist ziemlich selbsterklärend. Sie erhalten einen Zeiger auf das Fenster und verwenden die native CloseWindow (die tatsächlich minimiert, gehe Figur) auf sie. Beachten Sie, dass die minimalistische Art, wie ich sie geschrieben habe, eine kleine Verzögerung verursacht, wenn die Taste zum ersten Mal gedrückt wird, weil die User32 Instanz geladen ist. Sie können es beim Start laden, um diese erste Verzögerung zu vermeiden.

Kredit geht an the accepted answer here.

+0

Danke für Ihre Analyse von 'WFramePeer'. – bobndrew

+0

Leider funktioniert Ihr Quellcode "Versteckt und zeigt den Rahmen" nur für einen gefangenen Frame-Zustand. Wenn sich der Rahmen vor der Minimierung in der Mitte des Bildschirms befindet, wird er immer in der oberen linken Ecke wiederhergestellt. Und während ein Schnappschuss-Rahmen korrekt wiederhergestellt wird, verliert er die Position und Größe, die vorher eingerastet wurden (die normalerweise beim Nicht-Fang wiederhergestellt würde). – bobndrew

+0

@bobndrew "* Wenn sich der Rahmen vor der Minimierung in der Mitte des Bildschirms befindet, wird er immer in der oberen linken Ecke wiederhergestellt. *" Ich bekomme dieses Verhalten nicht. Mein Rahmen wird immer korrekt wiederhergestellt (Position und Größe). – user1803551

0

Dies scheint ein Swing-Bug zu sein. Der Fehlerbericht auf der Bug-Datenbank:

JDK-7029238 : componentResized not called when the form is snapped

In diesem Bericht konnte der Fehler nicht reproduziert werden, begegnete man nun den gleichen Fehler (ich glaube, es ist die gleiche oder zumindest bezogen), vielleicht ist es ein guter Zeitpunkt, diesen Bericht wieder zu eröffnen. (Ich habe keine andere Bezugnahme auf diese findet, so dass ich davon ausgehen, es xxxxxxx fest)

+1

Vielleicht sind die Fehler zusammengehörig, aber der gefundene Fehler ähnelt eher dem angehängten Screenshot in [JDK-8016356] (https://bugs.openjdk.java.net/browse/JDK-8016356). Hier ist ein [Video] (https://www.youtube.com/watch?v=-nZEh6Oo7Q8) davon. Mein Fehler hat mehr mit _ "Losen des Snap-Zustandes durch programmgesteuertes Minimieren von" _ zu tun. – bobndrew

+0

Oh, ok, tut mir leid. Ich denke, mein Englisch ist schlimmer als ich dachte. – Tuzane

+0

Kein Problem, danke für Ihre Hilfe! – bobndrew