javaswinglayout-managergrouplayout

Aligning Vertical and Horizontal SequentialGroup in Swing


I have written this code

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

     }

and the result is

enter image description here

What I want is to center the last two labels (totalLevels and levelDispaly) and make them take the three columns. I did many trials and faild. Note that the left side of the pane is empty because I have deleted the unnecessary parts of the code just to concentrate on the problem.


Solution

  • You were close - you fell on a caveat called still developing intuition for GroupLayout. Some people never leave this limbo :)

    enter image description here

    Jokes aside, here is your MCVE with a few changes:

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

    Your problem was in the horizontal group. You restricted your labels to be aligned with the left column. As a general rule of thumb, if you want components which are unrestricted (free to be aligned) in a certain direction, then you need to add another "level" of groups in that direction's hierarchy.

    Specifically, after removing the 2 labels from your horizontal code, you are left with a sequential group (hGroup in your case) which has parallel groups in it. Since the 2 labels are not to be aligned with these groups, they must be added to a sequential group that is not hGroup. That means that if hGroup is sequential, it will have a single "child" parallel group, which will have under it the new sequential group (that replaces hGroup) along with the 2 labels:

    (not showing the components under p1-p3.)

    However, since hGroup now has only 1 child, you can just remove this redundant top-level sequential group and make hGroup that single parallel group. Remember that both your horizontal and vertical groups can be either one of sequential or parallel groups. Don't restrict yourself to having both directions sequential since that just adds redundant groups (it will still work):

    The following are general thinking patterns that can help you with GroupLayout. Do note that there are many ways one can think about these and come up with alternative explanations. After a while with Grouplayout this will become completely intuitive.

    Choosing the right type of parent group

    If you want to know if your vertical/horizontal group should be sequential or parallel, just look at the "governing direction". The "governing direction" is the direction in which you can definitively tell the sequence of the components at the highest level.

    In your case, let's look at vertical group. When looking from top to bottom, you can tell definitively the order of components (or groups thereof) from top to bottom, but not from left to right because the last 2 labels don't have a definitive left to right order with respect to the other components - these 2 labels are determined by alignment. Then

    Vertical group && Vertical governing direction == Sequential group

    For the horizontal group, again you can only tell the top to bottom order:

    Horizontal group && Vertical governing direction == Parallel group

    If there is no single "governing direction", you can choose either. Imagine a 2x2 grid, there are 4 layout hierarchies which will get the same result.

    If you make a mistake and end up with the wrong type of group, you will have a group with only 1 child group of the other type. Then you can just cut that top-level group.

    Creating the group hierarchy

    After choosing the "governing direction", start advancing in that direction and adding the components in that definitive order. If there is more than 1 component with the same place in the order, it means that you need to add a group in the other direction.

    Observe the following schematics for the vertical (red arrow) sequential group:

    enter image description here

    Starting from top to bottom, there are 3 components in the 1st location of the order (red rectangle). That means that we need a parallel group to section (separate) each component in a sub-hierarchy (green lines). This process continues until we reach the first label, at which point there are no "competing" components, so we just add that component (red rectangle). Same for the second label. This is 1-to-1 the code for the vertical group.

    Observe a similar schematics for the horizontal (blue arrow) parallel group:

    enter image description here

    Starting from top to bottom, there are 3 components in the first location. This means that we need to add a sequential group. However, each of these 3 component shares its sequential location with other components horizontal to it. This means that we need to add 3 parallel groups (blue rectangles) in order to section the components. These 3 parallel groups are added to the sequential group (green rectangle). Then we reach the first label and just add that component (green rectangle). Same for the second label. This is 1-to-1 the code for the horizontal group.

    Alignment

    I suggest not worrying about alignment at all until you got the group hierarchies the way you wanted. In the cases (such as yours) where you link components, alignment doesn't play a role anyway since the components already fill the whole space (check for yourself). Setting alignments is quick and easy after everything is set.

    Non-layout-related notes: