Java Swing GridBagLayout不符合预期

q0qdq0h2  于 2023-05-21  发布在  Java
关注(0)|答案(5)|浏览(132)

在我简单的SwingApp中,我无法让gridwidth在GridBagLayout中工作。
我在Java Swing中有这样的代码:

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.Border;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;

class GridBagLayt extends JFrame {
 
    GridBagLayt()
    {
        JButton jb1 = new JButton("1");
        JButton jb2 = new JButton("2");
        JButton jb3 = new JButton("3");
        JButton jb4 = new JButton("4");
        JButton jb5 = new JButton("5");
        
        GridBagLayout lay = new GridBagLayout();
        GridBagConstraints cons = new GridBagConstraints();
        
        setLayout(lay);
        cons.fill = GridBagConstraints.BOTH;
        cons.gridheight = 1;
        cons.weightx = 1;
        cons.weighty = 1;

        cons.gridx = 0; cons.gridy = 0; 
        cons.gridwidth = 2;
        lay.setConstraints(jb1, cons);
        add(jb1);
        
        cons.gridx = 2; 
        lay.setConstraints(jb2, cons);
        add(jb2);
         
        cons.gridx = 0; cons.gridy = 1; 
        cons.gridwidth = 1;
        lay.setConstraints(jb3, cons);
        add(jb3);
        
        cons.gridx = 1;
        cons.gridwidth = 2;
        lay.setConstraints(jb4, cons);
        add(jb4);
        
        cons.gridx = 3;
        cons.gridwidth = 1;
        lay.setConstraints(jb5, cons);
        add(jb5);
        
        
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(700,200);
        setVisible(true);
    }
  }

    public class SwingApp {
 
        public static void main(String[] args) {
     
            new GridBagLayt();
        }
    }

这就是我想要的:

但我却得到了这个:

我真的不知道问题出在哪里,坐标和网格宽度对我来说似乎没问题,我希望这能起作用,但事实并非如此。如果我在这两行之间再添加四个按钮,那么第三行似乎可以,但我不希望这样:
示例:如果我添加此代码

cons.gridx = 0; cons.gridy = 0; 
        cons.gridwidth = 2;
        lay.setConstraints(jb1, cons);
        add(jb1);
        
        cons.gridx = 2; 
        lay.setConstraints(jb2, cons);
        add(jb2);
        
        
        cons.gridx = 0; cons.gridy = 1; 
        cons.gridwidth = 1;
        lay.setConstraints(jb3, cons);
        add(jb3);
        
        cons.gridx = 1;
        lay.setConstraints(jb4, cons);
        add(jb4);
        
        cons.gridx = 2;
        lay.setConstraints(jb5, cons);
        add(jb5);
      
        cons.gridx = 3;
        lay.setConstraints(jb6, cons);
        add(jb6);
        
        cons.gridx = 0; cons.gridy = 2; 
        lay.setConstraints(jb7, cons);
        add(jb7);
        
        
        cons.gridx = 1; cons.gridy = 2; 
        cons.gridwidth = 2;
        lay.setConstraints(jb8, cons);
        add(jb8);
        
        cons.gridx = 3; cons.gridy = 2; 
        cons.gridwidth = 1;
        lay.setConstraints(jb9, cons);
        add(jb9);

然后我得到了这个:

但我不想第二排

oxcyiej7

oxcyiej71#

我会为每一行使用一个额外的JPanel,并且不会扩展JFrame(除非需要,即向其添加功能):

public class SwingApp2 {
    
    public static void main(String[] args) {
        SwingUtilities.invokeLater(SwingApp2::new);
    }

    private SwingApp2()
    {
        var jb1 = new JButton("1");
        var jb2 = new JButton("2");
        var jb3 = new JButton("3");
        var jb4 = new JButton("4");
        var jb5 = new JButton("5");

        var line1 = new JPanel(new GridBagLayout());
        var gbc = new GridBagConstraints(RELATIVE, 0, 1, 1, 1.0, 1.0, CENTER, BOTH, new Insets(0, 0, 0, 0), 0, 0);
        line1.add(jb1, gbc);
        line1.add(jb2, gbc);

        var line2 = new JPanel(new GridBagLayout());
        line2.add(jb3, gbc);
        gbc.weightx *= 2;
        line2.add(jb4, gbc);
        gbc.weightx /= 2;
        line2.add(jb5, gbc);

        var frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        // using GridLayout so the lines are same height
        frame.setLayout(new GridLayout(0, 1));
        frame.add(line1);
        frame.add(line2);
        frame.setSize(700,200);
        frame.setVisible(true);
    }
}

