Как получить LinearLayout, в котором компоненты расположены друг над другом в Swing?

Я пытаюсь создать собственный файл JDialog. Я хотел бы, чтобы компоненты располагались друг над другом с их предпочтительной высотой, но ширина должна заполнять контейнер. Аналогично LinearLayout в Android. Я хочу, чтобы компоненты сохраняли предпочтительную высоту при изменении размера окна.

Из Использование менеджеров макетов:

Сценарий. Вам нужно отобразить несколько компонентов в компактном ряду в их естественном размере.

Рассмотрите возможность использования JPanel для группировки компонентов и использования либо менеджера FlowLayout JPanel по умолчанию, либо менеджера BoxLayout. SpringLayout также хорош для этого.

Например. Я хотел бы иметь JTextField выше JTabbedPane. Я пробовал использовать все предлагаемые менеджеры компоновки FlowLayout, BoxLayout, SpringLayout, но они не сохраняют естественный размер моих компонентов, когда окно JDialog увеличивается в высоту.

Есть ли в Java Swing менеджер компоновки, который я могу использовать в своей ситуации?

Вот небольшой пример, показывающий мою проблему с макетами Swing: A Dialog

import javax.swing.BoxLayout;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;

public class TestDialog extends JDialog {

    public TestDialog() {
        setLayout(new BoxLayout(this.getContentPane(), BoxLayout.PAGE_AXIS));
        //setLayout(new SpringLayout());

        JTextField field1 = new JTextField();
        JTextField field2 = new JTextField();
        JTextField field3 = new JTextField();
        JTextField field4 = new JTextField();

        JPanel panel1 = new JPanel();
        JPanel panel2 = new JPanel();
        
        panel1.setLayout(new BoxLayout(panel1, BoxLayout.PAGE_AXIS));
        panel2.setLayout(new BoxLayout(panel2, BoxLayout.PAGE_AXIS));
        
        panel1.add(field2);
        panel2.add(field3);
        panel2.add(field4);
        
        JTabbedPane tabs = new JTabbedPane();
        tabs.addTab("Tab 1", panel1);
        tabs.addTab("Tab 2", panel2);
        
        add(field1);
        add(tabs);
        
    //field1.setMaximumSize(field1.getPreferredSize());
    //SpringUtilities.makeCompactGrid(this.getContentPane(), 2, 1, 2, 2, 2, 2);
        
        this.pack();
        this.setVisible(true);
    }
    
    public static void main(String[] args) {
        new TestDialog();
    }
}

person Jonas    schedule 17.01.2011    source источник


Ответы (6)


Я хотел бы иметь JTextField над JTabbedPane. Я пробовал со всеми предложенными менеджерами компоновки

Вертикальный BoxLayout должен работать нормально. Если компоненты увеличиваются при увеличении размера кадра, вам может потребоваться добавить:

textField.setMaximumSize( textField.getPreferredSize() );
tabbedPane.setMaximumSize( tabbedPane.getPreferredSize() );

Если вам нужна дополнительная помощь, опубликуйте SSCCE, демонстрирующий проблему.

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

Вам необходимо указать предпочтительный размер для текстовых полей. Это делается с помощью:

