为什么GridBagLayout将我的组件居中而不是放在角落?

19

到目前为止,我尽量避免手动编写代码使用GridBagLayout,但这次我无法避免,并且正在阅读SUN的教程GridBagLayout。 然而,目前进展并不顺利。我认为我有所误解。
例如,我尝试以下代码(与 SUN 文章中的代码类似):

public class MainFrame extends JFrame { 


    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MainFrame frame = new MainFrame();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the frame
     */
    public MainFrame() {
        super();
        setBounds(100, 100, 500, 375);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        Container mainContainer = getContentPane();

        mainContainer.setLayout(new GridBagLayout());       

        //add label
        JLabel someLabel = new JLabel("Label 1:");
        GridBagConstraints constraints = new GridBagConstraints();

        constraints.gridx = 0;
        constraints.gridy = 0;
        //constraints.anchor = GridBagConstraints.FIRST_LINE_START;
        //constraints.weightx = 0.5;
        mainContainer.add(someLabel, constraints);      

        JTextField someText = new JTextField(30);

        constraints = new GridBagConstraints();

        constraints.gridx = 1;
        constraints.gridy = 0;
        constraints.weightx = 0.5;
        mainContainer.add(someText, constraints);

        //
    }

}

我在框架的中心将标签(label)和文本框(textfield)并排显示。
但是我希望它们出现在左上角,因为标签的gridx和gridy都是0。
即使我设置constraints.anchor = GridBagConstraints.FIRST_LINE_START;也没有改变结果。
我错了吗?
根据SUN的文章:

指定组件左上角的行和列。最左侧的列地址为gridx=0,顶部行地址为gridy=0。

5个回答

16
constraints.weighty = 1;添加到JLabel约束条件中,将constraints.anchor = GridBagConstraints.NORTHWEST;添加到TextField约束条件中。
编辑:
来自Oracle的GridBagLayout指南
引用: 较大的数字表示组件所在的行或列应获得更多空间。对于每个列,权重与该列中指定的最高weightx相关联,在每个多列组件的权重在其所在的列之间分配。类似地,每行的权重与该行内指定的最高weighty相关联。额外的空间倾向于流向最右侧的列和底部行。

如果我也将constraints.weighty=1设置为标签,并将锚定点设置为标签的FIRST_LINE_START,它就能正常工作。但是,weighty中的值1实际上代表什么意思呢? - Cratylus
2
此外,在他们的代码示例中,这行带有注释的代码出现了:c.weighty = 1.0; //请求任何额外的垂直空间 - BenCole
对于JButton5,我看到了它。但我不认为这是一个好的展示方式。无论如何,为什么是1而不是0.8呢?我不明白这些数字是如何选择的。 - Cratylus
1
说实话,我也不知道。我用MiGLayout做所有的事情...http://www.miglayout.com/ - BenCole
记录一下:如果生成的布局在水平方向上比需要填充的区域小,额外的空间将按照其权重比例分配给每个列。 权重为零的列不会获得额外的空间。weightxweighty的实际数值只与彼此相关。 - Ti Strga

14
你需要继续阅读Swing教程,查看关于weightX/weightY部分的内容,其中有如下说明: 除非你至少指定一个非零的 weightx 或 weighty 值,否则所有组件都会聚集在它们容器的中心。 你只指定了 weightX 但没有指定 weightY。
编辑,情况比我想象的更加复杂。似乎你还需要指定以下内容:
constraints.anchor = GridBagConstraints.FIRST_LINE_START;

对于两个组件,除了重要性之外。


