如何使用BoxLayout在容器内设置组件大小

11

我在使用BoxLayout时遇到了问题。

在我的示例中,我试图减小文本字段的高度并更改按钮的宽度(如下图底部的绿色标记所示)。我知道有关技术setPreferredSize()setMaximumSize()的信息,但它没有像应该那样起作用。行add(Box.createHorizontalGlue())也没有帮助。

感谢任何想法。


public class Testy extends JPanel {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                constructGUI();
            }
        });
    }

    private static void constructGUI() {
        JFrame frame = new JFrame("Testy");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.DARK_GRAY);
        centerPanel.setPreferredSize(new Dimension(100, 400));
        frame.add(centerPanel, BorderLayout.CENTER);

        Testy eastPanel = new Testy();
        frame.add(eastPanel, BorderLayout.EAST);

        frame.pack();
        frame.setVisible(true);
    }

    public Testy() {
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        JButton button = new JButton("Button ...... 1");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        button = new JButton("Button 2");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        button = new JButton("Button ........... 3");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        JLabel label = new JLabel("Label");
        //label.setPreferredSize(...);
        //label.setMaximumSize(...);
        add(label);

        JTextField textField = new JTextField();
        //textField.setPreferredSize(...);
        //textField.setMaximumSize(...);
        add(textField);

        button = new JButton("Button 4");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        add(button);

        //add(Box.createHorizontalGlue());
    }
}

图片


2
作为一个快速的解决方案,您可以使用嵌套布局。在右侧,创建一个带有BorderLayoutJPanel,将一个JPanel(称为centerPanel)放置在CENTER位置,将一个JPanel(称为buttonPanel)放置在PAGE_END位置。现在,使用一个带有GridLayout的新JPanel(称为compPanel),并将所有组件放置在其中,然后将此compPanel放置在centerPanel中。将JButton(button4)直接放置在buttonPanel中。我想这会起作用 :-) - nIcE cOw
2
为了更快地获得帮助,请发布一个SSCCE。 SSCCE应包括导入,类定义和main方法。 - Andrew Thompson
3个回答

30

首先需要认识到,在Java Swing中组件的位置和大小取决于布局管理器(如果设置了布局管理器),而不是组件本身。组件向管理器请求大小。

对于这种情况,我会使用不同的布局方式——组合GridLayout和BorderLayout就足够简单明了了。但是如果想要使用BoxLayout,则...

  1. 文档说:

    BoxLayout会关注组件请求的最小、首选和最大大小。 在微调布局时,您可能需要调整这些大小。...例如,按钮的最大大小通常与其首选大小相同。如果希望在有额外空间时将按钮绘制得更宽,则需要更改其最大大小。

  2. 然后设置组件的最大大小:c.setMaximumSize(new Dimension(Integer.MAX_VALUE, c.getMinimumSize().height)); (在您的示例中,c指代buttonlabeltextField

编辑1:

以下是可工作的源代码:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

public class Testy extends JPanel {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                constructGUI();
            }
        });
    }

    private static void constructGUI() {
        JFrame frame = new JFrame("Testy");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        JPanel centerPanel = new JPanel();
        centerPanel.setBackground(Color.DARK_GRAY);
        centerPanel.setPreferredSize(new Dimension(100, 400));
        frame.add(centerPanel, BorderLayout.CENTER);

        Testy eastPanel = new Testy();
        frame.add(eastPanel, BorderLayout.EAST);

        frame.pack();
        frame.setVisible(true);
    }

    public Testy() {
        setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));

        JButton button = new JButton("Button ...... 1");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        button = new JButton("Button 2");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        button = new JButton("Button ........... 3");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        JLabel label = new JLabel("Label");
        //label.setPreferredSize(...);
        label.setMaximumSize(new Dimension(Integer.MAX_VALUE, label.getMinimumSize().height));
        add(label);

        JTextField textField = new JTextField();
        //textField.setPreferredSize(...);
        textField.setMaximumSize(new Dimension(Integer.MAX_VALUE, textField.getMinimumSize().height));
        add(textField);

        button = new JButton("Button 4");
        //button.setPreferredSize(...);
        button.setMaximumSize(new Dimension(Integer.MAX_VALUE, button.getMinimumSize().height));
        add(button);

        // add(Box.createVerticalGlue());
    }
}

屏幕截图

编辑2:

如果你想将按钮4放在右列底部,请在add(textField);button = new JButton("Button 4");之间添加以下代码:add(Box.createVerticalGlue());


