java какой менеджер компоновки подходит для этой задачи?

У меня есть родитель JPanel с 3 дочерними элементами JPanel внутри. Все они в настоящее время используют GridLayout и вместе представляют класс UML. Проблема в том, что когда я добавляю новый атрибут или метод, все 3 JPanel увеличиваются до одинакового размера.

Желательное поведение заключается в том, что:

  1. Размер поля заголовка всегда остается неизменным при добавлении метода или атрибута.
  2. При добавлении метода увеличивается только панель методов, а размер панели заголовков и атрибутов остается прежним.
  3. При добавлении атрибута увеличивается только панель атрибутов, а размер других панелей остается прежним.

Родительская панель JPanel уже может автоматически увеличиваться/уменьшаться всякий раз, когда добавляется метод/атрибут. Я играю с банкоматом GridBagLayout, но я не приближаюсь к желаемым результатам.

Есть ли простой (или более простой) способ решить эту проблему?!

Вот несколько фотографий, чтобы показать мою ситуацию.

Недавно созданный класс UML => введите здесь описание изображениявот как он ведет себя сейчас => введите здесь описание изображения

Но я хочу это =› введите описание изображения здесьили это=›введите здесь описание изображения или это =›  введите здесь описание изображения

Edit2: добавлены новые фотографии для ясности. Мне ужасно жаль, если оригинальная версия вводит в заблуждение.

Редактировать 3: ДА! Я разобрался! Чувствовалось вечность!! Вот ССЭК:

Дочерняя панель

    import java.awt.Component;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;

    public class APanel extends JPanel{
        private JTextField tf;

    public APanel() {
        this.setLayout(new GridLayout(0,1));
        this.addMouseListener(mouseInputListener);
      }

    MouseInputListener mouseInputListener = new MouseInputAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
        System.out.println("Adding a new text field!");
        tf = MyTF.create("Double click");
        addNewTF(tf);
        Component source = (Component) e.getSource();
        Container c = source.getParent();
        while(true) {
            if(c instanceof PPanel)
                break;
            else
                c=c.getParent();
        }
        PPanel p = (PPanel) c;
        p.expand();
        }
    };

    public void addNewTF(JTextField tf) {
        this.add(tf);
        this.setSize(this.getWidth(), this.getHeight()+tf.getHeight());
        this.revalidate();
        this.repaint();

    }
}

Родительская панель:

    import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

public class PPanel extends JPanel{
    //private APanel panel1;
    private JPanel panel1;
    private APanel panel2;
    private APanel panel3;
    public PPanel() {

        this.setLayout(new BoxLayout(this , BoxLayout.Y_AXIS));
        this.setBackground(Color.black);

        panel1 = new JPanel();
        panel1.setLayout(new GridLayout(0,1));
        panel1.add(new JTextField("title"));
        panel2 = new APanel();
        panel2.setBorder(BorderFactory.createLineBorder(Color.red));
        panel3 = new APanel();
        panel3.setBorder(BorderFactory.createLineBorder(Color.black));

        this.add(panel1);
        this.add(Box.createRigidArea(new Dimension(0,1)));
        this.add(panel2);
        this.add(panel3);


    }
    public void expand() {
        this.setSize(this.getWidth(), this.getHeight()+33);
        this.revalidate();
        this.repaint();
    }
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        PPanel panel = new PPanel();
        panel.setBounds(10, 10, 100, 150);
        JPanel c = new JPanel(null);
        c.add(panel);
        frame.add(c);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(new Dimension(350, 300));
        frame.setTitle("Demo");
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

Вспомогательный класс:

    import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import javax.swing.JTextField;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.MouseInputListener;

public class MyTF {
    public static JTextField create(String name) {
        final JTextField tf = new JTextField(name);
        System.out.println(tf.getPreferredSize());
        tf.setPreferredSize(new Dimension(100,33));
        tf.addMouseListener(mouseInputListener);
        return tf;
    }

