簡體   English   中英

這是教授Java框架有關Windows的Aero Snap功能的唯一方法嗎?

[英]Is this the only way to teach a Java Frame something about the Aero Snap feature of Windows?

如果我通過單擊Windows WindowDecoration的最小化按鈕並通過Alt-Tabbing最小化它或在Windows任務欄中單擊它來最小化Aero捕捉到屏幕左側的JFrame ,框架將恢復正確地捕捉到剩下。 好!

但是,如果我最小化幀

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的源代碼,你會看到它調用了FramePeersetState方法。 JDK的FramePeer接口的JFrame實現是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.jarjna.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實例已加載。 您可以在啟動時加載它以避免第一次延遲。

信用證轉到這里接受的答案

這似乎是一個Swing bug。 關於Bug數據庫的錯誤報告:

JDK-7029238:在捕捉表單時未調用componentResized

在這個報告中,錯誤無法再現,現在您遇到了相同的錯誤(我認為它是相同的,或至少相關),也許是重新打開此報告的好時機。 (我沒有找到任何其他參考,所以我認為它沒有被修復)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM