【问题标题】:Providing white space in a Swing GUI在 Swing GUI 中提供空白
【发布时间】:2026-01-28 09:35:01
【问题描述】:

没有空白的 GUI 显得“拥挤”。如何在不显式设置组件的位置或大小的情况下提供空白?

【问题讨论】:

标签: java swing whitespace layout-manager


【解决方案1】:

使用各种LayoutManagers 可以提供各种组件之间的间距。

1.) 边框布局:

2.) 流式布局:

3.) 网格布局:

4.) GridBagLayout :

GridBagConstraints.insets

5.) CardLayout (example) :

CardLayout(int hGap, int vGap)

显示所有构造函数的示例:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class LayoutExample {

    private final int hGap = 5;
    private final int vGap = 5;

    private String[] borderConstraints = {
        BorderLayout.PAGE_START,
        BorderLayout.LINE_START,
        BorderLayout.CENTER,
        BorderLayout.LINE_END,
        BorderLayout.PAGE_END
    };

    private JButton[] buttons;

    private GridBagConstraints gbc;

    private JPanel borderPanel;
    private JPanel flowPanel;
    private JPanel gridPanel;
    private JPanel gridBagPanel;
    private JPanel cardPanel;

    public LayoutExample() {
        buttons = new JButton[16];
        gbc = new GridBagConstraints();
        gbc.anchor = GridBagConstraints.FIRST_LINE_START;   
        gbc.insets = new Insets(hGap, vGap, hGap, vGap);        
    }

    private void displayGUI() {
        JFrame frame = new JFrame("Layout Example");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        JPanel contentPane = new JPanel(
                        new GridLayout(0, 1, hGap, vGap));
        contentPane.setBorder(
            BorderFactory.createEmptyBorder(hGap, vGap, hGap, vGap));
        borderPanel = new JPanel(new BorderLayout(hGap, vGap));
        borderPanel.setBorder(
            BorderFactory.createTitledBorder("BorderLayout"));
        borderPanel.setOpaque(true);
        borderPanel.setBackground(Color.WHITE);
        for (int i = 0; i < 5; i++) {
            buttons[i] = new JButton(borderConstraints[i]);
            borderPanel.add(buttons[i], borderConstraints[i]);
        }
        contentPane.add(borderPanel);

        flowPanel = new JPanel(new FlowLayout(
                    FlowLayout.CENTER, hGap, vGap));
        flowPanel.setBorder(
            BorderFactory.createTitledBorder("FlowLayout"));
        flowPanel.setOpaque(true);
        flowPanel.setBackground(Color.WHITE);
        for (int i = 5; i < 8; i++) {
            buttons[i] = new JButton(Integer.toString(i));
            flowPanel.add(buttons[i]);
        }
        contentPane.add(flowPanel);

        gridPanel = new JPanel(new GridLayout(2, 2, hGap, vGap));
        gridPanel.setBorder(
            BorderFactory.createTitledBorder("GridLayout"));
        gridPanel.setOpaque(true);
        gridPanel.setBackground(Color.WHITE);
        for (int i = 8; i < 12; i++) {
            buttons[i] = new JButton(Integer.toString(i));
            gridPanel.add(buttons[i]);
        }
        contentPane.add(gridPanel);

        gridBagPanel = new JPanel(new GridBagLayout());
        gridBagPanel.setBorder(
            BorderFactory.createTitledBorder("GridBagLayout"));
        gridBagPanel.setOpaque(true);
        gridBagPanel.setBackground(Color.WHITE);
        buttons[12] = new JButton(Integer.toString(12));
        addComp(gridBagPanel, buttons[12], 0, 0, 1, 1
                            , GridBagConstraints.BOTH, 0.33, 0.5);
        buttons[13] = new JButton(Integer.toString(13));
        addComp(gridBagPanel, buttons[13], 1, 0, 1, 1
                            , GridBagConstraints.BOTH, 0.33, 0.5);
        buttons[14] = new JButton(Integer.toString(14));
        addComp(gridBagPanel, buttons[14], 0, 1, 2, 1
                            , GridBagConstraints.BOTH, 0.66, 0.5);
        buttons[15] = new JButton(Integer.toString(15));
        addComp(gridBagPanel, buttons[15], 2, 0, 1, 2
                            , GridBagConstraints.BOTH, 0.33, 1.0);
        contentPane.add(gridBagPanel);

        cardPanel = new JPanel(new CardLayout(hGap, vGap));
        cardPanel.setBorder(
            BorderFactory.createTitledBorder("CardLayout"));
        cardPanel.setOpaque(true);
        cardPanel.setBackground(Color.WHITE);
        cardPanel.add(getPanel(Color.BLUE));
        cardPanel.add(getPanel(Color.GREEN));
        contentPane.add(cardPanel);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel getPanel(Color bColor) {
        JPanel panel = new JPanel(new FlowLayout(
                    FlowLayout.CENTER, hGap, vGap));
        panel.setOpaque(true);
        panel.setBackground(bColor.darker().darker());
        JButton swapperButton = new JButton("Next");
        swapperButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                CardLayout cardLayout = (CardLayout) cardPanel.getLayout();
                cardLayout.next(cardPanel);
            }
        });

        panel.add(swapperButton);

        return panel;
    }

    private void addComp(JPanel panel, JComponent comp
                                , int x, int y, int gWidth
                                    , int gHeight, int fill
                                        , double weightx, double weighty) {
        gbc.gridx = x;
        gbc.gridy = y;
        gbc.gridwidth = gWidth;
        gbc.gridheight = gHeight;
        gbc.fill = fill;
        gbc.weightx = weightx;
        gbc.weighty = weighty;      

        panel.add(comp, gbc);
    }

    public static void main(String[] args) {
        Runnable runnable = new Runnable(){
            @Override
            public void run() {
                new LayoutExample().displayGUI();
            }
        };
        EventQueue.invokeLater(runnable);
    }
}

