Обеспечение неявного минимального размера в JDialog?

Вопрос

У меня есть модальное приложение JDialog, для которого я хочу иметь обязательный минимальный размер (т.е. ОС не позволяет пользователю делать окно меньше минимального размера). Но размер окна по-прежнему должен изменяться (т.е. setResizable(false) не вариант). Желательно, чтобы обязательный минимальный размер был определен неявно (т. е. динамически определялся минимальными размерами дочерних компонентов, а не фиксированным размером, заданным явно с помощью setMinimumSize).

Проблема, с которой я столкнулся, заключается в том, что если я явно не установлю минимальный размер для JDialog через setMinimumSize, ОС не будет применять его. Таким образом, хотя JDialog неявно знает правильный минимальный размер для использования в любой момент времени, этот размер не будет применяться, если я не вызову что-то вроде dlg.setMinimumSize(dlg.getMinimumSize()). См. SSCCE ниже.

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

Есть ли способ настроить JDialog для обеспечения неявного минимального размера диалогового окна? Если нет, есть ли какой-нибудь другой элегантный способ обеспечить динамическое изменение минимального размера в открытом диалоговом окне?

SSCCE

В этом SSCCE цель состоит в том, чтобы создать диалоговое окно, размер которого не может быть меньше 300x300 пикселей. Эта цель достигается только в том случае, если минимальный размер задан явно.

public class SSCCE {
    public static void main(String[] args) {
        JDialog dlg = new JDialog(null, ModalityType.APPLICATION_MODAL);
        dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        JPanel pnl = new JPanel(new BorderLayout());
        pnl.add(new JLabel("300x300"), BorderLayout.CENTER);

        // The panel is given a hard-coded minimum size just to make the
        // problem easier to see
        pnl.setMinimumSize(new Dimension(300,300));

        dlg.add(pnl);

        System.out.println("Dialog minimum size: " + dlg.getMinimumSize());
        // Minimum size is not enforced by the OS without this call
        dlg.setMinimumSize(dlg.getMinimumSize());
        System.out.println("Dialog minimum size: " + dlg.getMinimumSize());

        dlg.pack();
        dlg.setVisible(true);
    }
}

С явным вызовом setMinimumSize:

Диалог JDialog 300x300 правильного размера

Без явного вызова setMinimumSize:

JDialog неправильного размера

Вывод в обоих случаях:

Минимальный размер диалогового окна: java.awt.Dimension[width=300,height=300]
Минимальный размер диалогового окна: java.awt.Dimension[width=300,height=300]

Для чего это стоит - я наблюдал это на Windows 8.1 с jdk1.8.0_91.


person Kevin K    schedule 19.10.2016    source источник
comment
диалоговое окно, размер которого не может быть меньше 300x300 пикселей. Почему? И что еще более важно, почему именно диалог, а не область содержимого диалога (панель)?   -  person Andrew Thompson    schedule 22.10.2016
comment
@AndrewThompson Размер 300x300 выбран для SSCCE произвольно. В SSCCE я вручную установил это как минимальный размер для JPanel, чтобы имитировать некоторый элемент управления или контейнер, который имеет неотъемлемый минимальный размер, например JLabel или JTable. Минимальные размеры распространяются через менеджеры компоновки вверх — поскольку панель имеет минимальный размер 300x300, то и диалог должен иметь минимальный размер 300x300. Но ОС не применяет это, если только я явно не устанавливаю минимальный размер с помощью вызова JDialog.setMinimumSize().   -  person Kevin K    schedule 09.11.2016


Ответы (3)


Однако это далеко не идеально, поскольку приводит к фиксации минимального размера диалогового окна.

Да, вы не должны жестко кодировать значения.

Вы можете переопределить метод getMinimumSize() вашей панели, чтобы:

  1. просто верните предпочтительный размер или
  2. вернуть некоторое соотношение предпочтительного размера.

Редактировать;

есть ли какой-нибудь другой элегантный способ обеспечить динамическое изменение минимального размера в открытом диалоговом окне?

Вы можете добавить AncestorListener на свою панель. Событие ancestorAdded генерируется, когда панель добавляется в видимое окно (поэтому в основном событие генерируется после setVisible() в диалоговом окне). Затем вы можете использовать метод SwingUtilities.windowForComponent(...), чтобы получить окно для панели. Затем вы можете вызвать setMinimumSize() в диалоговом окне.

person camickr    schedule 19.10.2016
comment
Я пробовал переопределить getMinimumSize() на JPanel, это не имеет значения. Жестко закодированные значения предназначены только для краткости в SSCCE. JDialog знает правильный минимальный размер, проблема в том, что он не применяет его, если он явно не установлен через dlg.setMinimumSize(...) - person Kevin K; 19.10.2016
comment
это полезно, спасибо. Я полагаю, что я мог бы также использовать ContainerListener для ответа, если компонент добавлен/удален. - person Kevin K; 20.10.2016

Проверяйте комментарии в коде. Здесь нет магического числа!

import java.awt.*;
import java.awt.Dialog.ModalityType;
import javax.swing.*;

public class SSCCE {
    public static void main(String[] args) {
        JDialog dlg = new JDialog(null, ModalityType.APPLICATION_MODAL);
        dlg.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        JPanel pnl = new JPanel(new BorderLayout());
        pnl.add(new JLabel("As big as needed"), BorderLayout.CENTER);
        //pnl.setMinimumSize(new Dimension(300,300));
        dlg.add(pnl);

        System.out.println("Dialog minimum size: " + dlg.getMinimumSize());
        dlg.pack(); // make the dialog as large as needed to display content
        // Minimum size is not enforced by the OS without this call
        dlg.setMinimumSize(dlg.getPreferredSize());
        System.out.println("Dialog minimum size: " + dlg.getMinimumSize());

        dlg.setVisible(true);
    }
}

Выход

Dialog minimum size: java.awt.Dimension[width=97,height=16]
Dialog minimum size: java.awt.Dimension[width=113,height=55]
person Andrew Thompson    schedule 20.10.2016
comment
Привет, Эндрю, как я уже упоминал в camickr, минимальный размер жестко запрограммирован на JPanel только для краткости в SSCCE. Мой фактический вопрос о том, как заставить ОС применять неявный минимальный размер файла JDialog. Я обновил свой вопрос, чтобы сделать это более ясным. - person Kevin K; 20.10.2016

У меня была такая же проблема, и это код, который я использую:

addComponentListener(new ComponentAdapter() {
    @Override
    public void componentResized(ComponentEvent e) {
        Dimension size = getSize();
        
        if( size.getWidth() <= getMinimumSize().getWidth() || size.getHeight() <= getMinimumSize().getHeight() ) {
            int width = (int) (getMinimumSize().getWidth() + 2);
            int height = (int) (getMinimumSize().getHeight() + 2);
            
            setSize( new Dimension( width, height ) );
        }
    }
});
person Brad    schedule 01.05.2021