这是教授Java框架有关Windows的Aero Snap功能的唯一方法吗?

时间:2016-01-06 16:40:47

标签: java swing jframe minimize aero-snap

如果我通过单击Windows WindowDecoration的最小化按钮并通过Alt-Tabbing或在Windows任务栏中单击它来最小化Aero-snapped到屏幕左侧的JFrame最小化,帧恢复正确对齐到左侧。好!

但是,如果我通过

最小化帧
setExtendedState( getExtendedState() | Frame.ICONIFIED );

通过将鼠标悬停在Windows TaskBar上来查看预览,它会将框架显示错误的位置。 通过Alt-Tabbing取消最小化或在Windows任务栏中单击它后,框架将在此错误的位置和大小恢复。帧边界是“未绑定”值,如果您将帧拖离ScreenBorder,Windows通常会记住恢复该值。

Bug的屏幕录制:

enter image description here

我的结论是,Java不了解AeroSnap并向Windows提供了错误的界限。 (例如Toolkit.getDefaultToolkit().isFrameStateSupported( Frame.MAXIMIZED_VERT ) );返回false。)

这是我对错误的解决方法:

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

2 个答案:

答案 0 :(得分:6)

  

是否有更好的方法让AeroSnap和Java Frames协同工作?

好多了。直接设置扩展状态会绕过操作系统设置它的处理。

如果您查看JFrame#setExtendedState的源代码,您会看到它调用FramePeer的{​​{1}}方法。 JDK setState接口的JFrame实现是FramePeer类,它将WFramePeer方法声明为setState。所以,在Oracle对此做一些事情或者使用本机代码之前,你运气不好(见下文)。

幸运的是,您不必为事件监听器和缓存范围而疯狂。隐藏和显示框架足以“重置”#34;最小化之前的大小:

native

虽然这确实有副作用,没有详细预览框架的内容:

enter image description here

使用本机代码的解决方案

如果您关心使用JNA,那么您可以完全模仿原生平台的最小化。您需要在构建路径中加入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.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实例。您可以在启动时加载它以避免第一次延迟。

信用转到the accepted answer here

答案 1 :(得分:0)

这似乎是一个Swing bug。关于Bug数据库的错误报告:

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

在本报告中,错误无法再现,现在您遇到了同样的错误(我认为它是相同的,或至少相关),也许是重新打开此报告的好时机。 (我没有找到任何其他参考,所以我认为它没有被修复)