Почему мой JTextArea переполняет фрейм в GroupLayout? - PullRequest
4 голосов
/ 08 декабря 2010

Я наблюдаю странное поведение при использовании GroupLayout.У меня есть JTextArea, которая содержится внутри JScrollPane, которая изменяет размеры и выталкивает другие компоненты из JFrame.Как ни странно, если я переставлю компоновку так, чтобы JTextArea не располагал ничего выше или ниже (без пробелов), он работает нормально.Это как если бы текстовая область спрашивала контейнер, сколько места в контейнере, а затем занимала 100% его, независимо от других компонентов.Другая странная вещь заключается в том, что это происходит только тогда, когда размер JTextArea (не JScrollPane) плюс высота других компонентов в контейнере достигает значения Short.MAX_VALUE.

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

.addComponent(textArea, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE - 500)

Кроме того, если я установлю предпочтительный размер на небольшое положительное значение и вместо GroupLayout.PREFERRED_SIZE или GroupLayout.DEFAULT_SIZE, это также, кажется, убирает это поведение.например,

.addComponent(textArea, 0, 1, Short.MAX_VALUE)

Учебные руководства Java по GroupLayout, кажется, ничего не упоминают об этом и склонны использовать Short.MAX_VALUE повсеместно.Я попытался поискать в Google, но мне показалось, что эту проблему очень трудно описать в терминах поиска.

Нашел ошибку или я просто не понимаю GroupLayout?Последнее, конечно, кажется более вероятным.

Этот пример создаст простую текстовую область.Нажмите нижнюю кнопку, чтобы заполнить ее текстом (и измените размер JTextArea внутри JScrollPane).Затем вы можете щелкнуть внутри текстовой области и добавить или удалить строки.После добавления дополнительных строк нажмите кнопку перерисовки (или измените размер рамки), чтобы увидеть странное поведение.

public class GroupLayoutTest {
    public GroupLayoutTest() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame("GroupLayout test");
                Container panel = frame.getContentPane();

                GroupLayout layout = new GroupLayout(panel);
                panel.setLayout(layout);

                JButton addBtn = new JButton("Add Lines");
                JButton redrawBtn = new JButton("Redraw");

                final JTextArea textArea = new JTextArea();
                final JScrollPane textPane = new JScrollPane(textArea);

                layout.setHorizontalGroup(layout.createParallelGroup()
                        .addComponent(redrawBtn)
                        .addComponent(textPane)
                        .addComponent(addBtn));

                layout.setVerticalGroup(layout.createSequentialGroup()
                        .addComponent(redrawBtn)
                        .addComponent(textPane)
                        .addComponent(addBtn));

                addBtn.addActionListener(new ActionListener() {
                    int m = 0;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        for (int i = m; m < i + 2044; ++m) {
                            textArea.append("Line " + m + "\n");
                        }

                        // redraw the frame
                        frame.validate();
                    }
                });

                redrawBtn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        frame.validate();
                    }
                });

                frame.setPreferredSize(new Dimension(640, 480));
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static void main(String[] args) {
        new GroupLayoutTest();
    }
}

Ответы [ 2 ]

2 голосов
/ 09 декабря 2010

Я не очень знаком с GroupLayout, это то, что я наблюдал, просматривая документы и размышляя об этом. Надеюсь, это полезно. Мой ответ был довольно длинным, поэтому я подведу итог:

Почему это происходит? Сочетание 3 вещей.

  1. GroupLayout учитывает предпочтительный размер ваших компонентов. (именно поэтому вы выбрали его, судя по вашим комментариям).
  2. Ваша вертикальная группа является последовательной. Он выкладывает по одному компоненту за раз - в их предпочтительном размере или больше, если есть место.
  3. Ваш JScrollPane имеет неограниченный предпочтительный размер. Он увеличивается до любого предпочтительного размера JTextArea ... не оставляя места для компонента под ним, когда его предпочтительный размер становится больше, чем у вашего фрейма.

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

Более длительная попытка объяснения / мыслительного процесса:

Это как если бы текстовая область спрашивала контейнер, сколько места в контейнер, а затем взять 100% его, независимо от других компонентов.

Текстовая область не спрашивает контейнер, насколько он может быть увеличен - напротив, он сообщает контейнеру, какой он хочет быть. В этом случае textArea непосредственно содержится в области прокрутки. Предпочтительный размер текстовой области будет увеличиваться с размером текста в текстовой области. Предпочтительный размер области прокрутки будет увеличиваться с предпочтительным размером текстовой области, если вы не установите предпочтительный размер на панели прокрутки. Оттуда менеджер по макетам решит, как все устроить.

