英文:
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));
// 初始选项按钮
JButton button1 = new JButton("这是选项 A");
JButton button2 = new JButton("选项 B");
JButton button3 = new JButton("另一个文本");
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);
// 窗口配置
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,但按钮的大小会改变。
推荐的方法是什么,可以将元素居中对齐同时保持它们的尺寸不变?
<details>
<summary>英文:</summary>
I'm starting with swing, I have some questions about how to align elements within a GridBagLayout, I'm not sure either whether this is the correct approach, please advice.
I have the below code
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());
}
}
My goal is to produce the following output:
[![desired output][1]][1]
So far I have tried with BoxLayout with vertical alignment and it works but the problem is that it overwrites the preferred sized of the buttons and I want them all to be the same width.
Also, I tried with GridLayout and BorderLayout adding the elements to NORTH, CENTER, and SOUTH but the sizes of the buttons change.
**What is the recommended way to center the elements but keeping their dimensions?**
[1]: https://i.stack.imgur.com/P7Z7p.png
</details>
# 答案1
**得分**: 2
我会嵌套布局:
1. 一个包含按钮并使用 `new GridLayout(0, 1, 0, vGap)` 的 JPanel,这是一个包含一列和可变行数的网格,按钮之间有 vGap 的间隔。
2. 将这个 JPanel 添加到另一个使用 GridBagLayout 布局的 JPanel 中,在默认方式下添加(没有 GridBagConstraints),这会使第一个 JPanel 居中于第二个 JPanel 中。这显然必须以所需的大小实现。可以通过以下方法实现:
* 以合理的方式重写 `getPreferredSize()` 方法
* 调用 `setPreferredSize(new Dimension(someWidth, someHeight))` — 这不是很“干净”
* 为其添加边框,特别是 `BorderFactory.EmptyBorder(gap, gap, gap, gap)`,其中 gap 是 JPanel 周围的边框大小...
完成。
使用 GridBagLayout 的测试代码:
```java
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);
int mnemonic = (int) btnText.charAt(0);
button.setMnemonic(mnemonic);
button.setFont(button.getFont().deriveFont(BTN_SIZE));
buttonPanel.add(button);
}
setLayout(new GridBagLayout());
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:
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);
int mnemonic = (int) btnText.charAt(0);
button.setMnemonic(mnemonic);
button.setFont(button.getFont().deriveFont(BTN_SIZE));
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);
}
}
英文:
I would nest layouts:
- A JPanel that holds the buttons and uses a
new GridLayout(0, 1, 0, vGap)
-- a grid that holds one column and variable number of rows, with a vGap gap between buttons. - Add that JPanel into another JPanel that uses GridBagLayout, and add it in a default way (no GridBagConstraints) which will center the first JPanel into the second. This would obviously have to somehow be the size desired. This can be achieved by either
- overriding
getPreferredSize()
in a sensible way - Calling
setPreferredSize(new Dimension(someWidth, someHeight))
-- this isn't quite as "clean" - Giving this a border, specifically a
BorderFactor.EmptyBorder(gap, gap, gap, gap)
where gap is the size of the border around the JPanel...
Done.
Test code that uses the 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);
}
}
Example 2 that uses EmptyBorder:
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);
}
}
答案2
得分: 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)));
但是,细节决定成败。
英文:
I'm not sure I completely understand the issue, but if you want to vertically align the buttons, BUT allow them to keep their preferred size, just don't provide any kind of fill
constraint, for example
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);
}
}
}
Or, if you want them to have the same width, use a fill
constraint
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);
}
}
}
If you want to mix a more complex layout, then you should consider making use of compound layouts
But wait, there's no outline...
So, a number of ways you "might" be able to do this, for example, you could use a CompoundBorder
....
setBorder(new CompoundBorder(new LineBorder(Color.CYAN, 16), new EmptyBorder(32, 32, 32, 32)));
But the devil is in the detail
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论