Почему эти три панели с использованием GridBagLayout по-разному используют дополнительное пространство и как сделать их едиными

У меня есть приложение с несколькими панелями; Я хотел бы иметь возможность использовать разные менеджеры компоновки для разных панелей, но хотел бы, чтобы они вели себя одинаково, когда пользователь изменяет размер окна.

    package example;

    import java.awt.GridBagConstraints;
    import java.awt.GridBagLayout;

    import javax.swing.BoxLayout;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JScrollPane;
    import javax.swing.JTabbedPane;
    import javax.swing.JTextField;

    public class TP1 extends JFrame
    {
        public static void main(String[] args)
        {
            TP1 tp1 = new TP1();
            tp1.go();
        }

        public void go()
        {
            setDefaultCloseOperation(EXIT_ON_CLOSE);

            // create a panel with some labels on it
            JPanel innerFirst = new JPanel();
            innerFirst.setLayout(new BoxLayout(innerFirst, BoxLayout.PAGE_AXIS));
            innerFirst.add(new JLabel("one"));
            innerFirst.add(new JLabel("two"));
            innerFirst.add(new JLabel("three"));
            innerFirst.add(new JLabel("four"));

            // put that panel in a scroll pane
            JScrollPane firstSP = new JScrollPane(innerFirst);

            // make another panel and put our scrolled panel in it
            JPanel outerFirst = new JPanel(); 
            outerFirst.setLayout(new BoxLayout(outerFirst, BoxLayout.PAGE_AXIS));
            outerFirst.add(firstSP); 

            // create a GridBagLayout panel with some text fields on it
            JPanel innerSecond = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = .25;
            gbc.anchor = GridBagConstraints.LINE_START;
            innerSecond.add(new JTextField(8), gbc);
            gbc.gridx = 0;
            gbc.gridy = 1;
            innerSecond.add(new JTextField(10), gbc);
            gbc.gridx =0;
            gbc.gridy = 2;
            innerSecond.add(new JTextField(12), gbc);

            // put that panel in a scroll pane
            JScrollPane secondSP = new JScrollPane(innerSecond);

            // make another panel and put our second scrolled panel in it
            JPanel outerSecond = new JPanel(); 
            outerSecond.setLayout(new BoxLayout(outerSecond, BoxLayout.LINE_AXIS));
            outerSecond.add(secondSP); 

            JPanel innerThird = new JPanel(new GridBagLayout());
            GridBagConstraints gbc3 = new GridBagConstraints();
            gbc3.anchor = GridBagConstraints.LINE_END;
            gbc.weightx = .25;
            gbc3.gridx = 0;
            gbc3.gridy = 0;
            innerThird.add(new JLabel("1st label"), gbc3);
            gbc3.gridy = 1;
            innerThird.add(new JLabel("second label"), gbc3);
            gbc3.gridy = 2;
            innerThird.add(new JLabel("IIIrd label"), gbc3);

            gbc3.anchor = GridBagConstraints.LINE_START;
            gbc3.gridx = 1;
            gbc3.gridy = 0;
            innerThird.add(new JTextField(8), gbc3);
            gbc3.gridy = 1;
            innerThird.add(new JTextField(12), gbc3);
            gbc3.gridy = 2;
            innerThird.add(new JTextField(14), gbc3);

            JScrollPane thirdSP = new JScrollPane(innerThird);
            JPanel outerThird = new JPanel();
            outerThird.setLayout(new BoxLayout(outerThird, BoxLayout.LINE_AXIS));
            outerThird.add(thirdSP);

            // put the scrolled panes onto a tabbed pane
            JTabbedPane tp = new JTabbedPane();
            tp.add("text fields", outerSecond);
            tp.add("labels", outerFirst);
            tp.add("mixed", outerThird);

            // add the tabbed pane to the frame
            this.add(tp);

            // pack it and ship it.
            pack();
            setVisible(true);
        }
    }

Запустив приведенный выше код, мы получим окно с панелью с вкладками с тремя вкладками. Если мы уменьшим окно, все они получат полосы прокрутки, как и предполагалось. Если мы увеличим его, все три ведут себя по-разному: вкладка с метками оставляет их только в верхнем левом углу окна, вкладка с полями только центрирует их вертикально по левому краю, а вкладка с сеткой смешанных меток и полей центрирует их как по горизонтали, так и по вертикали в увеличенном окне.

Это неприемлемо для приложения; Мне нужно каким-то образом сделать так, чтобы все панели вели себя одинаково. Мне нужно, чтобы все они имели полосы прокрутки, и я хотел бы, чтобы все они держались в левом верхнем углу, если окно сделано больше, чем внутренняя панель.

Еще одно требование: мои вкладки заняты чем-то, что расширяет JPanel, мне сказали, прежде чем я могу поместить JScrollPane прямо во вкладку, но для моего приложения я тоже не хочу этого делать. Это просто делает другие вещи более сложными, чем они должны быть.

