Java哪种布局管理器适合这个任务?

4
我有一个JPanel父面板,内部有3个JPanel子面板。它们目前都使用GridLayout,并共同表示一个UML类。问题在于,当我添加新属性或方法时,所有3个面板的大小都会增加。
期望的行为是:
1. 标题框的大小始终保持不变,无论何时添加方法或属性。 2. 添加方法时,仅方法面板增大,标题和属性面板的大小保持不变。 3. 添加属性时,仅属性面板增大,其他面板的大小保持不变。
父面板已经可以在添加方法/属性时自动增长/缩小。我正在尝试使用GridBagLayout,但是远未达到期望的结果。
是否有更简单的方法解决这个问题?以下是几张图片展示我的情况。
新创建的UML类 => enter image description here 当前的行为 => enter image description here 但我想要这样 => enter image description here 或这样=>enter image description here 或这样 =>enter image description here 编辑2:为了更清晰,添加了新图片。如果原始版本有误导性,我非常抱歉。
编辑3:太好了!我解决了这个问题! SSEEC如下:
    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代码建议并尝试调整它时,它失败了!直到我完成SSEEC,进行最终编译和运行之前(实际上在这一点上我已经放弃了),我才意识到它实际上可以工作...这个面板可以在自己的空间内增长,它们不会互相干扰。

我就像是怎么回事啊!

回到我的代码中,将其与SSEEC进行比较,发现有一个bug,面板高度扩展的代码位置错了,因此它们会相互侵占空间。

更好的是!我可以通过一个像素指定中间框与上下框之间的距离。这意味着我仍然可以使用mKorbel的技巧来绘制分隔这些框的背景线!

编辑4:有没有办法设置组件的大小?如果你运行SSEEC,你会注意到一旦添加JTextField,它就非常巨大!它比容器还要大...


加入一些草图可能会很有用,我有点难以想象你想要什么。 - Jeffrey
好的,这样可以了吗?现在好一些了吗? - bili
如果中心区域的JPanel(在新属性添加并增加大小)发生变化,NORTH和SOUTH区域的JPanel可以调整大小(减小大小)。 - mKorbel
@mKorbel:我正在测试使用BoxLayout的代码,目前是由0verbose建议的,但需要再玩一下。当使用Box.createVerticalGlue()时,我得到了两个灰色框,一个在顶部和中间之间,另一个在中间和底部之间。我认为上面的图片没有很好地说明它。顶部框始终保持相同的大小,也就是高度,但整个面板调整为更大的面板时,宽度可以改变。中间和底部框可以增长和缩小。 - bili
不要忘记在最后一行调用revalidate() + repaint(),以防止GUI输出错误,如果你已经调用了pack(),那么这步操作可能是不必要的,但是为了更好的答案,在所有情况下,您都需要发布一个简短的可运行代码示例,以展示您所尝试的内容。如果没有这样做,您只会降低成功的机会(这是您的问题)。 - mKorbel
4个回答

6

我建议您使用BoxLayout。这里有一个教程。通过使用glue和rigid area,您几乎可以获得所有想要的布局。在您的情况下,代码应该是这样的:

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);

当您需要插入新的子项时,请记得将其插入到正确的位置:在一个粘合剂和childCenter之间。 例如:
container.add(newChild, 2) ;

@kleopatra:为什么你反对使用setXXSize方法?你能解释一下它们有什么问题吗? - Heisenbug
当然可以 - 只是:你自己是怎么找到答案的呢?例如,@bendicott 在我的回答下留言中所做的那样 http://stackoverflow.com/questions/7074514/images-in-jtable-cells-off-by-one-pixel - kleopatra
@kleopatra:如果您有时间并且愿意,能否制作一个与发布的代码相关的示例?我如何在不使用setXXSize方法的情况下实现相同的效果?或者我的方法完全错误? - Heisenbug
使用布局管理器就可以解决布局问题啦 :-) - kleopatra
@kleopatra:好的,非常感谢你,再次为我浪费的时间道歉 :) - Heisenbug
显示剩余5条评论

4
也许通过使用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();
    }
}

3

很抱歉,我不能百分之百确定我理解了你的布局。如果您能附上图片,那会更好。

但是,如果网格布局不符合您的要求,您可以使用GridBagLayout,它几乎可以做任何事情,但有点复杂,或者使用边框和其他布局的组合。

可能需要使用几个边框布局来帮助您完成任务。在这种布局中,中心区域增长速度快,而北、南、西和东部区域增长速度较慢。请尝试使用它们。

还可以尝试CheckBoxLayout。

如果您要创建许多复杂的对话框,请查看MigLayout。虽然需要一些学习曲线,但此布局确实可以帮助您节省时间。

由于您更新了问题,现在我可以肯定地说,您可以定义BorderLayout,其中包括North、South和Center。

中心区域将包含具有2行的网格布局。每一行都将再次包含一个边框布局。上面的边框布局将在南侧包含其他面板。下面的边框布局将在North中包含边框布局。

每个布局都将包含另一个边框布局,其中西侧有标签,中心有文本字段。


我已经添加了一些图片。请查看我的原帖。我不是在创建很多框,只是一个并尝试让它表现良好! - bili

1
你可以使用GridBagLayout来实现。我想对我有用的是:
  • 在顶部面板中添加第四个子面板
  • 将前三个面板的weighty设置为0
  • 将底部(新)面板的weighty设置为1
  • 根据您在子面板中呈现文本的技术,您可能需要显式设置子面板的首选大小。
  • 当您向子面板添加新项目时,您需要使整个内容无效并重新布局。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接