JTextField textField = new JTextField(10);
person camickr    schedule 17.01.2011
comment
Я обновил свой вопрос с помощью простого примера. Я попробовал setMaximumSize(), как вы предложили, но он не работает с пустым JTextField, так как он будет очень маленьким. - person Jonas; 17.01.2011
comment
@ Джонас, ты можешь использовать setMinimumSize() для пустого JTextField - person sasidhar; 17.01.2011
comment
@Jonas, если вы не предоставите текстовому полю больше информации для работы с ним, это не зависит от того, сколько символов вы ожидаете ввести в текстовое поле. Смотрите мое редактирование для решения. - person camickr; 17.01.2011
comment
@camickr: Спасибо, это решает проблему с высотой, но создает проблему с шириной. Я хотел бы, чтобы компоненты могли иметь любую ширину, так как это имеет смысл для JTextField. Я хотел бы иметь что-то простое в других фреймворках, например. LinearLayout в Android и, возможно, StackPanel в WPF. - person Jonas; 17.01.2011
comment
@ Джонас, я не понимаю твоего требования. Сначала вы сказали, что НЕ хотите, чтобы размер компонента изменялся. Теперь вы говорите, что текстовое поле может иметь любую ширину. Вы можете изменить 10 при создании текстового поля на любое число, которое вы хотите. Или вы можете изменить максимальный размер, чтобы использовать предпочтительную высоту и любую максимальную ширину, которую вы решите использовать. - person camickr; 17.01.2011
comment
@camickr: Да, сначала я сказал неправильно. Но мне просто нужен простой макет, в котором все компоненты расположены друг над другом с их предпочтительной высотой и заполненной шириной контейнеров. Я удивлен, насколько сложно это сделать в Swing. Может быть, GridBagLayout сделает это, я узнаю об этом. - person Jonas; 18.01.2011
comment
Я получил макет, который хотел, с textField.setMaximumSize(new Dimension(Integer.MAX_VALUE, textField.getPreferredSize().height));, спасибо за setMaximumSize()-предложение! - person Jonas; 18.01.2011

Нулевой макет (в основном x/y) может быть тем, что вы ищете, или, возможно, групповым макетом.

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

Концептуально я думаю, что вас смущает тот факт, что макет в конечном итоге определяет размер компонентов, поэтому «естественный» размер немного вводит в заблуждение. Я предполагаю, что вам нужен размер, установленный на компоненте? Или, возможно, вы имеете в виду значение по умолчанию, когда ни один из методов setSize не был вызван для компонента?

В любом случае, с Swing вы должны быть более точными в своих ожиданиях по размеру, чтобы получить хорошие результаты.

person Yishai    schedule 17.01.2011
comment
Меня это не смущает, JTextField, вдвое превышающее значение по умолчанию, не имеет смысла, если текст имеет тот же размер, см. мой обновленный вопрос с примером. Я узнаю больше о GroupLayout и MiGLayout, но я бы предпочел решить эту проблему без сторонних библиотек. - person Jonas; 17.01.2011
comment
GroupLayout не является третьей стороной (хотя это JDK 1.6). Я думаю, вы говорите, что вы думаете, что JTextField не должен заполнять такую ​​​​большую область, потому что вы думаете, что естественный размер - это что-то разумное. Это просто неправда. С JTextArea, особенно потому, что он разделяет JTextComponent с JTextArea и JTextPane, он, вероятно, получает такое поведение. - person Yishai; 17.01.2011
comment
Да, но для JTextField это ужасное поведение. Я хочу простой макет, как LinearLayout для Android, где все компоненты расположены друг над другом с их предпочтительной высотой, но они заполняют ширину контейнера. Я удивлен, насколько сложно это сделать в Swing. - person Jonas; 18.01.2011
comment
Когда я пытаюсь использовать GroupLayout, я получаю IllegalStateException вместо JTextField с сообщением: is not attached to a horizontal group. Кажется, трудно использовать GroupLayout. - person Jonas; 18.01.2011

Я совершенно не понял, что вы имели в виду под естественным размером компонента. Но если вы не хотите, чтобы менеджер макетов испортил размер компонентов, я бы посоветовал вам использовать либо setMaximumSize(int width,int height), либо установить для макета значение null и использовать метод setBounds(int x,int y,int width,int height) для определения размера вашего компонента.

Если вы хотите настроить, как размеры компонентов изменяются относительно друг друга, я бы посоветовал взглянуть на GridBagLayout и использовать свойства weightx и weighty.

person sasidhar    schedule 17.01.2011
comment
Я бы предпочел не указывать никаких цифр, это одна из основных причин, по которой я использую менеджеры компоновки в первую очередь. - person Jonas; 18.01.2011

В итоге я реализовал свой собственный StackLayout, код ниже. Вот как выглядит то же самое приложение в моем вопросе. Компоненты располагаются сверху вниз и занимают всю ширину, как LinearLayout для Android.

Мой диалог с использованием StackLayout

import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;

public class StackLayout implements LayoutManager {