SUN的引用说weightx或weighty(所以我没想到需要两个都设置)。无论如何,我尝试了对于标签weighty=1,对于文本weighty=0.5,对于两者都是weighty=0.5,但结果相同。 - Cratylus
weightx 用于水平定位。 weighty 用于垂直定位。我同时使用了 1.0 并且将 FIRST_LINE_START 应用到了两者上,这对我起作用了。 - camickr
是的,现在它可以工作了。但是weighty中的值1实际上是什么意思呢?我不明白。另外,通过需要锚点,我觉得网格已经存在,而不是通过将组件放在上面来构建它。对吗? - Cratylus
GridBagLayout会按照组件的首选大小显示它们。当有额外的空间时,weightX/Y值是布局管理器如何分配这些空间的提示。EON的提示有效是因为他添加了第二行组件,并使用weighty = 1.0要求该行占用所有额外空间。在您的情况下,您只有一行,因此需要指定其中一个组件占用所有可用的额外空间。 - camickr
好的,但是我只对标签使用了weighty属性。所以标签会获得额外的垂直空间(用户可能看不到)。但为什么这也会影响文本框并使其上移呢? - Cratylus
引用关于在中心聚集的那一部分,再加一分。起初我不小心忽略了这一部分,对于一个包含单个项目且没有保持其位置的面板感到困惑。并不是每天都会在面板内使用单个组件。 - klaar

3
您可以使用一个技巧来实现这一点,即在行后添加一个虚拟组件并将其展开以填充垂直空间。此外,您可以重复使用约束条件,无需创建新对象:
编辑:好吧,忘了那个技巧:( 正确的方法是像Deon Botha和BenCole说的那样,我已经使用锚更新了我的代码。
不要接受这个答案,而是接受Deon或Ben的答案。
public class MainFrame extends JFrame { 
    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MainFrame frame = new MainFrame();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

public MainFrame() {
    super();
    setBounds(100, 100, 500, 375);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    Container mainContainer = getContentPane();
    mainContainer.setLayout(new GridBagLayout());       

    JLabel someLabel = new JLabel("Label 1:");
    JTextField someText = new JTextField(30);

    GridBagConstraints constraints = new GridBagConstraints();
    constraints.anchor = GridBagConstraints.FIRST_LINE_START;

    constraints.gridx = 0;
    constraints.gridy = 0;    
    constraints.weightx = 1.0;
    mainContainer.add(someLabel, constraints);      

    constraints.gridx = 1;                       
    constraints.weightx = 1.0;
    constraints.weighty = 1.0;        
    mainContainer.add(someText, constraints);                       
}
}

但是我为什么需要这个虚拟组件呢?组件应该由布局管理器放置在左上角吗?gridx=gridy=0的含义是什么? - Cratylus
你说得对,“应该可以工作”,但是GridBayLayout很难搞,其实它非常好……只是有点棘手。我不是那种纯粹主义者,如果用一个虚拟标签就能解决问题,为什么不用呢? - user481572
2
或者你可以设置 constraints.weighty = 1.0; constraints.anchor = GridBagConstraints.FIRST_LINE_START; - dbotha

1
这对我有用:

这对我有用:

public class NewJFrame extends javax.swing.JFrame {

    public NewJFrame() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        jPanel2 = new javax.swing.JPanel();
        jComboBox3 = new javax.swing.JComboBox();
        jComboBox4 = new javax.swing.JComboBox();
        jComboBox5 = new javax.swing.JComboBox();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setBackground(new java.awt.Color(255, 204, 51));
        setMinimumSize(new java.awt.Dimension(800, 600));
        getContentPane().setLayout(null);

        jPanel2.setLayout(new java.awt.GridBagLayout());

        jComboBox3.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 1.0;
        jPanel2.add(jComboBox3, gridBagConstraints);

        jComboBox4.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 1.0;
        jPanel2.add(jComboBox4, gridBagConstraints);

        jComboBox5.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        jPanel2.add(jComboBox5, gridBagConstraints);

        getContentPane().add(jPanel2);
        jPanel2.setBounds(30, 40, 150, 260);

        pack();
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new NewJFrame().setVisible(true);
            }
        });
    }

    private javax.swing.JComboBox jComboBox3;
    private javax.swing.JComboBox jComboBox4;
    private javax.swing.JComboBox jComboBox5;
    private javax.swing.JPanel jPanel2;
}

1

我可能没有直接回答你的问题,但相信我,你应该在一个集成开发环境(IDE)中进行布局的尝试和错误。我个人推荐使用Netbeans。在那里,你可以拖放组件然后查看属性。一开始,属性检查器会给出一些默认值,因此生成的代码较少。但是,当你开始调整布局时,你可以看到代码并且对你所做的事情有一个良好的理解。


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