结果:

只有一种方法可以做到这一点,非常依赖于如何调整组件的大小,如果图形用户界面的大小改变
还要注意,大小将根据首选大小(即标签内容)而变化

mec1mxoz

mec1mxoz2#

这个GUI很难实现的原因是GridBagLayout没有任何东西可以用来调整四列的大小
GridBagLayout确实有一些属性,允许您为没有组件的列定义默认值,如下所示:

import java.util.*;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class GridBagLayoutTest3 implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new GridBagLayoutTest3());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(createPanels(), BorderLayout.CENTER);

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

    private JPanel createPanels() {

        //  Create the GridBagLayout and GridBagConstraints to be used by the panel

        GridBagLayout gbl = new GridBagLayout();
        JPanel panel = new JPanel(gbl);

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 5, 5, 5);

        //  Create and add components to the panel

        JButton button1 = new JButton("Button 1");
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 2;
        panel.add(button1, gbc);

        gbc.gridx += 2;
        JButton button2 = new JButton("Button 2");
        panel.add(button2, gbc);

        gbc.gridwidth = 1;
        gbc.gridx = 0;
        gbc.gridy++;
        JButton button3 = new JButton("Button 3");
        panel.add(button3, gbc);

        gbc.gridwidth = 2;
        gbc.gridx += 1;
        JButton button4 = new JButton("Button 4");
        panel.add(button4, gbc);

        gbc.gridwidth = 1;
        gbc.gridx += 2;
        JButton button5 = new JButton("Button 5");
        panel.add(button5, gbc);

        //  Set properties of the GridBagLayout for columns without components in a single column

        int[] columns = new int[4];
        Arrays.fill(columns, button1.getPreferredSize().width);
        gbl.columnWidths = columns;
        double[] weights = new double[4];
        Arrays.fill(weights, 1.0);
        gbl.columnWeights = weights;

        return panel;
    }

}

注:您还可以查看:https://stackoverflow.com/a/57463596/131872,其中有一个更复杂的例子,展示了如何将这种方法用于按钮的“键盘”布局。

q1qsirdb

q1qsirdb3#

GridBagLayout关注的是对齐,而不是比例。有时它可以用来布置大小相等的组件,但它并不擅长于此。
然而,SpringLayout在这方面做得很好,并且有专门的方法来做这件事。代价是SpringLayout很难使用,而且很容易被错误地使用。它的工作原理类似于20世纪90年代的“表单布局”。对于布局中的每个组件,您必须声明至少一个水平边缘和一个垂直边缘应该位于相对于容器边缘或相对于其他子组件边缘的位置。
首先,最好创建容器,而不是扩展JFrame。子类化JFrame是没有意义的,因为你只是在使用JFrame,而不是改变它的行为:

SpringLayout layout = new SpringLayout();
JPanel panel = new JPanel(layout);

接下来,让我们计算小按钮的一致大小:

JButton[] buttons = { jb1, jb2, jb3, jb4, jb5 };

Spring width = Spring.constant(0);
Spring height = Spring.constant(0);
for (JButton button : buttons) {
    width = Spring.max(width, Spring.width(button));
    height = Spring.max(height, Spring.height(button));
}

接下来,让我们将按钮1、2和4的宽度设置为两倍:

Spring largeButtonWidth = Spring.scale(width, 2);

现在我们可以使用这些Spring为每个按钮创建constraints

Spring zero = Spring.constant(0);

panel.add(jb1, new SpringLayout.Constraints(
    zero, zero, largeButtonWidth, height));

panel.add(jb2, new SpringLayout.Constraints(
    largeButtonWidth, zero, largeButtonWidth, height));

panel.add(jb3, new SpringLayout.Constraints(
    zero, height, width, height));

panel.add(jb4, new SpringLayout.Constraints(
    width, height, largeButtonWidth, height));