输出:

【讨论】:

  • +1 我很喜欢一些 GridBagConstraints 插图。我们经常使用 GBL,所以我编写了一些帮助类来为我完成繁琐的工作,以便 GBL 代码非常简洁易读。就像这里的示例一样,我使用默认的 Insets,这样我就不必每次都输入它,结果看起来是一个更“放松”的布局。
【解决方案2】:

在 Swing GUI 中有多种方法可以提供组件之间的分隔以及组件周围的空白:

但更一般地说,看看:

  • 可以在布局构造函数中定义间距。
  • 边界。

这里是使用布局分隔符hGap & vGap 值和边框(特别是EmptyBorder)来提供“白色”(实际上显示为红色以使其非常明显)空间。调整微调器以查看结果。

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;

public class WhiteSpace {

    private JPanel gui = null;
    private BorderLayout mainLayout = 
        new BorderLayout(0, 0);
    private final FlowLayout buttonLayout = 
            new FlowLayout(FlowLayout.CENTER, 0, 0);
    private final JPanel buttonPanel = new JPanel(buttonLayout);
    private final SpinnerNumberModel hModel = 
            new SpinnerNumberModel(0, 0, 15, 1);
    private final SpinnerNumberModel vModel = 
            new SpinnerNumberModel(0, 0, 15, 1);
    private final SpinnerNumberModel hBorderModel = 
            new SpinnerNumberModel(0, 0, 15, 1);
    private final SpinnerNumberModel vBorderModel = 
            new SpinnerNumberModel(0, 0, 15, 1);
    private ChangeListener changeListener;

