如果我JFrame
通过单击Windows WindowDecoration的最小化按钮并通过Alt-Tabbing最小化它或在Windows任务栏中单击它来最小化Aero-snapped到屏幕左侧,那么框架将正确地恢复到左侧.好!
但是,如果我最小化帧
setExtendedState( getExtendedState() | Frame.ICONIFIED );
并通过将鼠标悬停在Windows TaskBar上来查看预览,它会将框架显示错误的位置.通过Alt-Tabbing取消最小化或在Windows任务栏中单击它后,框架将在此错误的位置和大小恢复.帧边界是"未绑定"值,如果您将帧拖离ScreenBorder,Windows通常会记住恢复该值.
Bug的屏幕录制:
我的结论是,Java不了解AeroSnap并向Windows提供了错误的界限.(例如,Toolkit.getDefaultToolkit().isFrameStateSupported( Frame.MAXIMIZED_VERT ) );
返回false.)
这是我对bug的修复:
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 ); } } ); } }
这似乎适用于Windows7下的所有情况,但感觉太乱用窗口管理了.我出于某种原因避免在Linux或MacOS下测试这个;-)
是否有更好的方法让AeroSnap和Java Frames协同工作?
编辑:
我在Oracle上提交了一个错误:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8147840
是否有更好的方法让AeroSnap和Java Frames协同工作?
好多了.直接设置扩展状态会绕过操作系统设置它的处理.
如果您看一下源代码,JFrame#setExtendedState
您会看到它调用FramePeer
的setState
方法.JDK的接口JFrame
实现FramePeer
是WFramePeer
类,它将其setState
方法声明为native
.因此,在Oracle对此做一些事情或使用本机代码之前,你运气不好(见下文).
幸运的是,您不必为事件监听器和缓存边界而疯狂.隐藏和显示框架足以将大小"重置"为最小化之前的大小:
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)); } }
虽然这确实有副作用,没有给出框架内容的详细预览:
解决方案使用本机代码如果您想使用JNA,那么您可以完全模仿本机平台的最小化.您需要在构建路径中包含jna.jar
和jna-platform.jar
.
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)); } }
这是非常自我解释的.你得到一个指向窗口的指针,并使用原生CloseWindow
(实际上最小化,去图).请注意,我编写它的简约方法会在第一次按下按钮时导致一个小延迟,因为User32
实例已加载.您可以在启动时加载它以避免第一次延迟.
信用证转到这里接受的答案.