Помимо желания разместить все дополнительное пространство внизу и справа, мне очень хотелось бы понять, ПОЧЕМУ эти три ситуации ведут себя по-разному. Я по-прежнему считаю, что всем нам было бы лучше, если бы мы понимали принципы, лежащие в основе того, что мы делаем, вместо того, чтобы просто копировать примеры направо и налево и делать что-то методом проб и ошибок, пока оно не сработает.

(Между прочим, у меня есть панель GroupLayout, которая, кажется, тяготеет к левому верхнему углу, но я не думаю, что это необходимо для моего вопроса, и это 100 строк кода как есть.)


person arcy    schedule 09.02.2012    source источник


Ответы (2)


Похоже, чтобы понять, почему это происходит в вашем коде, нужно разобраться в некоторых терминах. Например, PAGE_AXIS, LINE_AXIS, LINE_END, LINE_START и т. д. Поскольку вы предоставляете их как ограничения, это вещи, которые описывают ориентацию компонентов, добавляемых в контейнер, и их начальную точку, например, как вы пишете:

innerFirst.setLayout(new BoxLayout(innerFirst, BoxLayout.PAGE_AXIS));

Здесь вы говорите своему BoxLayout начать добавлять компоненты с точки, которая относится к началу страницы. (Когда вы запускаете Блокнот, ваш курсор помещается на PAGE_AXIS в новом документе). Но когда вы пишете это:

JPanel innerSecond = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = .25;
gbc.anchor = GridBagConstraints.LINE_START;

Здесь термин привязка используется, когда компонент меньше, чем его область отображения. Он определяет, где в области отображения разместить компонент. Но здесь, поскольку вы упомянули, что он имеет значение LINE_START, что означает:

Place the component centered along the edge of its display area where lines of text 
would normally begin for the current ComponentOrientation. Equal to WEST for horizontal,
left-to-right orientations and EAST for horizontal, right-to-left orientations.

Вот почему три JTextFields, которые вы создали, вы видите в центре с левой стороны.

person nIcE cOw    schedule 09.02.2012
comment
Вероятно, мне следовало опубликовать другую версию — я пробовал использовать LINE_START и FIRST_LINE_START в этом месте, и это не меняет положение меток. FIRST_LINE_START должен помещать элементы в верхний левый угол, как мне нужно, но по какой-то причине этого не происходит. Кто-нибудь знает, почему? - person arcy; 09.02.2012
comment
@rcook: попробуйте добавить что-то вроде gbc.weighty = 0.1; затем делайте вещи, которые изменят местоположение. - person nIcE cOw; 09.02.2012
comment
Что ж, это изменило ситуацию — теперь три текстовых поля распределены по панели по вертикали. Есть ли способ сохранить их вместе, как этикетки? Мне все еще интересно, почему метки ведут себя по-разному. - person arcy; 09.02.2012
comment
@rcook: разница в том, что вы используете Layout Manager, для JLabel вы используете BoxLayout, а для JTextField вы используете GridBagLayout. - person nIcE cOw; 09.02.2012
comment
Этот ответ ближе всего помог мне. Ограничения веса и веса для данного компонента позволяют GridBag вычислить, сколько любого пространства сверх предпочтительного пространства GridBagPanel должно быть занято этим компонентом. Это не количество пикселей; это «вес» по сравнению с другими весомыми компонентами в этом столбце. После того, как я получил наборы weightx и weighty, мне пришлось установить first_line_start и first_line_end для нижнего и правого компонентов в пакете сетки, иначе элемент в этой ячейке растянулся бы по своему пространству. - person arcy; 09.02.2012
comment
@rcook: Даже ответ на этот вопрос помог мне понять GridBagLayout, я никогда не использовал его, просто изучил его, чтобы ответить вам, поэтому теперь буду экспериментировать с ним, чтобы узнать его немного больше :-) - person nIcE cOw; 10.02.2012

Я по-прежнему считаю, что нам всем было бы лучше, если бы мы понимали принципы, лежащие в основе того, что мы делаем,

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

person camickr    schedule 09.02.2012
comment
Я прочитал Visual Guide и большинство других частей руководств по Oracle/Sun/Java Swing. Это хорошее введение. И я согласен, что мне нужно узнать, как используются различные ограничения, это то, что я пытаюсь сделать. Я не нашел ничего, что покрывает это. Где-то есть правила, которые говорят, что в этой ситуации мы центрируем по вертикали и выравниваем по левому краю, а в этом мы выравниваем по левому краю и по верхнему краю, а в этом мы центрируем по вертикали и по горизонтали. У меня есть по одной каждой из них, и я хочу знать, по каким правилам туда помещаются эти вещи. - person arcy; 09.02.2012
comment
Учебник объясняет это. FlowLayout выравнивает компоненты в ряд. Они могут быть выровнены по левому/центру/справа в зависимости от свойства выравнивания менеджера компоновки. Для BoxLayout то же самое вы можете горизонтально/вертикально размещать компоненты. Затем в вертикальной компоновке компоненты могут быть выровнены по горизонтали слева/по центру/справа в зависимости от значения alignementX компонента. С GridBagLayout компоненты слипаются в центре, если вы не укажете значение weightX для одного из компонентов. Работайте и разбирайтесь с одним менеджером компоновки за раз. - person camickr; 09.02.2012