Является ли это единственным способом научить Java Frame чему-нибудь о функции Aero Snap в Windows?
Если я минимизирую JFrame
который был Aero-привязан слева от экрана, нажав на кнопку сворачивания Windows WindowDecoration и свернув ее, нажав Alt-Tabbing или щелкнув ее на панели задач Windows, рамка будет восстановлена правильно, привязанная слева. Хорошо!
Но если я уменьшу кадр
setExtendedState( getExtendedState() | Frame.ICONIFIED );
и посмотрите на предварительный просмотр, наведя курсор на панель задач Windows, она показывает неправильную позицию фрейма. После его минимизации с помощью Alt-Tabbing или нажатия на панели задач Windows, фрейм восстанавливается в этом неправильном положении и размере. Границы рамки - это "неотвязанные" значения, которые Windows обычно запоминает для восстановления, если вы перетаскиваете рамку из ScreenBorder.
Экранная запись ошибки:
Мой вывод заключается в том, что Java не знает об AeroSnap и поставляет неправильные границы для Windows. (Например Toolkit.getDefaultToolkit().isFrameStateSupported( Frame.MAXIMIZED_VERT ) );
возвращает ложь.)
Это мое исправление для ошибки:
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 ответа
Есть ли лучший способ, позволяющий AeroSnap и Java Frames работать вместе?
Не намного лучше. Непосредственная настройка расширенного состояния позволяет обойти настройку операционной системы.
Если вы посмотрите на исходный код JFrame#setExtendedState
вы увидите, что он вызывает FramePeer
"s setState
метод. JDK's 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
Экземпляр загружен. Вы можете загрузить его при запуске, чтобы избежать этой первой задержки.
Кредит идет к принятому ответу здесь.
Это похоже на ошибку Swing. Отчет об ошибке в базе данных ошибок:
JDK-7029238: componentResized не вызывается при привязке формы
В этом отчете ошибка не может быть воспроизведена, теперь вы столкнулись с той же ошибкой (я думаю, что это то же самое или, по крайней мере, связано), возможно, сейчас самое время снова открыть этот отчет. (Я не нашел никакой другой ссылки на это, поэтому я предполагаю, что это не было исправлено)