    static MouseInputListener mouseInputListener = new MouseInputAdapter() {
        @Override
            public void mouseClicked(MouseEvent e) {
                Component source = (Component) e.getSource();
                Container c = source.getParent();
                while(true) {
                    if(c instanceof PPanel)
                        break;
                    else if(c instanceof APanel)
                    {
                        c.dispatchEvent(e);
                        c = c.getParent();
                        break;

                    }
                    else
                        c=c.getParent();
                }
                c.dispatchEvent(e);
        }

    };
}

Я отказался от попытки поиграть с GridBagLayout, это было слишком для меня. Затем я попробовал borderLayout, как было предложено, но не смог заставить его работать так, как я этого хочу. Затем, наконец, BoxLayout, он должен был работать, но в моем коде была ошибка! Поэтому, когда я попробовал 0verbose code submit и поэкспериментировал с ним, у меня ничего не вышло! Только после того, как я закончил SSEEC, выполнил окончательную компиляцию и запустил его, прежде чем решил опубликовать (в этот момент я практически сдался), тогда я понял, что это сработало ... Панель, которая может расти в своем собственном пространстве, они не мешайте друг другу.

Я был как WTF!

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

Еще лучше! Я могу указать расстояние между средним полем и полем над ним и под ним на один пиксель. Это означает, что я все еще могу использовать трюк mKorbel, чтобы нарисовать заднюю линию, разделяющую эти поля!

Редактировать 4: есть ли способ установить размер компонента? Если вы запустите SSEEC, вы заметите, что после добавления JTextField оно становится огромным! Он больше контейнера...


person bili    schedule 28.08.2011    source источник
comment
Бросить несколько набросков может быть полезно, у меня небольшие проблемы с визуализацией того, что вы хотите.   -  person Jeffrey    schedule 29.08.2011
comment
Ну вот. Сейчас лучше?   -  person bili    schedule 29.08.2011
comment
JPanels в областях NORTH и SOUTH могут быть изменены (уменьшение SIZE), если JPanel в CENTER области (при добавлении нового атрибута и увеличении размера)   -  person mKorbel    schedule 29.08.2011
comment
@ mKorbel: я тестирую код, который использует BoxLayout, предложенный 0verbose atm, но мне нужно немного поиграть. У меня есть два серых поля, одно между верхним и средним и одно между средним и нижним полем при использовании Box.createVerticalGlue(). Я думаю, что фотографии выше сделали плохую работу, чтобы проиллюстрировать это. Верхний блок всегда остается одного размера, ну, высота, но ширина может измениться, когда вся панель изменяется на большую. Средняя и нижняя рамки могут увеличиваться и уменьшаться.   -  person bili    schedule 29.08.2011
comment
не забудьте вызвать revalidate() + repaint() в качестве последних строк, просто чтобы предотвратить неправильный вывод в графический интерфейс, ооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо!) здесь у вас короткий исполняемый код, который показывает, что вы пробовали, если вы не просто уменьшаете свои шансы на успех (ваш плохой)   -  person mKorbel    schedule 29.08.2011


Ответы (4)


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

JPanel container = new JPanel();
container .setLayout(new BoxLayout(container , BoxLayout.Y_AXIS));

JPanel childTop = new JPanel();
JPanel childCenter = new JPanel();
JPanel childBottom = new JPanel();


childTop.setMaximumSize(...);
childBottom.setMaximumSize(...);

container.add(childTop);
container.add(Box.createVerticalGlue());
container.add(childCenter);
container.add(Box.createVerticalGlue());
container.add(childBottom);

Когда вам нужно вставить новый дочерний элемент, не забудьте вставить его в правильное положение: между одним из клеев и дочерним центром. Например:

container.add(newChild, 2) ;
person Heisenbug    schedule 28.08.2011
comment
@kleopatra: почему ты против методов setXXSize? не могли бы вы объяснить мне, что с ними не так? - person Heisenbug; 29.08.2011
comment
Конечно, я мог бы - просто: что вы сделали, чтобы узнать для себя? Как ф.и. @bendicott сделал в комментарии к моему ответу stackoverflow.com/questions/7074514/ - person kleopatra; 29.08.2011
comment
@kleopatra: хорошо... спасибо. Я прочитал ссылку. В этом случае вы используете JTable, поэтому вы использовали setRowHeight и т. д. Но если я управляю простым JLable, как я могу указать такие ограничения без методов setXXSize? Может быть, это не нужно обсуждать в комментариях, я задам вопрос после того, как прочитаю что-то еще об этом. - person Heisenbug; 29.08.2011
comment
@kleopatra: если у вас есть время и вы хотите, не могли бы вы привести пример, связанный с опубликованным кодом? как я могу добиться того же эффекта без методов setXXSize? Или мой подход совершенно неверен? - person Heisenbug; 29.08.2011
comment
с макетами ответ всегда один: используйте подходящий LayoutManager :-) - person kleopatra; 29.08.2011
comment
@0verbose: да, BoxLayout — это то, что я хотел! Спасибо! - person bili; 29.08.2011
comment
@kleopatra: извините за все вопросы, но у меня все еще есть сомнения, глядя на ваш пример: stackoverflow.com/questions/7074514/. setPreferredWidth отличается от setPreferredSize? мы можем использовать этот метод? - person Heisenbug; 31.08.2011
comment
@Overbose: технический ответ таков: TableColumn не является компонентом, поэтому setPref != setPref для компонентов :-) Однако хороший вопрос: поскольку JTable является собственным LayoutManager (вроде того, что касается размеров столбцов), столбец getXXWidth звучит как эквивалент comp.getXXSize. Но они не предназначены: а) у них нет доступа ни к компоненту рендеринга, ни к данным, поэтому они ничего не могут сделать для расчета размера б) prefSize имеет дополнительное значение того, что хочет пользователь, он обновляется таблицей когда пользователь изменяет размер столбца - person kleopatra; 31.08.2011
comment
@kleopatra: хорошо... большое спасибо и еще раз извините за потраченное впустую время :) - person Heisenbug; 31.08.2011
comment
обучение нетерпеливого ученика никогда не бывает напрасным, спасибо, что спросили :-) - person kleopatra; 31.08.2011

