防止 FlowLayout 垂直居中
Prevent Vertical Centering of FlowLayout
我有一个使用 FlowLayout 布局管理器的 JPanel,它包含不同大小的组件。
编辑:我想使用 FlowLayout,因为它允许组件在调整容器大小时换行并且它们不再彼此相邻。
下图描述了 FlowLayout 在不同组件上的垂直对齐方式:
如何修改 FlowLayout 以对齐组件的顶部,如下图所示:
这是问题的代码示例:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
frame.getContentPane().add(flowPanel);
JButton firstComp = new JButton("First");
firstComp.setPreferredSize(new Dimension(200, 300));
flowPanel.add(firstComp);
JButton secondComp = new JButton("Second");
secondComp.setPreferredSize(new Dimension(160, 180));
flowPanel.add(secondComp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
FlowLayout
本身不支持对齐,所以除非你真的需要它的多行行为,否则最好使用支持对齐的布局管理器(例如 BoxLayout
)。不过,通过使用基线对齐,FlowLayout
可以解决此问题:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Align {
private static final int PREF_HEIGHT = 100;
Align() {
JFrame frame = new JFrame("Align test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel bg = new JPanel();
((FlowLayout) bg.getLayout()).setAlignOnBaseline(true);
frame.add(bg);
JPanel left = new JPanel();
left.setBackground(Color.BLUE);
left.setPreferredSize(new Dimension(100, PREF_HEIGHT));
bg.add(left);
JPanel right = new JPanel() {
@Override
public int getBaseline(int width, int height) {
return PREF_HEIGHT;
}
};
right.setBackground(Color.GREEN);
right.setPreferredSize(new Dimension(100, 50));
bg.add(right);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Align();
}
});
}
}
结果:
不幸的是,这一点也不完美。首先是获得基线的半神奇方式,这取决于面板中 other 组件的高度。其次,FlowLayout
会为组件保留太多 space 当它自己移动到一行时 - 它需要相同数量的 space 就好像它和另一个面板一样高. (如果您在 right
之后添加更多面板,则可以看到这一点)。到那时,使用嵌套布局放置较小的面板可能比弄乱基线更容易。
基本上,你最好使用其他一些布局管理器,除非你真的无法避免FlowLayout
。
FlowLayout 是唯一支持将组件换行的标准 JDK 布局管理器。 (可能有第三方布局,比如支持这个的MigLayout)。
如果您不喜欢默认功能,则可以自定义布局管理器。这是一个让 FlowLayout 执行默认布局然后将每个组件重置到行顶部的简单示例:
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class TopFlowLayout extends FlowLayout
{
@Override
public void layoutContainer(Container container)
{
super.layoutContainer(container);
Component c = container.getComponent(0);
int lineStart = getVgap();
int lineHeight = lineStart + c.getSize().height;
for (int i = 0; i < container.getComponentCount(); i++)
{
c = container.getComponent(i);
Point p = c.getLocation();
Dimension d = c.getSize();
if (p.y < lineHeight) // still on current line
{
p.y = lineStart;
lineHeight = Math.max(lineHeight, lineStart + d.height);
}
else // start a new line
{
lineStart = lineHeight + getVgap();
p.y = lineStart;
lineHeight = lineStart + d.height;
}
p.y = lineStart;
c.setLocation(p);
}
}
private static void createAndShowGUI()
{
TopFlowLayout layout = new TopFlowLayout();
layout.setAlignment(FlowLayout.LEFT);
JPanel flowPanel = new JPanel( layout );
Random random = new Random();
for (int i = 0; i < 10; i++)
{
flowPanel.add( createButton(i + "", random.nextInt(100), random.nextInt(100)) );
}
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( flowPanel );
frame.setLocationByPlatform( true );
frame.setSize(400, 400);
frame.setVisible( true );
}
private static JButton createButton(String text, int width, int height)
{
JButton button = new JButton(text);
Dimension size = new Dimension(width + 50, height + 50);
button.setPreferredSize(size);
return button;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
您可能还想考虑扩展 Wrap Layout,它也基于 FlowLayout,但增加了额外的功能。
我有一个使用 FlowLayout 布局管理器的 JPanel,它包含不同大小的组件。
编辑:我想使用 FlowLayout,因为它允许组件在调整容器大小时换行并且它们不再彼此相邻。
下图描述了 FlowLayout 在不同组件上的垂直对齐方式:
如何修改 FlowLayout 以对齐组件的顶部,如下图所示:
这是问题的代码示例:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel flowPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
frame.getContentPane().add(flowPanel);
JButton firstComp = new JButton("First");
firstComp.setPreferredSize(new Dimension(200, 300));
flowPanel.add(firstComp);
JButton secondComp = new JButton("Second");
secondComp.setPreferredSize(new Dimension(160, 180));
flowPanel.add(secondComp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
FlowLayout
本身不支持对齐,所以除非你真的需要它的多行行为,否则最好使用支持对齐的布局管理器(例如 BoxLayout
)。不过,通过使用基线对齐,FlowLayout
可以解决此问题:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Align {
private static final int PREF_HEIGHT = 100;
Align() {
JFrame frame = new JFrame("Align test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel bg = new JPanel();
((FlowLayout) bg.getLayout()).setAlignOnBaseline(true);
frame.add(bg);
JPanel left = new JPanel();
left.setBackground(Color.BLUE);
left.setPreferredSize(new Dimension(100, PREF_HEIGHT));
bg.add(left);
JPanel right = new JPanel() {
@Override
public int getBaseline(int width, int height) {
return PREF_HEIGHT;
}
};
right.setBackground(Color.GREEN);
right.setPreferredSize(new Dimension(100, 50));
bg.add(right);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new Align();
}
});
}
}
结果:
不幸的是,这一点也不完美。首先是获得基线的半神奇方式,这取决于面板中 other 组件的高度。其次,FlowLayout
会为组件保留太多 space 当它自己移动到一行时 - 它需要相同数量的 space 就好像它和另一个面板一样高. (如果您在 right
之后添加更多面板,则可以看到这一点)。到那时,使用嵌套布局放置较小的面板可能比弄乱基线更容易。
基本上,你最好使用其他一些布局管理器,除非你真的无法避免FlowLayout
。
FlowLayout 是唯一支持将组件换行的标准 JDK 布局管理器。 (可能有第三方布局,比如支持这个的MigLayout)。
如果您不喜欢默认功能,则可以自定义布局管理器。这是一个让 FlowLayout 执行默认布局然后将每个组件重置到行顶部的简单示例:
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class TopFlowLayout extends FlowLayout
{
@Override
public void layoutContainer(Container container)
{
super.layoutContainer(container);
Component c = container.getComponent(0);
int lineStart = getVgap();
int lineHeight = lineStart + c.getSize().height;
for (int i = 0; i < container.getComponentCount(); i++)
{
c = container.getComponent(i);
Point p = c.getLocation();
Dimension d = c.getSize();
if (p.y < lineHeight) // still on current line
{
p.y = lineStart;
lineHeight = Math.max(lineHeight, lineStart + d.height);
}
else // start a new line
{
lineStart = lineHeight + getVgap();
p.y = lineStart;
lineHeight = lineStart + d.height;
}
p.y = lineStart;
c.setLocation(p);
}
}
private static void createAndShowGUI()
{
TopFlowLayout layout = new TopFlowLayout();
layout.setAlignment(FlowLayout.LEFT);
JPanel flowPanel = new JPanel( layout );
Random random = new Random();
for (int i = 0; i < 10; i++)
{
flowPanel.add( createButton(i + "", random.nextInt(100), random.nextInt(100)) );
}
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add( flowPanel );
frame.setLocationByPlatform( true );
frame.setSize(400, 400);
frame.setVisible( true );
}
private static JButton createButton(String text, int width, int height)
{
JButton button = new JButton(text);
Dimension size = new Dimension(width + 50, height + 50);
button.setPreferredSize(size);
return button;
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
您可能还想考虑扩展 Wrap Layout,它也基于 FlowLayout,但增加了额外的功能。