В этом случае вы используете GroupLayout с SequentialGroup для вертикального макета. Это позволит расположить компоненты по порядку, исходя из их размеров min / pref / max. Если вы измените положение textPane как последнего элемента в группе, он не будет воровать пространство у других ваших компонентов ... поэтому я предполагаю, что для очень больших размеров GroupLayout не волнует, отображаются ли все компоненты - если после последнего компонента не осталось места, они не отображаются до тех пор, пока контейнер не будет увеличен. Для небольших размеров пользователи могут регулировать размер рамки ... но для больших размеров, как в вашем примере, это невозможно.

Отдельно от упущения - похоже, что это просто сочетание огромной области прокрутки, а GroupLayout не знает, когда остановиться ... поэтому нижняя часть панели прокрутки обрезается. Этого можно избежать, просто установив разумный предпочтительный размер на JScrollPane, хотя, как предложил @camickr.

Другая странная вещь в том, что она появляется произойти только тогда, когда JTextArea (не JScrollPane) размер плюс другие высота компонентов внутри контейнера достичь Short.MAX_VALUE.

Если вы зарегистрируете размеры каждого компонента, вы увидите, что если вы не укажете предпочтительный размер для JScrollPane, он всегда будет иметь предпочтительный размер больше, чем у textArea, поэтому я не думаю, что выше утверждение верно. Размер области прокрутки плюс другие компоненты в контейнере все равно будут превышать Short.MAX_VALUE и, по-видимому, вызывают проблемы в GroupLayout, что очень верно.

Смысл JScrollPane, который я вижу, заключается в размещении компонента с большим (или потенциально большим) предпочтительным размером в отдельной меньшей области (с соответствующими полосами прокрутки).Затем вы всегда можете позволить полосе прокрутки увеличиваться до размера, превышающего ее предпочтительный размер ... но она начинается с указания панели прокрутки, насколько большой она должна быть.Фактически, установка предпочтительного размера эффективно сообщает JScrollPane, когда ему нужны полосы прокрутки.Например, если предпочтительный размер области прокрутки равен 400 300, то всякий раз, когда предпочтительная ширина содержащегося компонента превышает 400 (или размер области прокрутки, если она находится в контейнере, который позволяет ей расти больше, чем ее предпочтительный размер), показать горизонтальные полосы прокрутки.Точно так же и по высоте.В противном случае он всегда будет увеличиваться до размера, который у вас есть, и ему не понадобятся полосы прокрутки, устранение точки или, в некоторых случаях, предотвращение отображения других компонентов.

Документы для GroupLayoutОтметим, что он обычно используется другими инструментами компоновщика, а не разработчиками (хотя вы все еще можете).Я хотел бы рассмотреть возможность использования другого макета, чем тот, который обычно требует от вас использовать специальные максимальные значения для правильной работы.Мой личный фаворит - FormLayout , бесплатный менеджер макетов сторонних производителей с лицензией BSD.Я не думаю, что вам когда-либо нужно указывать фиксированные размеры для обычных компонентов, но в случае панелей прокрутки вы действительно хотите задать предпочтительный размер в макетах, которые соответствуют предпочтительному размеру.

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

Вот как это будет выглядеть в FormLayout (он также имеет компоновщики, но в этом случае яиспользуя ограничения ячеек, чтобы легко охватить столбец):

FormLayout layout = new FormLayout(
      "pref,fill:pref:grow", // cols
      "pref,3dlu,fill:pref:grow,3dlu,pref" // rows
);

JPanel panel = new JPanel(layout);

CellConstraints cc = new CellConstraints();

panel.add(redrawBtn, cc.xy(1, 1));
panel.add(textPane, cc.xyw(1, 3, 2)); // span 2 columns
panel.add(addBtn, cc.xy(1, 5));

frame.setContentPane(panel);
1 голос
/ 08 декабря 2010

Я не знаю внутренности GroupLayout, но обычно вам нужно указать предпочтительный размер комбинированного текста textArea / scrollpane, чтобы дать менеджеру раскладки некоторую информацию для работы. Это делается с помощью:

final JTextArea textArea = new JTextArea(10, 30);

или

textPane.setPreferredSize( new Dimension(400, 200) );

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

...