如何让 FlowLayout 像其他组件一样在底部对齐 JPanel?
How can I get FlowLayout to align JPanels at the bottom like it does for other components?
我有一个案例,我将 JPanel 添加到 FlowLayout,但它们没有将自己对齐到布局的底部。我正在使用这个 layout.setAlignOnBaseline(true)
并且它正确地将 JLabels 对齐到面板的底部。但是,一旦这些标签本身包裹在面板中,它就不再有效了。这是我的意思的示例,顶部和底部有两个面板。
import javax.swing.*;
import java.awt.*;
public class BadLayout {
private static final Font font1 = new Font("Arial", Font.BOLD, 14);
private static final Font font2 = new Font("Arial", Font.BOLD, 30);
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Bad layout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout layout = new FlowLayout(FlowLayout.LEADING, 0, 0);
layout.setAlignOnBaseline(true);
JPanel topPanel = new JPanel();
topPanel.setLayout(layout);
topPanel.setBackground(Color.BLACK);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
JPanel subPanel = new JPanel();
subPanel.setLayout(layout);
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
topPanel.add(subPanel);
}
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(layout);
bottomPanel.setBackground(Color.DARK_GRAY);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
bottomPanel.add(label);
}
JPanel parentPanel = new JPanel();
parentPanel.setLayout(new BorderLayout());
parentPanel.add(topPanel, BorderLayout.NORTH);
parentPanel.add(bottomPanel, BorderLayout.SOUTH);
frame.getContentPane().add(parentPanel);
frame.pack();
frame.setVisible(true);
});
}
}
如果您 运行 这段代码,您会注意到顶部面板有较小的 "Foo" 面板居中,而底部面板有所需的 'bottom-aligned' 行为我希望。有什么想法可以让子 JPanel 的行为相同吗?
我自己,为此我会使用比 FlowLayout "oomph" 多的布局,例如 GridBagLayout。如果你使用它,你可以设置 anchor 为 SOUTH
,weighty 为 0.0,这样可以防止组件拉伸它的高度并将它放在底部。例如:
JPanel topPanel = new JPanel();
// topPanel.setLayout(layout);
topPanel.setLayout(new GridBagLayout());
topPanel.setBackground(Color.BLACK);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = i;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 0.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.SOUTH;
JPanel subPanel = new JPanel();
subPanel.setLayout(new BorderLayout());
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setPreferredSize(subPanel.getMinimumSize());
// subPanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
topPanel.add(subPanel, gbc);
}
在思考没有 GridBagLayout 怎么能做到这一点之后,我想到了这个,尽管它有点 'hackish':
JPanel topPanel = new JPanel();
((FlowLayout) topPanel.getLayout()).setAlignOnBaseline(true);
topPanel.setBackground(Color.BLACK);
int h = 0;
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.GREEN);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
JPanel wrapBorder = new JPanel(new BorderLayout());
JPanel subPanel = new JPanel();
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setBackground(Color.GREEN);
wrapBorder.setOpaque(false);
wrapBorder.add(BorderLayout.SOUTH, subPanel);
if(wrapBorder.getPreferredSize().height > h) {
h = wrapBorder.getPreferredSize().height;
}
topPanel.add(wrapBorder);
}
for(Component component : topPanel.getComponents()) {
component.setPreferredSize(new Dimension(component.getPreferredSize().width, h));
}
我这样做的唯一原因是因为我个人从未使用过 GridBagLayout
,但它有其优点(可以看出 ;))。
我可能只是缺少一种使 JPanel
垂直扩展的方法,我把事情复杂化了很多。
setAlignOnBaseline(...)
方法的 API 声明:
没有基线的组件将居中
JPanel 没有合理的基线可供使用,因为组件可能位于多行,具体取决于所使用的布局管理器。所以它是居中的。
我无法从你的问题中判断出你是否真的试图将所有文本置于基线上而不考虑字体大小,或者你是否只是想让所有组件都绘制在面板的底部.
如果您试图将文本置于基线上居中,那么也许您可以使用如下代码覆盖面板的基线:
JPanel subPanel = new JPanel()
{
@Override
public int getBaseline(int width, int height)
{
Component c = getComponent(0);
return c.getBaseline(width, height);
}
};
当然,这只有在面板上的所有组件都具有相同的基线时才有效。
或者,如果您只是想将所有组件放置在面板底部,那么您需要使用不同的布局管理器。
您可以使用 Relative Layout 将所有组件对齐到底部。
它可以直接替换您现有的代码:
RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS, 0);
rl.setAlignment( RelativeLayout.TRAILING );
JPanel topPanel = new JPanel(rl);
或者,如果您不想使用非 JDK class,那么 BoxLayout
或 GridBagLayout
将是可行的方法。
如果您使用 BoxLayout
,那么您将需要使用每个组件的 setAlignmentY(...)
属性。
如果您使用 GridBagLayout
,那么您将需要使用每个组件的约束。
我有一个案例,我将 JPanel 添加到 FlowLayout,但它们没有将自己对齐到布局的底部。我正在使用这个 layout.setAlignOnBaseline(true)
并且它正确地将 JLabels 对齐到面板的底部。但是,一旦这些标签本身包裹在面板中,它就不再有效了。这是我的意思的示例,顶部和底部有两个面板。
import javax.swing.*;
import java.awt.*;
public class BadLayout {
private static final Font font1 = new Font("Arial", Font.BOLD, 14);
private static final Font font2 = new Font("Arial", Font.BOLD, 30);
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Bad layout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
FlowLayout layout = new FlowLayout(FlowLayout.LEADING, 0, 0);
layout.setAlignOnBaseline(true);
JPanel topPanel = new JPanel();
topPanel.setLayout(layout);
topPanel.setBackground(Color.BLACK);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
JPanel subPanel = new JPanel();
subPanel.setLayout(layout);
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
topPanel.add(subPanel);
}
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(layout);
bottomPanel.setBackground(Color.DARK_GRAY);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
bottomPanel.add(label);
}
JPanel parentPanel = new JPanel();
parentPanel.setLayout(new BorderLayout());
parentPanel.add(topPanel, BorderLayout.NORTH);
parentPanel.add(bottomPanel, BorderLayout.SOUTH);
frame.getContentPane().add(parentPanel);
frame.pack();
frame.setVisible(true);
});
}
}
如果您 运行 这段代码,您会注意到顶部面板有较小的 "Foo" 面板居中,而底部面板有所需的 'bottom-aligned' 行为我希望。有什么想法可以让子 JPanel 的行为相同吗?
我自己,为此我会使用比 FlowLayout "oomph" 多的布局,例如 GridBagLayout。如果你使用它,你可以设置 anchor 为 SOUTH
,weighty 为 0.0,这样可以防止组件拉伸它的高度并将它放在底部。例如:
JPanel topPanel = new JPanel();
// topPanel.setLayout(layout);
topPanel.setLayout(new GridBagLayout());
topPanel.setBackground(Color.BLACK);
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.RED);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = i;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 0.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.SOUTH;
JPanel subPanel = new JPanel();
subPanel.setLayout(new BorderLayout());
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setPreferredSize(subPanel.getMinimumSize());
// subPanel.setAlignmentY(Component.BOTTOM_ALIGNMENT);
topPanel.add(subPanel, gbc);
}
在思考没有 GridBagLayout 怎么能做到这一点之后,我想到了这个,尽管它有点 'hackish':
JPanel topPanel = new JPanel();
((FlowLayout) topPanel.getLayout()).setAlignOnBaseline(true);
topPanel.setBackground(Color.BLACK);
int h = 0;
for (int i = 0; i < 10; i++) {
JLabel label = new JLabel("Foo");
label.setForeground(Color.WHITE);
label.setBackground(Color.GREEN);
label.setOpaque(true);
label.setFont(i % 2 == 0 ? font1 : font2);
JPanel wrapBorder = new JPanel(new BorderLayout());
JPanel subPanel = new JPanel();
subPanel.setBackground(Color.RED);
subPanel.add(label);
subPanel.setBackground(Color.GREEN);
wrapBorder.setOpaque(false);
wrapBorder.add(BorderLayout.SOUTH, subPanel);
if(wrapBorder.getPreferredSize().height > h) {
h = wrapBorder.getPreferredSize().height;
}
topPanel.add(wrapBorder);
}
for(Component component : topPanel.getComponents()) {
component.setPreferredSize(new Dimension(component.getPreferredSize().width, h));
}
我这样做的唯一原因是因为我个人从未使用过 GridBagLayout
,但它有其优点(可以看出 ;))。
我可能只是缺少一种使 JPanel
垂直扩展的方法,我把事情复杂化了很多。
setAlignOnBaseline(...)
方法的 API 声明:
没有基线的组件将居中
JPanel 没有合理的基线可供使用,因为组件可能位于多行,具体取决于所使用的布局管理器。所以它是居中的。
我无法从你的问题中判断出你是否真的试图将所有文本置于基线上而不考虑字体大小,或者你是否只是想让所有组件都绘制在面板的底部.
如果您试图将文本置于基线上居中,那么也许您可以使用如下代码覆盖面板的基线:
JPanel subPanel = new JPanel()
{
@Override
public int getBaseline(int width, int height)
{
Component c = getComponent(0);
return c.getBaseline(width, height);
}
};
当然,这只有在面板上的所有组件都具有相同的基线时才有效。
或者,如果您只是想将所有组件放置在面板底部,那么您需要使用不同的布局管理器。
您可以使用 Relative Layout 将所有组件对齐到底部。
它可以直接替换您现有的代码:
RelativeLayout rl = new RelativeLayout(RelativeLayout.X_AXIS, 0);
rl.setAlignment( RelativeLayout.TRAILING );
JPanel topPanel = new JPanel(rl);
或者,如果您不想使用非 JDK class,那么 BoxLayout
或 GridBagLayout
将是可行的方法。
如果您使用 BoxLayout
,那么您将需要使用每个组件的 setAlignmentY(...)
属性。
如果您使用 GridBagLayout
,那么您将需要使用每个组件的约束。