    @Override
    public void layoutContainer(Container parent) {
        Insets insets = parent.getInsets();
        int maxWidth = parent.getWidth() - (insets.left + insets.right);

        int y = insets.top;

        for(int n = 0; n < parent.getComponentCount(); n++) {
            Component c = parent.getComponent(n);
            int height = c.getPreferredSize().height;
            c.setBounds(0, y, maxWidth, height);

            y += height;
        }
    }

    @Override
    public Dimension minimumLayoutSize(Container c) {
        int sumHeight = 0;
        int maxWidth = 0;

        for(int n = 0; n < c.getComponentCount(); n++) {
            Dimension preferredSize = c.getComponent(n).getPreferredSize();
            if(n == 0) {
                maxWidth = preferredSize.width;
            } else {
                maxWidth += preferredSize.width;
            }
            sumHeight += preferredSize.height;
        }

        // add the containers insets
        Insets insets = c.getInsets();
        sumHeight += insets.top + insets.bottom;
        maxWidth += insets.left + insets.right;

        return new Dimension(maxWidth, sumHeight);
    }

    @Override
    public Dimension preferredLayoutSize(Container c) {
        return minimumLayoutSize(c);
    }

    @Override
    public void addLayoutComponent(String arg0, Component c) {}

    @Override
    public void removeLayoutComponent(Component c) {}

}
person Jonas    schedule 18.01.2011

Если вы используете GridBagLayout и не указываете заполнение или вес, вы должны получить то, что хотите. По сути, у вас будет макет сетки из 1 столбца и 2 строк. Просто создайте свой GridBagConstraints и установите только параметры x и y при добавлении компонентов — не устанавливайте никаких других параметров.

person BigMac66    schedule 17.01.2011
comment
Он работает нормально, но у меня проблемы с использованием более чем ограниченного пространства. Я обновил свой вопрос. Я попытался заполнить ширину контейнера fill=HORIZONTAL, но это не сработало. - person Jonas; 18.01.2011
comment
вам нужно добавить weightx=‹not zero› хотя бы к одному компоненту. - person shemnon; 18.01.2011
comment
@shemnon: я проверил это сейчас, но ничего не изменилось. - person Jonas; 18.01.2011

Я бы предложил гораздо более простой менеджер компоновки - BorderLayout() - из того, что вы описываете. Ниже приведен код: вам нужно указать ширину не менее 1 JTextField, чтобы настроить ширину JDialog. Вы можете использовать EmptyBorder, если вам нужны интервалы. Надеюсь, это поможет, - М.С.

импортировать java.awt.*;

импортировать javax.swing.*;

открытый класс TestDialog расширяет JDialog {

private JTextField  field1 = new JTextField(20),
            field2 = new JTextField(),
            field3 = new JTextField(),
            field4 = new JTextField();

public TestDialog(Frame f) {
    super (f, "Test Dialog");
    Container cp = getContentPane();
    cp.setLayout (new BorderLayout());
    cp.add (northPanel(), "North");
    cp.add (tabbedPane(), "Center");
    pack();
}

private JPanel northPanel () {
    JPanel nPanel = new JPanel(new BorderLayout());
    nPanel.add (field1, "Center");
              // nPanel.setBorder (...);
    return nPanel;
}

private JTabbedPane tabbedPane () {
    JTabbedPane tp = new JTabbedPane();
    tp.add ("Tab 1", panel1());
    tp.add ("Tab 2", panel2());
    return tp;
}


private JPanel panel1 () {
    JPanel tfPanel = new JPanel(new BorderLayout());
    tfPanel.add (field2, "North");
    return tfPanel;
}


private JPanel panel2 () {
    JPanel tfPanel = new JPanel(new BorderLayout()),
           t2Panel = new JPanel(new BorderLayout());
    t2Panel.add (field3, "North");
    t2Panel.add (field4, "Center");
    tfPanel.add (t2Panel, "North");
    return tfPanel;
}

public static void main (String[] args) {
    new TestDialog (null).setVisible (true);

}}

person Manidip Sengupta    schedule 20.01.2011