【问题标题】:Prevent Vertical Centering of FlowLayout防止 FlowLayout 垂直居中
【发布时间】:2015-05-06 18:47:42
【问题描述】:

我有一个使用 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);

【问题讨论】:

    标签: java swing alignment layout-manager flowlayout


    【解决方案1】:

    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 会在组件自己移动到一行时为组件保留太多空间 - 它占用的空间量与其他面板一样高。 (如果您在right 之后添加更多面板,则可以看到这一点)。在这一点上,使用嵌套布局来放置较小的面板可能比使用基线更容易。

    基本上,您最好使用其他布局管理器,除非您确实无法避免FlowLayout

    【讨论】:

    • 我想使用 FlowLayout 的原因是因为它允许组件在调整容器大小并且组件不再彼此相邻时换行。如果可以使用不同的布局管理器实现此行为,那么我很乐意切换。
    • @bouncer 我不知道其他布局管理器可以做到这一点。关于缺少对齐支持的bug report 有一个可能有用的解决方法。
    • 不确定我是否遗漏了什么,但解决方法并没有改变任何东西。
    • I want to use the FlowLayout is because it allows the components to wrap to a new line - 这个要求应该是问题的一部分,而不是作为评论添加。
    【解决方案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();
                }
            });
        }
    }
    

    您可能还想考虑扩展Wrap Layout,它同样基于 FlowLayout,但增加了额外的功能。

    【讨论】:

      猜你喜欢
      • 2013-05-26
      • 2013-10-15
      • 1970-01-01
      • 1970-01-01
      • 2012-12-29
      • 2011-07-07
      • 2014-02-27
      • 2023-03-31
      • 1970-01-01
      相关资源
      最近更新 更多