возможно, с помощью BorderLayout вы можете это сделать

(пример основных вещей revalidate(); + repaint(); по сравнению с pack();)

введите здесь описание изображениявведите здесь описание изображения введите здесь описание изображения

из кода

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.LineBorder;

public class AddComponentsAtRuntime {

    private JFrame f;
    private JPanel panel;
    private JCheckBox checkValidate, checkReValidate, checkRepaint, checkPack;

    public AddComponentsAtRuntime() {

        JButton b = new JButton();
        b.setBackground(Color.red);
        b.setBorder(new LineBorder(Color.black, 2));
        b.setPreferredSize(new Dimension(600, 10));
        panel = new JPanel(new GridLayout(0, 1));
        panel.add(b);
        JPanel panelnorth = new JPanel();
        panelnorth.setPreferredSize(panel.getPreferredSize());
        f = new JFrame();
        f.setLayout(new BorderLayout(5, 5));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(panelnorth,BorderLayout.NORTH);
        f.add(panel,BorderLayout.CENTER);
        f.add(getCheckBoxPanel(),BorderLayout.SOUTH);
        f.setLocation(200, 200);
        f.pack();
        f.setVisible(true);
    }

    private JPanel getCheckBoxPanel() {
        checkValidate = new JCheckBox("validate");
        checkValidate.setSelected(false);
        checkReValidate = new JCheckBox("revalidate");
        checkReValidate.setSelected(false);
        checkRepaint = new JCheckBox("repaint");
        checkRepaint.setSelected(false);
        checkPack = new JCheckBox("pack");
        checkPack.setSelected(false);
        JButton addComp = new JButton("Add New One");
        addComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                JButton b = new JButton();
                b.setBackground(Color.red);
                b.setBorder(new LineBorder(Color.black, 2));
                b.setPreferredSize(new Dimension(600, 10));
                panel.add(b);
                makeChange();
                System.out.println(" Components Count after Adds :" + panel.getComponentCount());
            }
        });
        JButton removeComp = new JButton("Remove One");
        removeComp.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                int count = panel.getComponentCount();
                if (count > 0) {
                    panel.remove(0);
                }
                makeChange();
                System.out.println(" Components Count after Removes :" + panel.getComponentCount());
            }
        });
        JPanel panel2 = new JPanel();
        panel2.add(checkValidate);
        panel2.add(checkReValidate);
        panel2.add(checkRepaint);
        panel2.add(checkPack);
        panel2.add(addComp);
        panel2.add(removeComp);
        return panel2;
    }

    private void makeChange() {
        if (checkValidate.isSelected()) {
            panel.validate();
        }
        if (checkReValidate.isSelected()) {
            panel.revalidate();
        }
        if (checkRepaint.isSelected()) {
            panel.repaint();
        }
        if (checkPack.isSelected()) {
            f.pack();
        }
    }

    public static void main(String[] args) {
        AddComponentsAtRuntime makingChanges = new AddComponentsAtRuntime();
    }
}
person mKorbel    schedule 28.08.2011

К сожалению, я не на 100% уверен, что понял вашу схему. Будет лучше, если вы прикрепите фото.

Но если Grid Layout растет не так, как вы хотите, вы можете либо использовать GridBagLayout, который может делать почти все, но немного сложнее, либо использовать комбинацию Border и других макетов.

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

Проверьте также BoxLayout.

Если вы собираетесь создавать много сложных диалоговых окон, воспользуйтесь MigLayout. Хотя существует некоторая кривая обучения, этот макет действительно может помочь вам сэкономить часы и дни.

Поскольку вы обновили свой вопрос, я могу определенно сказать, что вы можете определить BorderLayout с севером, югом и центром.

Центр будет содержать макет сетки с 2 строками. Каждая строка снова будет содержать макет границы. Макет верхней границы будет содержать другую панель на юге. Макет нижней границы будет содержать макет границы на севере.

Каждый из этих макетов будет содержать еще один макет границы с меткой на западе и текстовым полем в центре.

person AlexR    schedule 28.08.2011
comment
Я добавил в несколько фотографий. Смотрите мой ОП. Я не создаю много ящиков, а только один и пытаюсь заставить его работать хорошо! - person bili; 29.08.2011

Для этого вы можете использовать GridBagLayout. Я думаю, что то, что сработало для меня, было

  • добавить четвертую подпанель на верхнюю панель
  • установите вес для трех верхних панелей на 0
  • установите вес для нижней (новой) панели на 1
  • в зависимости от того, какую технику вы используете для рендеринга текста на подпанелях, вам может потребоваться явно указать предпочтительный размер подпанелей.
  • когда вы добавляете новые элементы в подпанели, вам нужно будет аннулировать и изменить макет всего этого.
person Burleigh Bear    schedule 28.08.2011