在 Swing 中对齐垂直和水平的 SequentialGroup

Aligning Vertical and Horizontal SequentialGroup in Swing

这段代码是我写的

package test;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;

import javax.swing.*;
import javax.swing.GroupLayout.Alignment;


public class MainFrame extends JFrame  
{
    private int levels;
    private int slots;

    private JLabel labelShowLevel;
    private JFormattedTextField textShowLevel;
    private JButton buttonShowLevel ;

    private JLabel labelAddEntity ;
    private JFormattedTextField textAddEntity ;
    private JButton buttonAddEntity ;
    private JComboBox cb;

    private JLabel labelRemoveEntity ;
    private JFormattedTextField textRemoveEntity ;
    private JButton buttonRemoveEntity ;

    private JLabel labelSearchEntity ;
    private JFormattedTextField textSearchEntity ;
    private JButton buttonSearchEntity ;

    private JLabel labelEmptySlots ;
    private JButton buttonEmptySlots ;

    private JLabel levelDispaly;
    private JLabel totalLevels;


      public MainFrame(int levels, int slots) 
       {   
          this.levels = levels;
          this.slots  = slots;

          getContentPane().add( CreatPanel(), BorderLayout.EAST);  

          this.setDefaultCloseOperation(EXIT_ON_CLOSE);
          this.setPreferredSize(new Dimension(1000,500));
          this.revalidate();
          this.repaint();
          this.pack();
          this.setVisible(true);
       }


      JPanel CreatPanel()
      {
            JPanel panel = new JPanel();

            labelShowLevel = new JLabel("Display Level");
            labelAddEntity = new JLabel("Enter new car/motorbike");
            labelRemoveEntity = new JLabel("Exit car/motorbike");
            labelSearchEntity = new JLabel("Find car/motorbike");
            labelEmptySlots = new JLabel("Get total empty slots");


            textShowLevel = new JFormattedTextField();
            textAddEntity = new JFormattedTextField();
            textRemoveEntity = new JFormattedTextField();
            textSearchEntity = new JFormattedTextField();

            textShowLevel.setPreferredSize(new Dimension(100, HEIGHT));

            buttonShowLevel = new JButton("Show");          
            buttonAddEntity = new JButton("Enter");
            buttonRemoveEntity= new JButton("Exit");      
            buttonSearchEntity = new JButton("Search");
            buttonEmptySlots = new JButton("Find");

            Font font = new Font("sans comic", Font.ITALIC, 18);
            levelDispaly = new JLabel("Now Displaying Level 0");
            levelDispaly.setFont(font);
            totalLevels = new JLabel("Total Levels:"+ this.levels+" Total slots per level:"+this.slots);




            String[] items = { "Car", "Motorbike" };
            cb = new JComboBox(items);
            cb.setSelectedItem(items[0]);



            GroupLayout layout = new GroupLayout(panel);
            panel.setLayout(layout);
            layout.setAutoCreateGaps(true);
            layout.setAutoCreateContainerGaps(true);

            GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup();
            hGroup.addGroup(layout.createParallelGroup().
                     addComponent(labelShowLevel).addComponent(labelAddEntity).addComponent(labelRemoveEntity).addComponent(labelSearchEntity).addComponent(labelEmptySlots).addComponent(levelDispaly).addComponent(totalLevels));
            hGroup.addGroup(layout.createParallelGroup().
                    addComponent(textShowLevel).addComponent(textAddEntity).addComponent(cb).addComponent(textRemoveEntity).addComponent(textSearchEntity));
            hGroup.addGroup(layout.createParallelGroup().
                    addComponent(buttonShowLevel).addComponent(buttonAddEntity).addComponent(buttonRemoveEntity).addComponent(buttonSearchEntity).addComponent(buttonEmptySlots));
            layout.setHorizontalGroup(hGroup);

            layout.linkSize(SwingConstants.HORIZONTAL, buttonShowLevel, buttonAddEntity,buttonRemoveEntity,buttonSearchEntity,buttonEmptySlots);

            // Create a sequential group for the vertical axis.
            GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();

            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                     addComponent(labelShowLevel).addComponent(textShowLevel).addComponent(buttonShowLevel));
            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                     addComponent(labelAddEntity).addComponent(textAddEntity));
            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                    addComponent(cb).addComponent(buttonAddEntity));
            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                    addComponent(labelRemoveEntity).addComponent(textRemoveEntity).addComponent(buttonRemoveEntity));
            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                    addComponent(labelSearchEntity).addComponent(textSearchEntity).addComponent(buttonSearchEntity));
            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                    addComponent(labelEmptySlots).addComponent(buttonEmptySlots));

            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                    addComponent(levelDispaly));
            vGroup.addGroup(layout.createParallelGroup(Alignment.BASELINE).
                    addComponent(totalLevels));
            layout.setVerticalGroup(vGroup);    
          return panel;
      }

      public static void main(String args[]) 
      {

          java.awt.EventQueue.invokeLater(new Runnable() {
              public void run() 
              {
                  try 
                  {
                      UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
                  } 
                  catch (Exception ex) {
                      ex.printStackTrace();
                  }
                  (new MainFrame(5,5)).setVisible(true);   
              }
          });
      }

     }

