【问题标题】:How to align vertically buttons of different sizes within a GridBagLayout?如何在 GridBagLayout 中垂直对齐不同大小的按钮?
【发布时间】:2020-12-26 08:24:15
【问题描述】:

我从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,但按钮的大小会发生变化。

在保持元素尺寸的情况下,将元素居中的推荐方法是什么?

【问题讨论】:

    标签: java swing center layout-manager gridbaglayout


    【解决方案1】:

    我会嵌套布局:

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

    【讨论】:

    • @FrederickÁlvarez,阅读建议在面板上使用 EmptyBorder 以在底部保留额外空间,使其看起来更靠近顶部。
    • 收回我的赞成票。我不喜欢 JPanel 覆盖 getPreferredSize() 方法。布局管理器应根据添加的组件和使用的边框来计算大小。
    • BTW - DKM - 你的代码覆盖首选大小是我见过的更好的实现之一 - 因为它考虑了“自然”首选大小。我只是认为在这种情况下根本没有必要。
    • 1) [加 1 用于加入 2 个解决方案。] 2) Kleopatra 还教了我一两千件事。
    • @DontKnowMuchButGettingBetter,我从 kleopatra 那里学到了艰难的方法...... - 是的,我们都有,我完全同意。重写 getPreferredSize() 总是优先于使用 setPreferredSize(...) - 当它需要时。我建议在这种情况下都不需要。您编写的自定义代码越少越好。 EmptyBorder 将允许 OP 使用 API 在按钮面板周围留出额外空间。
    【解决方案2】:

    我不确定我是否完全理解这个问题,但如果你想垂直对齐按钮,但允许它们保持其首选大小,只是不要提供任何类型的 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)));
    

    但魔鬼在细节中

    【讨论】:

    • MadProgrammer 当然会回答....当然这是一个很好的回答。 1+
    猜你喜欢
    • 1970-01-01
    • 2014-07-24
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 1970-01-01
    • 2018-01-16
    • 1970-01-01
    • 2021-10-06
    相关资源
    最近更新 更多