如何在 GridBagLayout 中垂直对齐不同大小的按钮?

How to align vertically buttons of different sizes within a GridBagLayout?

我是从swing开始的,我有一些关于如何在GridBagLayout中对齐元素的问题,我也不确定这是否是正确的方法,请指教。

我有以下代码

import javax.swing.*;
import java.awt.*;

public class App {
    public void start() {
        JPanel mainPanel = new JPanel(new FlowLayout());
        mainPanel.setBorder(BorderFactory.createLineBorder(Color.CYAN, 20));

        //buttons for initial options
        JButton button1 = new JButton("This is option A");
        JButton button2 = new JButton("option B");
        JButton button3 = new JButton("Another text");

        JPanel second = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.CENTER;
        second.setBackground(Color.GREEN);
        second.add(button1, gbc);
        second.add(button2, gbc);
        second.add(button3, gbc);

        mainPanel.add(second, BorderLayout.CENTER);

        //frame configuration
        JFrame frame = new JFrame();
        frame.setContentPane(mainPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 600);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setResizable(false);
    }

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
        SwingUtilities.invokeLater(() -> new App().start());
    }
}

我的目标是生成以下输出:

到目前为止,我已经尝试使用垂直对齐的 BoxLayout 并且它有效,但问题是它覆盖了按钮的首选大小,我希望它们都具有相同的宽度。

此外,我尝试使用 GridLayout 和 BorderLayout 将元素添加到 NORTH、CENTER 和 SOUTH,但按钮的大小发生了变化。

将元素居中但保持其尺寸的推荐方法是什么?

我会嵌套布局:

  1. 一个包含按钮并使用 new GridLayout(0, 1, 0, vGap) 的 JPanel -- 包含一列和可变行数的网格,按钮之间有一个 vGap 间隙。
  2. 将该 JPanel 添加到另一个使用 GridBagLayout 的 JPanel 中,并以默认方式(无 GridBagConstraints)添加它,这将使第一个 JPanel 居中到第二个 JPanel 中。这显然必须以某种方式达到所需的大小。这可以通过
  • 以合理的方式覆盖getPreferredSize()
  • 调用 setPreferredSize(new Dimension(someWidth, someHeight)) -- 这不是很“干净”
  • 为其添加边框,特别是 BorderFactor.EmptyBorder(gap, gap, gap, gap),其中间隙是 JPanel 周围边框的大小...

完成。

使用 GridBagLayout 的测试代码:

import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import javax.swing.*;

public class ButtonLayout extends JPanel {
    public static final int MY_WIDTH = 750;
    public static final int MY_HEIGHT = 500;
    private static final float BTN_SIZE = 24f;
    
    private String[] buttonTexts = {"This is Option A", "Option B", 
            "Something Else Entirely"};
    
    public ButtonLayout() {
        int colGap = 20;
        JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 0, colGap));
        for (String btnText : buttonTexts) {
            JButton button = new JButton(btnText);
            
            // set first letter of text as mnemonic (alt-char shortcut)
            int mnemonic = (int) btnText.charAt(0);
            button.setMnemonic(mnemonic);
            
            // make button bigger by increasing its font
            button.setFont(button.getFont().deriveFont(BTN_SIZE));
            
            // add to the GridLayout-using JPanel
            buttonPanel.add(button);
        }
        
        // set layout of main panel to GridBag
        setLayout(new GridBagLayout());
        
        // add the button panel in a "default" manner (no constraints)
        // which centers this panel
        add(buttonPanel);
    }
    
    @Override
    public Dimension getPreferredSize() {
        Dimension superSize = super.getPreferredSize();
        int width = Math.max(MY_WIDTH, superSize.width);
        int height = Math.max(MY_HEIGHT, superSize.height);
        
        return new Dimension(width, height);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        ButtonLayout mainPanel = new ButtonLayout();
        JFrame frame = new JFrame("ButtonLayout");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

使用 EmptyBorder 的示例 2:

import java.awt.GridLayout;
import javax.swing.*;

@SuppressWarnings("serial")
public class ButtonLayout extends JPanel {
    public static final int MY_WIDTH = 750;
    public static final int MY_HEIGHT = 500;
    private static final float BTN_SIZE = 24f;
    
    private String[] buttonTexts = {"This is Option A", "Option B", 
            "Something Else Entirely"};
    
    public ButtonLayout() {
        int colGap = 20;
        JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 0, colGap));
        for (String btnText : buttonTexts) {
            JButton button = new JButton(btnText);
            
            // set first letter of text as mnemonic (alt-char shortcut)
            int mnemonic = (int) btnText.charAt(0);
            button.setMnemonic(mnemonic);
            
            // make button bigger by increasing its font
            button.setFont(button.getFont().deriveFont(BTN_SIZE));
            
            // add to the GridLayout-using JPanel
            buttonPanel.add(button);
        }
        
        add(buttonPanel);
        
        int top = 60;
        int left = top;
        int bottom = 2 * top;
        int right = left;
        setBorder(BorderFactory.createEmptyBorder(top, left, bottom, right));
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        ButtonLayout mainPanel = new ButtonLayout();
        JFrame frame = new JFrame("ButtonLayout");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

我不确定我是否完全理解这个问题,但是如果你想垂直对齐按钮,但允许它们保持它们的首选大小,只是不要提供任何类型的 fill 约束,例如

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

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

    public SoTest() {
        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.gridwidth = GridBagConstraints.REMAINDER;

            add(new JButton("This is option A"), gbc);
            add(new JButton("Option B"), gbc);
            add(new JButton("Another button"), gbc);
        }

    }
}

或者,如果您希望它们具有相同的宽度,请使用 fill 约束条件

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

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

    public SoTest() {
        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.gridwidth = GridBagConstraints.REMAINDER;
            gbc.fill = GridBagConstraints.BOTH;

            add(new JButton("This is option A"), gbc);
            add(new JButton("Option B"), gbc);
            add(new JButton("Another button"), gbc);
        }

    }
}

如果你想混合更复杂的布局,那么你应该考虑使用复合布局

但是等等,没有大纲...

因此,您“可能”可以通过多种方式做到这一点,例如,您可以使用 CompoundBorder...

setBorder(new CompoundBorder(new LineBorder(Color.CYAN, 16), new EmptyBorder(32, 32, 32, 32)));

但细节决定成败