结果是

我想要的是将最后两个标签(totalLevels 和 levelDispaly)居中并让它们占据三列。我做了很多尝试,但都失败了。 请注意,窗格的左侧是空的,因为我删除了代码中不需要的部分只是为了专注于问题。

你很接近 - 你陷入了一个警告,称为 仍在为 GroupLayout 开发直觉。有些人永远不会离开这个边缘:)

开个玩笑,这是你的 MCVE 有一些变化:

public class MainFrame extends JFrame {

    private int levels;
    private int slots;

    private JLabel labelShowLevel;
    private JFormattedTextField textShowLevel;
    private JButton buttonShowLevel;

    private JLabel labelAddEntity;
    private JFormattedTextField textAddEntity;
    private JButton buttonAddEntity;
    private JComboBox cb;

    private JLabel labelRemoveEntity;
    private JFormattedTextField textRemoveEntity;
    private JButton buttonRemoveEntity;

    private JLabel labelSearchEntity;
    private JFormattedTextField textSearchEntity;
    private JButton buttonSearchEntity;

    private JLabel labelEmptySlots;
    private JButton buttonEmptySlots;

    private JLabel levelDispaly;
    private JLabel totalLevels;

    public MainFrame(int levels, int slots) {

        this.levels = levels;
        this.slots = slots;

        getContentPane().add(CreatPanel());

        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.pack();
        this.setVisible(true);
    }

    JPanel CreatPanel() {

        JPanel panel = new JPanel();

        labelShowLevel = new JLabel("Display Level");
        labelAddEntity = new JLabel("Enter new car/motorbike");
        labelRemoveEntity = new JLabel("Exit car/motorbike");
        labelSearchEntity = new JLabel("Find car/motorbike");
        labelEmptySlots = new JLabel("Get total empty slots");

        textShowLevel = new JFormattedTextField();
        textAddEntity = new JFormattedTextField();
        textRemoveEntity = new JFormattedTextField();
        textSearchEntity = new JFormattedTextField();

        textShowLevel.setPreferredSize(new Dimension(100, HEIGHT));

        buttonShowLevel = new JButton("Show");
        buttonAddEntity = new JButton("Enter");
        buttonRemoveEntity = new JButton("Exit");
        buttonSearchEntity = new JButton("Search");
        buttonEmptySlots = new JButton("Find");

        Font font = new Font("sans comic", Font.ITALIC, 18);
        levelDispaly = new JLabel("Now Displaying Level 0");
        levelDispaly.setFont(font);
        totalLevels = new JLabel("Total Levels:"+ this.levels + " Total slots per level:"
                                    + this.slots);

        String[] items = {"Car", "Motorbike"};
        cb = new JComboBox(items);
        cb.setSelectedItem(items[0]);

        GroupLayout layout = new GroupLayout(panel);
        panel.setLayout(layout);
        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);
        layout.linkSize(SwingConstants.HORIZONTAL, buttonShowLevel, buttonAddEntity, buttonRemoveEntity, buttonSearchEntity, buttonEmptySlots);

//@formatter:off
        // Horizontal
        GroupLayout.ParallelGroup hGroup = layout.createParallelGroup(Alignment.CENTER); // Will align the labels the way you wanted

        hGroup.addGroup(layout.createSequentialGroup()
                   .addGroup(layout.createParallelGroup()
                             .addComponent(labelShowLevel)
                             .addComponent(labelAddEntity)
                             .addComponent(labelRemoveEntity)
                             .addComponent(labelSearchEntity)
                             .addComponent(labelEmptySlots))
                   .addGroup(layout.createParallelGroup()
                             .addComponent(textShowLevel)
                             .addComponent(textAddEntity)
                             .addComponent(cb)
                             .addComponent(textRemoveEntity)
                             .addComponent(textSearchEntity))
                   .addGroup(layout.createParallelGroup()
                             .addComponent(buttonShowLevel)
                             .addComponent(buttonAddEntity)
                             .addComponent(buttonRemoveEntity)
                             .addComponent(buttonSearchEntity)
                             .addComponent(buttonEmptySlots)));
        hGroup.addComponent(levelDispaly);
        hGroup.addComponent(totalLevels);

        layout.setHorizontalGroup(hGroup);

        // Vertical
        GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup();

        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(labelShowLevel)
                   .addComponent(textShowLevel)
                   .addComponent(buttonShowLevel));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(labelAddEntity)
                   .addComponent(textAddEntity));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(cb)
                   .addComponent(buttonAddEntity));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(labelRemoveEntity)
                   .addComponent(textRemoveEntity)
                   .addComponent(buttonRemoveEntity));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(labelSearchEntity)
                   .addComponent(textSearchEntity)
                   .addComponent(buttonSearchEntity));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(labelEmptySlots)
                   .addComponent(buttonEmptySlots));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(levelDispaly));
        vGroup.addGroup(layout.createParallelGroup()
                   .addComponent(totalLevels));

        layout.setVerticalGroup(vGroup);
