防止FlowLayout的垂直居中

时间:2015-03-05 11:48:14

标签: java swing alignment layout-manager flowlayout

我有一个使用FlowLayout布局管理器的JPanel,并包含不同大小的组件。

编辑:我想使用FlowLayout,因为当容器调整大小并且它们不再相邻时,它允许组件换行到新行。

下图描绘了FlowLayout在不同组件上的垂直对齐方式:

How the FlowLayout is aligning the components

如何修改FlowLayout以对齐组件顶部,如下图所示:

How I would like the FlowLayout to align the components

以下是问题的代码示例:

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);

2 个答案:

答案 0 :(得分:2)

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();
            }
        });
    }
}

结果:

enter image description here

不幸的是,这是完美无瑕的。首先是获得基线的半神奇方式,这取决于面板中其他组件的高度。其次,当FlowLayout移动到一行时,right将为组件保留太多空间 - 它占用的空间大小与其他面板一样高。 (如果在FlowLayout之后添加更多面板,则可以看到这一点)。此时,使用嵌套布局来放置较小的面板比使用基线搞乱更容易。

基本上,除非你真的无法避免{{1}},否则你最好使用其他布局管理器。

答案 1 :(得分:2)

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();
            }
        });
    }
}

您可能还需要考虑扩展同样基于FlowLayout的Wrap Layout,但会添加其他功能。