panel.add(jb5, new SpringLayout.Constraints(
    Spring.sum(width, largeButtonWidth), height, width, height));

最后,我们需要告诉布局容器本身应该有多大。它需要足够宽以容纳jb2和jb5,并且足够高以容纳jb3、jb4和jb5。我们可以使用其中任何一个来指定容器的首选大小,但jb5可能是最简单的选择:

layout.putConstraint(
    SpringLayout.EAST, panel, 0, SpringLayout.EAST, jb5);
layout.putConstraint(
    SpringLayout.SOUTH, panel, 0, SpringLayout.SOUTH, jb5);

把所有这些放在一起看起来像这样:

import java.awt.EventQueue;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.Spring;
import javax.swing.SpringLayout;

public class RelativeButtonSizesDemo {
    private final JFrame frame;

    public RelativeButtonSizesDemo() {
        JButton jb1 = new JButton("1");
        JButton jb2 = new JButton("2");
        JButton jb3 = new JButton("3");
        JButton jb4 = new JButton("4");
        JButton jb5 = new JButton("5");

        JButton[] buttons = { jb1, jb2, jb3, jb4, jb5 };

        Spring width = Spring.constant(0);
        Spring height = Spring.constant(0);
        for (JButton button : buttons) {
            width = Spring.max(width, Spring.width(button));
            height = Spring.max(height, Spring.height(button));
        }

        // jb1, jb2, and jb4 will be twice as wide as the other buttons.
        Spring largeButtonWidth = Spring.scale(width, 2);

        SpringLayout layout = new SpringLayout();
        JPanel panel = new JPanel(layout);

        Spring zero = Spring.constant(0);

        panel.add(jb1, new SpringLayout.Constraints(
            zero, zero, largeButtonWidth, height));

        panel.add(jb2, new SpringLayout.Constraints(
            largeButtonWidth, zero, largeButtonWidth, height));

        panel.add(jb3, new SpringLayout.Constraints(
            zero, height, width, height));

        panel.add(jb4, new SpringLayout.Constraints(
            width, height, largeButtonWidth, height));

        panel.add(jb5, new SpringLayout.Constraints(
            Spring.sum(width, largeButtonWidth), height, width, height));

        // Set preferred size of panel itself to accommodate buttons.
        layout.putConstraint(
            SpringLayout.EAST, panel, 0, SpringLayout.EAST, jb5);
        layout.putConstraint(
            SpringLayout.SOUTH, panel, 0, SpringLayout.SOUTH, jb5);

        frame = new JFrame("Relative Button Sizes Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
    }

    public void show() {
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> new RelativeButtonSizesDemo().show());
    }
}
oiopk7p5

oiopk7p54#

任何合适的复杂UI/布局都应该拆分为多个组件。这通常称为“复合布局”。
这使您能够更好地专注于布局的每个“区域”的单独需求。
有时候,你只需要假装。

  • 请注意,我使用标签而不是按钮仅作为演示,因为MacOs按钮周围有很多空间,这更好地说明了GridBagConstraints#insets的使用 *
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

public class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.weightx = 1;
            gbc.fill = gbc.HORIZONTAL;
            gbc.insets = new Insets(4, 0, 4, 0);
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            add(makeFirstRow(), gbc);
            add(makeSecondRow(), gbc);
            add(makeThirdRow(), gbc);
        }

        protected JLabel makeLabel(String text) {
            JLabel label = new JLabel(text);
            label.setHorizontalAlignment(JLabel.CENTER);
            label.setOpaque(true);
            label.setBackground(Color.RED);
            label.setBorder(new EmptyBorder(8, 32, 8, 32));
            return label;
        }

        protected JPanel makeFirstRow() {
            JPanel rowPane = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.weightx = 1;
            gbc.fill = gbc.HORIZONTAL;
            gbc.insets = new Insets(0, 4, 0, 4);

            rowPane.add(makeLabel("1"), gbc);
            rowPane.add(makeLabel("2"), gbc);

            return rowPane;
        }

        protected JPanel makeSecondRow() {
            JPanel rowPane = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.weightx = 1;
            gbc.fill = gbc.HORIZONTAL;
            gbc.insets = new Insets(0, 4, 0, 4);

            rowPane.add(makeLabel("3"), gbc);
            rowPane.add(makeLabel("4"), gbc);
            rowPane.add(makeLabel("5"), gbc);

            return rowPane;
        }

        protected JPanel makeThirdRow() {
            JPanel rowPane = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.weightx = 1;
            gbc.fill = gbc.HORIZONTAL;
            gbc.insets = new Insets(0, 4, 0, 4);

            rowPane.add(makeLabel("6"), gbc);
            rowPane.add(makeLabel("7"), gbc);
            rowPane.add(makeLabel("8"), gbc);

            return rowPane;
        }
    }

}

