Если я минимизирую JFrame, который был привязан к левому краю экрана, нажав кнопку свертывания окна WindowDecoration Windows, и уменьшил его с помощью Alt-Tabbing или щелкнув его на панели задач Windows, фрейм будет восстановлен правильно щелкнул слева. Хороший!

Но если я минимизирую кадр на

setExtendedState( getExtendedState() | Frame.ICONIFIED );

И посмотрите на предварительный просмотр, наведя курсор на панель задач Windows, он показывает неправильное положение кадра. После отмены минимизации с помощью Alt-Tabbing или щелчка по нему на панели задач Windows, фрейм восстанавливается в неправильном положении и размере. Границы кадра - это «незакрепленные» значения, которые Windows обычно запоминает для восстановления, если вы перетаскиваете кадр за пределы ScreenBorder.

Экранная запись ошибки:

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

28
bobndrew 6 Янв 2016 в 19:40

2 ответа

Лучший ответ

Есть ли лучший способ совместить AeroSnap и Java Frames?

Не намного лучше. Непосредственная установка расширенного состояния обходит обработку ОС по его установке.

Если вы посмотрите на исходный код JFrame#setExtendedState, вы увидите, что он вызывает метод FramePeer setState. Реализация интерфейса FramePeer в JDK 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));
    }
}

Хотя у этого есть побочный эффект, заключающийся в отсутствии подробного предварительного просмотра содержимого фрейма:

enter image description here

Решение с использованием нативного кода

Если вы хотите использовать 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. Вы можете загрузить его при запуске, чтобы избежать этой первой задержки.

Благодарим принятый ответ здесь .

7
Community 23 Май 2017 в 11:47

Похоже, это ошибка Swing. Отчет об ошибке в базе данных ошибок:

JDK-7029238: componentResized не вызывается, когда форма привязана

В этом отчете ошибка не может быть воспроизведена, теперь вы столкнулись с той же ошибкой (я думаю, что она такая же или, по крайней мере, связана с ней), возможно, сейчас хорошее время, чтобы повторно открыть этот отчет. (Я не нашел другого упоминания об этом, поэтому предполагаю, что это не было исправлено)

0
Mayuso 11 Янв 2016 в 13:10