//@formatter:on

        return panel;
      }

    public static void main(String args[]) {

        EventQueue.invokeLater(() -> new MainFrame(5, 5));
    }
}

你的问题出在水平组。您将标签限制为与左列对齐。作为一般经验法则,如果您想要在某个方向上不受限制(自由对齐)的组件,那么您需要在该方向的层次结构中添加另一个 "level" 组。

具体来说,从水平代码中删除 2 个标签后,您将得到一个顺序组(在您的情况下为 hGroup),其中包含平行组。由于 2 个标签不与这些组对齐,因此必须将它们添加到不是 hGroup 的顺序组中。这意味着如果 hGroup 是顺序的,它将有一个单独的 "child" 并行组,它下面将有一个新的顺序组(取代 hGroup)以及 2 个标签:

  • hGroup(顺序)
    • p(标准杆)
      • s(顺序)
        • p1
        • p2
        • p3
      • 标签 1
      • 标签2

(不显示 p1-p3 下的组件。)

但是,由于 hGroup 现在只有 1 个 child,您可以删除这个冗余的 top-level 顺序组,并使 hGroup 成为单个并行组。请记住,您的水平组和垂直组都可以是顺序组或平行组之一。不要限制自己将两个方向都顺序排列,因为这只会增加冗余组(它仍然有效):

  • hGroup(标准杆)
    • s(顺序)
      • p1
      • p2
      • p3
    • 标签 1
    • 标签2

以下是可以帮助您 GroupLayout 的一般思维模式。请注意,人们可以通过多种方式来思考这些问题并提出替代解释。使用 Grouplayout 一段时间后,这将变得完全直观。

选择正确的parent组类型

如果您想知道您的 vertical/horizontal 组应该是顺序的还是并行的,只需查看 "governing direction"。 "governing direction" 是您可以在最高级别明确告诉组件顺序的方向。

对于你的情况,让我们看看垂直组。从上到下查看时,您可以从上到下明确地告诉组件(或其组)的顺序,但不能从左到右,因为最后 2 个标签没有明确的从左到右的顺序其他组件 - 这 2 个标签由对齐决定。然后

Vertical group && Vertical governing direction == Sequential group

对于水平组,你只能告诉从上到下的顺序:

Horizontal group && Vertical governing direction == Parallel group

如果没有单"governing direction",您可以任选其一。想象一个 2x2 的网格,有 4 个布局层次结构将得到相同的结果。

如果您犯了错误并最终得到了错误类型的组,您将得到一个只有 1 个 child 组的其他类型的组。然后你就可以剪掉那个 top-level 组了。

正在创建组层次结构

选择 "governing direction" 后,开始朝那个方向前进,并按照确定的顺序添加组件。如果订单中有超过1个相同位置的组件,则意味着您需要在另一个方向添加一个组。

观察垂直(红色箭头)顺序组的以下示意图:

从上到下,顺序的第一个位置(红色矩形)有 3 个组件。这意味着我们需要一个并行组来分割(分隔)sub-hierarchy(绿线)中的每个组件。这个过程一直持续到我们到达第一个标签,此时没有 "competing" 组件,所以我们只添加该组件(红色矩形)。第二个标签也一样。这是垂直组的一对一代码。

观察水平(蓝色箭头)并联组的类似示意图:

从上到下,第一个位置有3个组件。这意味着我们需要添加一个顺序组。但是,这 3 个组件中的每一个都与与其水平的其他组件共享其顺序位置。这意味着我们需要添加 3 个平行组(蓝色矩形)才能分割组件。这 3 个平行组被添加到顺序组(绿色矩形)。然后我们到达第一个标签并添加该组件(绿色矩形)。第二个标签也一样。这是水平组的一对一代码。

对齐方式

我建议在按照您想要的方式获得组层次结构之前根本不要担心对齐问题。在这种情况下(比如哟rs) 在 link 组件中,对齐不起作用,因为组件已经填满了整个 space(自己检查)。设置好所有内容后,设置对齐方式既快速又简单。

Non-layout-related 注释:

  • 在显示框架之前无需调用 revalidaterepaint
  • 调用 setSize(...) 是多余的,因为您稍后会调用 pack(您应该这样做)。
  • 您正在调用 *setVisible(true)twice: once inmain` 和构造函数中的一个。其中一个根本什么都不做,随便选一个。
  • 不要在 textShowLevel 上调用 setPrefferedSize(...)。我什至不确定你传递给它的是什么(一些 ImageObserver 东西)。您可以通过指定其包含的列数或覆盖其 getPreferredSize().
  • 来设置其宽度
  • 不要使用原始类型 JComboBox,您可能需要 <String>
  • 我没有将面板添加到 BorderLayout.EAST,因为它在我的上下文中没有意义。