现在,我不知道你怎么想,但我很想创建某种工厂方法,它会根据每行中所需的元素数量创建每行...但那就是我😉
但布局有点不同按钮4(和布局中的7)应该与按钮1和2大小相同,但在您的情况下,它们是1和2大小的2/3,按钮3和5应该是1和2的1/2,这里是2/3
然后相应地更改列weightx属性,例如...

protected JPanel makeSecondRow() {
    JPanel rowPane = new JPanel(new GridBagLayout());
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.fill = gbc.HORIZONTAL;
    gbc.insets = new Insets(0, 4, 0, 4);

    gbc.weightx = 0.25;
    rowPane.add(makeLabel("3"), gbc);
    gbc.weightx = 0.5;
    // This is simply adding an addition padding to the
    // labels preferred size, otherwise all three buttons
    // can be layed out at there preferred size with the
    // avaliable space
    gbc.ipadx = 64;
    rowPane.add(makeLabel("4"), gbc);
    gbc.weightx = 0.25;
    gbc.ipadx = 0;
    rowPane.add(makeLabel("5"), gbc);

    return rowPane;
}

现在,请记住,组件自己的首选大小发挥这一点。如果没有额外的ipadx,所有三个元素都可以在可用的容器区域中按其首选大小进行布局。
而且,不,你不“需要”为每一列指定weightx,这只是一个演示,但有时,你可能会发现在你的愿望中绝对更有用。

3hvapo4f

3hvapo4f5#

我终于得到了这个图形用户界面的工作,但我不得不使用两个黑客,我不建议使用。

Oracle有一个有用的教程Creating a GUI With Swing。跳过使用NetBeans IDE学习Swing部分。
这个GUI很难实现的原因是GridBagLayout没有用于调整四列大小的东西。因此,借用OP,我在创建JButtons行之前创建了一行空的JLabels。我根据JButton的大小调整了JLabels的大小。
在尝试之前我并不确定,但是gridheight为零会使JLabelsJButtons的第一行保持在相同的物理位置。
下面是完整的、丑陋的、可运行的代码。

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class GridBagLayoutTest2 implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new GridBagLayoutTest2());
    }

    @Override
    public void run() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(createPanels(), BorderLayout.CENTER);

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

    private JPanel createPanels() {
        JPanel panel = new JPanel(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.LINE_START;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 5, 5, 5);
        gbc.weightx = 0.0;
        
        JButton button1 = new JButton("Button 1");
        Dimension d = button1.getPreferredSize();
        
        gbc.gridheight = 0;
        gbc.gridwidth = 1;
        gbc.gridx = 0;
        gbc.gridy = 0;
        for (int index = 0; index < 4; index++) {
            JLabel label = new JLabel();
            label.setPreferredSize(d);
            panel.add(label, gbc);
            gbc.gridx++;
        }

        gbc.gridheight = 1;
        gbc.gridwidth = 2;
        gbc.gridx = 0;
        gbc.gridy++;
        gbc.weightx = 1.0;
        panel.add(button1, gbc);
        
        gbc.gridx += 2;
        JButton button2 = new JButton("Button 2");
        panel.add(button2, gbc);
        
        gbc.gridwidth = 1;
        gbc.gridx = 0;
        gbc.gridy++;
        JButton button3 = new JButton("Button 3");
        panel.add(button3, gbc);
        
        gbc.gridwidth = 2;
        gbc.gridx += 1;
        JButton button4 = new JButton("Button 4");
        panel.add(button4, gbc);
        
        gbc.gridwidth = 1;
        gbc.gridx += 2;
        JButton button5 = new JButton("Button 5");
        panel.add(button5, gbc);

        return panel;
    }

}

相关问题