    public Container getGui() {
        if (gui == null) {
            gui = new JPanel(mainLayout);
            gui.setBackground(Color.RED);

            JTree tree = new JTree();
            tree.setVisibleRowCount(10);
            for (int ii = tree.getRowCount(); ii > -1; ii--) {
                tree.expandRow(ii);
            }
            gui.add(new JScrollPane(
                    tree,
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                    JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),
                    BorderLayout.LINE_START);
            gui.add(new JScrollPane(new JTextArea(10, 30)));

            gui.add(buttonPanel, BorderLayout.PAGE_START);

            changeListener = (ChangeEvent e) -> {
                int hGap = hModel.getNumber().intValue();
                int vGap = vModel.getNumber().intValue();
                int hBorder = hBorderModel.getNumber().intValue();
                int vBorder = vBorderModel.getNumber().intValue();
                adjustWhiteSpace(hGap, vGap, hBorder, vBorder);
            };

            addModel("H Gap", hModel);
            addModel("V Gap", vModel);
            addModel("H Border", hBorderModel);
            addModel("V Border", vBorderModel);
        }

        return gui;
    }

    private void addModel(String label, SpinnerNumberModel model) {
        buttonPanel.add(new JLabel(label));
        final JSpinner spinner = new JSpinner(model);
        spinner.addChangeListener(changeListener);
        buttonPanel.add(spinner);
    }

    private void adjustWhiteSpace(
            int hGap, int vGap, int hBorder, int vBorder) {
        mainLayout.setHgap(hGap);
        mainLayout.setVgap(vGap);
        buttonLayout.setHgap(hGap);
        gui.setBorder(new EmptyBorder
                (vBorder, hBorder, vBorder, hBorder));
        Container c = gui.getTopLevelAncestor();
        if (c instanceof Window) {
            Window w = (Window) c;
            w.pack();
        }
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            WhiteSpace ws = new WhiteSpace();
            Container gui1 = ws.getGui();
            JFrame f = new JFrame("White (OK Red) Space");
            f.add(gui1);
            f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            f.setLocationByPlatform(true);
            f.setResizable(false);
            f.pack();
            f.setVisible(true);
        };
        SwingUtilities.invokeLater(r);
    }
}