我喜欢这个更新,虽然早在此之前就已经点赞了,以抵消其他人的踩,因为没有解释原因。 - nIcE cOw
我肯定需要一个盒式布局,你的示例按照预期工作。感谢澄清。 - Dumas45
@Dumas45 我确实需要一个布局管理器 - 为什么?顺便说一句,setXXSize(这是一个绝对不要用的方法)-1。 - kleopatra
@kleopatra 布局管理器是否适用于XXSizes取决于布局管理器的实现。如果您使用BoxLayout,则必须使用setXXSize(或者请给我一个没有它的示例)。如果设置了maxSize,它将用于BoxLayout。JButton默认将maxSize设置为preferredSize,然后如果我想要将按钮扩展到可用区域,则必须将其更改为MAX_VALUE。按钮的高度设置为某个常量值(minSize.height),因为我不希望将其扩展到可用空间。 - Boris Šuška
JButton默认情况下将maxSize设置为preferredSize是不正确的:它会将其preferredSize作为max返回(而不是设置)。这种差异可能看起来很小,但具有深远的影响:前者是动态的,后者是静态的,并且硬编码了当前preferred height。一旦preferred height发生变化,例如通过设置不同的字体,这将是错误的。调整xxSizes的唯一安全方法是子类化并覆盖getter。或者:使用更好的LayoutManager,在其自身级别上进行微调即可;-) - kleopatra
感谢您的澄清。我在这里找到了一个子类示例(http://docs.oracle.com/javase/tutorial/uiswing/layout/box.html)。这需要更多的工作。 - Boris Šuška

4
作为一种快速的解决方法,您可以使用嵌套布局。在右侧,创建一个带有BorderLayout的JPanel,并将JPanel(称为compPanel)置于CENTER位置,将JPanel(称为buttonPanel)置于PAGE_END位置。现在,使用一个新的JPanel(称为panel),并在其上放置所有组件,然后将此compPanel放置在centerPanel中。按原样将JButton(button4)放入buttonPanel中。
相反,BoxLayout尊重给定JComponent的首选大小,这通常是基于JComponent所持有的内容或显式给定的计算,因此组件不趋向于与其他给定组件对齐。
这是一个可行的示例:
import java.awt.*;
import javax.swing.*;

public class Testy extends JPanel {        

    private JPanel panel;
    private JPanel buttonPanel;

    public Testy() {
        setLayout(new BorderLayout(5, 5));

        JPanel compPanel = new JPanel();
        panel = new JPanel(new GridLayout(6, 1, 5, 5));     
        JButton button = new JButton("Button ...... 1");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        panel.add(button);

        button = new JButton("Button 2");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        panel.add(button);

        button = new JButton("Button ........... 3");
        //button.setPreferredSize(...);
        //button.setMaximumSize(...);
        panel.add(button);

        JLabel label = new JLabel("Label");
        //label.setPreferredSize(...);
        //label.setMaximumSize(...);
        panel.add(label);

        JTextField textField = new JTextField();
        //textField.setPreferredSize(...);
        //textField.setMaximumSize(...);
        panel.add(textField);
        compPanel.add(panel);

        buttonPanel = new JPanel();
        button = new JButton("Button 4");
        buttonPanel.add(button);

        add(compPanel, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.PAGE_END);
    }

    private void constructGUI() {
        JFrame frame = new JFrame("Testy");
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);

        JPanel centerPanel = new JPanel();
        frame.getContentPane().setLayout(new BorderLayout(5, 5));
        centerPanel.setBackground(Color.DARK_GRAY);
        frame.add(centerPanel, BorderLayout.CENTER);

        frame.add(this, BorderLayout.LINE_END);

        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Testy().constructGUI();
            }
        });
    }
}

输出:

布局示例


2
+1 如果您不需要使用BoxLayout,这是最佳解决方案。 - Boris Šuška

1
根据您的绘图,这应该接近了,只需要处理JLabel下方的组件(使用setPreferredSize())。
JPanel main = new JPanel(new GridLayout(1, 2));

JPanel left = new JPanel();
//left.setPreferredSize(some size);
JPanel right = new JPanel(new GridLayout(6, 1));
//right.setPreferredSize(some size);

right.add(new JButton("Button 1"));
//...
right.add(new JButton("Button 4"));

main.add(left);
main.add(right);

2
最好使用new GridLayout(0, 1),它表示任意数量的行但只有一列 - 这样更有意义。无论如何,我不知道当我添加超过6个组件时会发生什么。 - Boris Šuška

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