【讨论】:

    【解决方案3】:

    当你使用BoxLayout时,Box.createVerticalGlue()方法可以帮你做一些空白。

    另一种方法是BorderFactory.createEmptyBorder(int top, int left, int bottom, int right)。它可以帮助您在组件周围留出一些空白。

    感谢 Andrew Thompson 的提醒。我最近几天修改了 BoxLayout,发现Box.createVerticalGlue() 可以根据面板的大小添加一些空白,并且您不能设置空白长度的显式像素值。但是Box.createVerticalStrut() 可以做到这一点。这是一个 MCTaRE 并展示了这两种方法的效果。

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.border.*;
    import javax.swing.event.*;
    
    public class WhiteSpace extends JFrame{
        static WhiteSpace whiteSpace;
        DemoPanel demoPanel;
        boolean withGlue;
        JSpinner spinner;
    
        public WhiteSpace(){
            initialWindow();
            demoPanel = new DemoPanel();
            ActionPanel actionPanel = new ActionPanel();
    
            setLayout(new BorderLayout());
    
            getContentPane().add(actionPanel,BorderLayout.NORTH);
            getContentPane().add(demoPanel,BorderLayout.CENTER);
                setVisible(true);
        }
    
        public void initialWindow(){
            setSize(220, 300);
            setTitle("White Space");
            setResizable(false);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setLocationRelativeTo(null);
            //Show the window in the middle of the screen
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    whiteSpace = new WhiteSpace();
                }
            };
            SwingUtilities.invokeLater(runnable);
        }
    
        class DemoPanel extends JPanel{
            //Show the vertical white space between label1 and label2
            JLabel label1;
            JLabel label2;
            public void initialDemoPanel(){
                setBorder(BorderFactory.createTitledBorder(getBorder(), "DemoPanel", TitledBorder.LEADING, TitledBorder.TOP, new Font("Default",Font.PLAIN,10), Color.gray));
                setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
    
                label1 = new JLabel("This is first line");
                label2 = new JLabel("This is second line");
            }
    
            public DemoPanel(){
                initialDemoPanel();
                add(label1);
                if(withGlue){
                    add(Box.createVerticalGlue());
                }
                add(label2);
            }
    
            public DemoPanel(int strutValue){
                initialDemoPanel();
                add(label1);
                add(Box.createVerticalStrut(strutValue));
                add(label2);
            }
        }
    
        class ActionPanel extends JPanel{
            public ActionPanel(){
                setBorder(BorderFactory.createTitledBorder(getBorder(), "ActionPanel", TitledBorder.LEADING, TitledBorder.TOP, new Font("Default",Font.PLAIN,10), Color.gray));
    
                setLayout(new BoxLayout(this,BoxLayout.X_AXIS));
                JRadioButton glueButton = new JRadioButton("With Glue");
                glueButton.addActionListener(new glueButtonListener());
                add(glueButton);
    
                add(Box.createHorizontalStrut(10));
                //To create horizontal white space
                JLabel strutLabel = new JLabel("Strut Value");
                add(strutLabel);
                spinner = new JSpinner(new SpinnerNumberModel(0,0,50,1));
                spinner.addChangeListener(new spinnerListener());
                add(spinner);
                //public SpinnerNumberModel(Number value,Comparable minimum,Comparable maximum,Number stepSize)
            }
        }
    
        class glueButtonListener implements ActionListener{
            @Override
            public void actionPerformed(ActionEvent e) {
                spinner.setValue(new Integer(0));
                withGlue = (withGlue == true ? false:true);
                whiteSpace.getContentPane().remove(demoPanel);
                demoPanel = new DemoPanel();
                whiteSpace.getContentPane().add(demoPanel,BorderLayout.CENTER);
                whiteSpace.getContentPane().validate();
            }
        }
    
        class spinnerListener implements ChangeListener{
    
            @Override
            public void stateChanged(ChangeEvent e) {
                int strutValue = (Integer) spinner.getValue();
                whiteSpace.getContentPane().remove(demoPanel);
                demoPanel = new DemoPanel(strutValue);
                whiteSpace.getContentPane().add(demoPanel,BorderLayout.CENTER);
                whiteSpace.getContentPane().validate();
            }
        }
    }
    

    Box.createHorizontalGlue()Box.createHorizontalStrut(int height) 也可以使用。另外Box.createRigidArea(Dimension d)也有创造空白的能力。

    【讨论】:

      【解决方案4】:

      MigLayout 有多种创建空间的方式。 (在此布局中,空间称为间隙。) 可以在具有布局约束的*别创建间隙,可以 在行和列之间创建间隙,也可以在单个之间设置间隙 具有组件约束的组件。边界周围也有特定的差距 一个名为 insets 的容器,它有自己的特定关键字要设置。

      以下示例创建了所有这些类型的间隙:

      package com.zetcode;
      
      import java.awt.EventQueue;
      import javax.swing.BorderFactory;
      import javax.swing.JButton;
      import javax.swing.JFrame;
      import javax.swing.JLabel;
      import javax.swing.JPanel;
      import javax.swing.JTextField;
      import net.miginfocom.swing.MigLayout;
      
      
      public class MigLayoutGaps2 extends JFrame {
      
          public MigLayoutGaps2() {
      
              initUI();
      
              setTitle("Gaps");
              setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              setLocationRelativeTo(null);
          }
      
          private void initUI() {
      
              JPanel base = new JPanel(new MigLayout("flowy, ins 30, gap 15"));
              setContentPane(base);
      
              JPanel pnl1 = new JPanel();
              pnl1.setBorder(
                      BorderFactory.createTitledBorder("Grid gaps")
              );
      
              pnl1.setLayout(new MigLayout("gap 5 5, ins 10, wrap 3"));
      
              pnl1.add(new JButton("1"));
              pnl1.add(new JButton("2"));
              pnl1.add(new JButton("3"));
              pnl1.add(new JButton("4"));
              pnl1.add(new JButton("5"));
              pnl1.add(new JButton("6"));
      
              JPanel pnl2 = new JPanel();
              pnl2.setBorder(
                      BorderFactory.createTitledBorder("Column gaps")
              );
      
              pnl2.setLayout(new MigLayout("wrap 3", "[]10[]"));
      
              JLabel lbl1 = new JLabel();
              lbl1.setBorder(
                  BorderFactory.createEtchedBorder()
              );
      
              JLabel lbl2 = new JLabel();
              lbl2.setBorder(
                  BorderFactory.createEtchedBorder()
              );
      
              JLabel lbl3 = new JLabel();
              lbl3.setBorder(
                  BorderFactory.createEtchedBorder()
              );        
      
              pnl2.add(lbl1, "w 40, h 110");
              pnl2.add(lbl2, "w 40, h 110");
              pnl2.add(lbl3, "w 40, h 110");
      
              JPanel pnl3 = new JPanel();
              pnl3.setBorder(
                      BorderFactory.createTitledBorder("Row gaps")
              );
      
              pnl3.setLayout(new MigLayout("wrap", "", "[]15[]"));
      
              JLabel lbl4 = new JLabel();
              lbl4.setBorder(
                  BorderFactory.createEtchedBorder()
              );
      
              JLabel lbl5 = new JLabel();
              lbl5.setBorder(
                  BorderFactory.createEtchedBorder()
              );
      
              JLabel lbl6 = new JLabel();
              lbl6.setBorder(
                  BorderFactory.createEtchedBorder()
              );        
      
              pnl3.add(lbl4, "w 150, h 20");
              pnl3.add(lbl5, "w 150, h 20");
              pnl3.add(lbl6, "w 150, h 20");        
      
              JPanel pnl4 = new JPanel();
              pnl4.setBorder(
                      BorderFactory.createTitledBorder("Component gaps")
              );
      
              pnl4.setLayout(new MigLayout());
      
              pnl4.add(new JLabel("Name:"), "gapright 5");
              pnl4.add(new JTextField(10), "gapbottom 20, gaptop 20");
      
              base.add(pnl1);
              base.add(pnl2);
              base.add(pnl3);
              base.add(pnl4);
      
              pack();
          }
      
          public static void main(String[] args) {
      
              EventQueue.invokeLater(new Runnable() {
                  @Override
                  public void run() {
                      MigLayoutGaps2 ex = new MigLayoutGaps2();
                      ex.setVisible(true);
                  }
              });
          }
      }
      

      我们在布局中有四个面板。每个面板都有一个MigLayout 经理。

      JPanel base = new JPanel(new MigLayout("flowy, ins 30, gap 15"));
      

      这条线在面板之间创建容器插入和垂直间隙。

      pnl1.setLayout(new MigLayout("gap 5 5, ins 10, wrap 3"));
      

      这里我们为整个网格结构应用间隙,并设置容器间隙。

      pnl2.setLayout(new MigLayout("wrap 3", "[]10[]"));
      

      这条线会在列之间产生间隙。

      pnl3.setLayout(new MigLayout("wrap", "", "[]15[]"));
      

      使用此代码定义行间距。

      pnl4.add(new JLabel("Name:"), "gapright 5");
      pnl4.add(new JTextField(10), "gapbottom 20, gaptop 20");
      

      最后,可以在各个组件之间创建间隙。

      【讨论】:

        【解决方案5】:

        JGoodies FormLayout.

        作者 Karsten Lentzsch 收集了关于 UI 设计的presentations。特别是this PDF 谈到了对美学空白的需求。添加有意义的空间,同时注意杂乱无章的情况,让小麦与谷壳分开。

        【讨论】:

          【解决方案6】:

          每当我遇到这个问题时,我都会使用 JPanels。例如在 GridLayout 中:

          JFrame frame = new JFrame;
          frame.setLayout(new GridLayout(2, 0));
          
          //We want the bottom left to be blank
          frame.add(new JLabel("Top Left"));
          frame.add(new JLabel("Top Right"));
          
          //This is the position we want empty
          frame.add(new JPanel());
          
          //Now we can continue with the rest of the script
          

          【讨论】: