ZetCode

GroupLayout layout manager

last modified July 28, 2026

GroupLayout is a flexible built-in Swing layout manager. It is the only built-in manager capable of creating multi-platform layouts. Other built-in managers are either too simplistic or use fixed-size gaps that are not suitable for user interfaces across different platforms and screen resolutions. In addition to GroupLayout, we can also use the third-party MigLayout to create multi-platform layouts in Java.

GroupLayout description

GroupLayout separates components from the actual layout; all components can be set up in one place and the layout definitions in another. The GroupLayout manager was originally created to be used by GUI builders such as NetBeans Matisse. Nevertheless, it can be used to hand code the layout as well.

GroupLayout works with horizontal and vertical layout separately. In one step, we define the layout along the horizontal axis; in the other step, we define the layout along the vertical axis. There are two types of component arrangements: sequential and parallel. In a horizontal layout, a row of components is called a sequential group and a column of components is called a parallel group. In a vertical layout, a column of components is called a sequential group and a row of components a parallel group.

Sequential group (horizontal dimension):

    +----------+  +----------+  +----------+
    |  btn1    |  |  btn2    |  |  btn3    |
    +----------+  +----------+  +----------+

    createSequentialGroup()
        .addComponent(btn1)
        .addComponent(btn2)
        .addComponent(btn3)


Parallel group (horizontal dimension):

    +----------+
    |  btn1    |
    +----------+
    |  btn2    |
    +----------+
    |  btn3    |
    +----------+

    createParallelGroup()
        .addComponent(btn1)
        .addComponent(btn2)
        .addComponent(btn3)

In the vertical dimension, the roles are reversed: components placed one below another form a sequential group, while components placed side by side form a parallel group.

In the GroupLayout manager, we can add three kinds of elements: single components, groups of components, and gaps. Groups are rows and columns of components. Gaps are spaces between the components. The gaps can be created manually or automatically by the manager.

There are four alignment constants: LEADING, TRAILING, CENTER, and BASELINE. These constants are specified in the createParallelGroup method. If we do not specify any of these constants, LEADING is used. In a left-to-right horizontal layout, LEADING means left and TRAILING means right. In a vertical top-to-bottom layout, LEADING means top and TRAILING means bottom. BASELINE is valid only in the vertical layout; it indicates that the elements should be aligned along their text baseline.

When we look at a typical GroupLayout example, we can see chained method calls. This is possible because each method returns the group on which it is called. Thanks to this, we do not need local variables to hold every group. The code is typically indented for better readability.

GroupLayout addComponent method

The addComponent method is the primary way of placing components into a layout. There are several overloaded versions of this method. In this section, we describe the most important one:

addComponent(Component comp, int min, int pref, int max)

The three size parameters control how the component behaves when the window is resized. Each parameter can be an absolute pixel value or one of the following constants:

Constant Description
GroupLayout.DEFAULT_SIZE Uses the component's own size value from the corresponding getMinimumSize, getPreferredSize, or getMaximumSize method.
GroupLayout.PREFERRED_SIZE Uses the component's preferred size for both the minimum and maximum parameters. This prevents the component from growing beyond its natural size.

The following table shows common parameter combinations and their effects on a text field:

Standard Shrink Fixed Explicit
min DEFAULT_SIZE DEFAULT_SIZE PREFERRED_SIZE 100
pref DEFAULT_SIZE DEFAULT_SIZE PREFERRED_SIZE 200
max DEFAULT_SIZE PREFERRED_SIZE PREFERRED_SIZE 300
Effect Shrinks and grows freely. Shrinks but cannot grow. For text fields. Fixed at preferred size. Explicit range 100–300 px.

If we omit the size parameters, the default behaviour is equivalent to passing DEFAULT_SIZE for all three values, allowing the component to determine its own sizing constraints.

GroupLayout gaps

Gaps are spaces between components. GroupLayout has a preferred gap, which is a symbolic gap whose actual size is determined by a LayoutStyle. Preferred gaps automatically adjust to the look and feel of the application. There are three types of preferred gaps: RELATED, UNRELATED, and INDENTED. The difference between related and unrelated gaps is in size—the distance between unrelated components is a bit larger. Indented gap represents a preferred horizontal distance of two components when one is positioned underneath the other with an indent. The main advantage of these gaps is that they are resolution independent; i.e. they have a different size in pixels on different resolution screens. Other built-in managers use fixed-size gaps across all resolutions.

GroupLayout also distinguishes between component gaps and container gaps. The latter are gaps between container edges and neighbouring components. A container gap is inserted with the addContainerGap method.

The GroupLayout has two methods that create automatic gaps. The setAutoCreateGaps method creates automatic gaps between components. The setAutoCreateContainerGaps creates automatic gaps between components and containers. Automatic gaps are preferred gaps, so they automatically adjust to the look and feel of the application.

It may be surprising to see that there are only three predefined gaps. In LaTeX, a high-quality typesetting system, there are only three vertical spaces available: \smallskip, \medskip, and \bigskip. When designing UIs, less is often more and just because we could use many different gap sizes, font sizes, or colours, it does not mean we should do it.

GroupLayout simple example

A component is added to the layout manager with the addComponent method. The parameters are the minimum, preferred, and maximum size values. We can either pass specific absolute values, or provide the GroupLayout.DEFAULT_SIZE or the GroupLayout.PREFERRED_SIZE constants.

The GroupLayout.DEFAULT_SIZE indicates that the corresponding size from the component should be used (e.g., for the minimum parameter, the value is determined from the getMinimumSize method). Similarly, the GroupLayout.PREFERRED_SIZE is determined by calling the component's getPreferredSize method.

com/zetcode/GroupLayoutSimpleEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import java.awt.EventQueue;

import static javax.swing.GroupLayout.Alignment.LEADING;
import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;

public class GroupLayoutSimpleEx extends JFrame {

    public GroupLayoutSimpleEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        var lbl = new JLabel("Name:");
        var field = new JTextField(15);

        GroupLayout.SequentialGroup sg = gl.createSequentialGroup();

        sg.addComponent(lbl).addPreferredGap(RELATED).addComponent(field,
                GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
                GroupLayout.PREFERRED_SIZE);

        gl.setHorizontalGroup(sg);

        GroupLayout.ParallelGroup pg = gl.createParallelGroup(
                LEADING, false);

        pg.addComponent(lbl).addComponent(field);
        gl.setVerticalGroup(pg);

        gl.setAutoCreateContainerGaps(true);

        pack();

        setTitle("Simple");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {
            var ex = new GroupLayoutSimpleEx();
            ex.setVisible(true);
        });
    }
}

In the example, we have a label and a text field. The text field has fixed width; i.e. we cannot stretch it horizontally.

var pane = getContentPane();
var gl = new GroupLayout(pane);
pane.setLayout(gl);

In Swing, a JFrame is a top-level container composed of several layered panes. Components are not placed directly onto the frame; instead, they are placed onto the content pane, which is the middle layer of the frame's root pane. The getContentPane method returns this content pane. The content pane's default layout manager is BorderLayout; we replace it with our own GroupLayout instance.

The GroupLayout constructor takes the container that it will manage as its argument. Once created, we pass the layout manager to the content pane's setLayout method. After this, every component added to the pane is positioned according to the rules defined in the GroupLayout instance. The variable pane is declared with var, which infers Container as its type since getContentPane returns a Container. Similarly, gl is inferred as GroupLayout.

GroupLayout.SequentialGroup sg = gl.createSequentialGroup();

sg.addComponent(lbl).addPreferredGap(RELATED).addComponent(field,
        GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
        GroupLayout.PREFERRED_SIZE);

Both the addComponent and the addPreferredGap methods return the group object; therefore, a chain of method calls can be created. We changed the text field's maximum size to GroupLayout.PREFERRED_SIZE, thus making it non-expandable in the horizontal direction beyond its preferred size. (The difference between the preferred size and the maximum size is the component's tendency to grow. This applies to managers that honour these values.) Changing the value back to GroupLayout.DEFAULT_SIZE will cause the text field to expand in the horizontal dimension.

GroupLayout.ParallelGroup pg = gl.createParallelGroup(
        LEADING, false);

In the vertical layout, the createParallelGroup receives false for its second parameter (resizable). This prevents the text field from growing in the vertical direction. The same effect can be achieved by setting the maximum size parameter of addComponent to GroupLayout.PREFERRED_SIZE, which is used in the vertical layout.

gl.setAutoCreateContainerGaps(true);

The setAutoCreateContainerGaps method creates gaps around the borders of the container.

pack();

The pack method resizes the window to the preferred size of the contained components.

GroupLayout simple example
Figure: GroupLayout simple example

Independent dimensions

GroupLayout works with horizontal and vertical layout separately. Each component has to be defined twice in the layout: once for the horizontal dimension and once for the vertical dimension.

Grid of buttons

We want to have six buttons in the layout. There are two rows; three buttons are put in one row. We need to sketch the layout or create a mental picture in advance and think how we can construct the layout for each dimension.

com/zetcode/GroupLayoutGridEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import java.awt.EventQueue;

public class GroupLayoutGridEx extends JFrame {

    public GroupLayoutGridEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var button1 = new JButton("Button1");
        var button2 = new JButton("Button2");
        var button3 = new JButton("Button3");
        var button4 = new JButton("Button4");
        var button5 = new JButton("Button5");
        var button6 = new JButton("Button6");

        gl.setHorizontalGroup(gl.createParallelGroup()
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3))
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button4)
                        .addComponent(button5)
                        .addComponent(button6))
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button4)
                        .addComponent(button5)
                        .addComponent(button6))
        );

        pack();

        setTitle("Grid of buttons");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutGridEx();
            ex.setVisible(true);
        });
    }
}

The example creates a grid of six buttons. The buttons have numbers so that we can see how they are placed.

gl.setHorizontalGroup(gl.createParallelGroup()
        .addGroup(gl.createSequentialGroup()
                .addComponent(button1)
                .addComponent(button2)
                .addComponent(button3))
        .addGroup(gl.createSequentialGroup()
                .addComponent(button4)
                .addComponent(button5)
                .addComponent(button6))
);

These lines create the horizontal layout: one parallel group of two sequential groups of three buttons. From the perspective of the horizontal dimension, there is one column of two rows of buttons.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup()
                .addComponent(button1)
                .addComponent(button2)
                .addComponent(button3))
        .addGroup(gl.createParallelGroup()
                .addComponent(button4)
                .addComponent(button5)
                .addComponent(button6))
);

The second layout is the vertical layout. Here we create one sequential group of two parallel groups of three buttons. From the perspective of the vertical dimension, parallel groups are rows of buttons placed within one column.

Nested group hierarchy for the 6-button grid:

setHorizontalGroup
└── ParallelGroup
    ├── SequentialGroup  (row 1)
    │   ├── button1
    │   ├── button2
    │   └── button3
    └── SequentialGroup  (row 2)
        ├── button4
        ├── button5
        └── button6


setVerticalGroup
└── SequentialGroup
    ├── ParallelGroup  (column of row 1)
    │   ├── button1
    │   ├── button2
    │   └── button3
    └── ParallelGroup  (column of row 2)
        ├── button4
        ├── button5
        └── button6

The indented tree structure mirrors the chained method calls in the source code. Each nesting level corresponds to one method call in the chain.

Grid of buttons: alternative approach

This is not the only possible way of creating this layout. For example, in a horizontal layout, instead of one parallel group of two sequential groups of three buttons, we could create one sequential group of three parallel groups of two buttons. The following example is an alternative solution.

com/zetcode/GroupLayoutGridEx2.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

import java.awt.EventQueue;

public class GroupLayoutGridEx2 extends JFrame {

    public GroupLayoutGridEx2() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var button1 = new JButton("Button1");
        var button2 = new JButton("Button2");
        var button3 = new JButton("Button3");
        var button4 = new JButton("Button4");
        var button5 = new JButton("Button5");
        var button6 = new JButton("Button6");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button4))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button2)
                        .addComponent(button5))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button3)
                        .addComponent(button6)));

        gl.setVerticalGroup(gl.createParallelGroup()
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button1)
                        .addComponent(button4))
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button2)
                        .addComponent(button5))
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button3)
                        .addComponent(button6)));

        pack();

        setTitle("Grid of buttons");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutGridEx2();
            ex.setVisible(true);
        });
    }
}

There might be more solutions to a specific layout. In our case, both solutions are technically equivalent.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup()
                .addComponent(button1)
                .addComponent(button4))
        .addGroup(gl.createParallelGroup()
                .addComponent(button2)
                .addComponent(button5))
        .addGroup(gl.createParallelGroup()
                .addComponent(button3)
                .addComponent(button6))
);

This horizontal layout creates a sequential group of three parallel groups of two buttons—a row of three columns of buttons.

gl.setVerticalGroup(gl.createParallelGroup()
        .addGroup(gl.createSequentialGroup()
                .addComponent(button1)
                .addComponent(button4))
        .addGroup(gl.createSequentialGroup()
                .addComponent(button2)
                .addComponent(button5))
        .addGroup(gl.createSequentialGroup()
                .addComponent(button3)
                .addComponent(button6))
);

For the vertical layout we create one parallel group of three sequential groups of two buttons—a column of three rows of buttons.

Gap examples

The GroupLayout manager provides several ways to control spacing between components. We can use fixed gaps with explicit pixel sizes, automatic gaps that are computed by the manager based on the platform's look and feel, or resizable gaps that expand and shrink with the window.

Fixed gaps

The following example creates fixed gaps between components.

com/zetcode/GroupLayoutFixedGapsEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
import java.awt.EventQueue;

import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;

public class GroupLayoutFixedGapsEx extends JFrame {

    public GroupLayoutFixedGapsEx() {

        initUI();
    }

    private void initUI() {

        var cpane = getContentPane();
        var gl = new GroupLayout(cpane);
        cpane.setLayout(gl);

        var button1 = new JButton("Button");
        var button2 = new JButton("Button");
        var button3 = new JButton("Button");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl.createParallelGroup()
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(button1)
                                .addPreferredGap(RELATED)
                                .addComponent(button2)
                                .addPreferredGap(RELATED)
                                .addComponent(button3)))
                .addContainerGap()
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addContainerGap()
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3))
                .addContainerGap()
        );

        pack();

        setTitle("Fixed gaps");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutFixedGapsEx();
            ex.setVisible(true);
        });
    }
}

There are three buttons in the layout. We create gaps between the buttons and between buttons and container edges.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addContainerGap()
        .addGroup(gl.createParallelGroup()
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button1)
                        .addPreferredGap(RELATED)
                        .addComponent(button2)
                        .addPreferredGap(RELATED)
                        .addComponent(button3)))
        .addContainerGap()
);

The addContainerGap methods create container gaps at the beginning and at the end of the row. They create preferred, fixed gaps. The addPreferredGap is used to insert a preferred, fixed gap between buttons.

Automatic gaps

In the next example, the gaps are automatically created for us.

com/zetcode/GroupLayoutAutomaticGapsEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

import java.awt.EventQueue;

public class GroupLayoutAutomaticGapsEx extends JFrame {

    public GroupLayoutAutomaticGapsEx() {

        initUI();
    }

    private void initUI() {

        var cpane = getContentPane();
        var gl = new GroupLayout(cpane);
        cpane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var button1 = new JButton("Button");
        var button2 = new JButton("Button");
        var button3 = new JButton("Button");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(button1)
                                .addComponent(button2)
                                .addComponent(button3))));

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3)));

        pack();

        setTitle("Automatic gaps");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutAutomaticGapsEx();
            ex.setVisible(true);
        });
    }
}

Automatic gaps are preferred, fixed gaps. The buttons remain in the initial positions when the window is resized.

gl.setAutoCreateGaps(true);
gl.setAutoCreateContainerGaps(true);

Passing true to these two methods generates automatic gaps. If we are not satisfied with the outcome, we can modify it with our own gaps.

Resizable gaps

In the last example of this section, we create resizable gaps.

com/zetcode/GroupLayoutResizableGapsEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

import java.awt.EventQueue;

import static javax.swing.GroupLayout.DEFAULT_SIZE;
import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;

public class GroupLayoutResizableGapsEx extends JFrame {

    public GroupLayoutResizableGapsEx() {

        initUI();
    }

    private void initUI() {

        var cpane = getContentPane();
        var gl = new GroupLayout(cpane);
        cpane.setLayout(gl);

        var button1 = new JButton("Button");
        var button2 = new JButton("Button");
        var button3 = new JButton("Button");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE)
                .addGroup(gl.createParallelGroup()
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(button1)
                                .addPreferredGap(RELATED, GroupLayout.DEFAULT_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(button2)
                                .addPreferredGap(RELATED, GroupLayout.DEFAULT_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(button3)))
                .addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE));

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE)
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3))
                .addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE));

        pack();

        setTitle("Resizable gaps");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutResizableGapsEx();
            ex.setVisible(true);
        });
    }
}

At the beginning and at the end of each dimension, we create resizable container gaps. This centres the components in the layout. Resizable gaps are also placed between button components.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE)
        .addGroup(gl.createParallelGroup()
                .addGroup(gl.createSequentialGroup()
                        .addComponent(button1)
                        .addPreferredGap(RELATED,
                                GroupLayout.DEFAULT_SIZE,
                                Short.MAX_VALUE)
                        .addComponent(button2)
                        .addPreferredGap(RELATED,
                                GroupLayout.DEFAULT_SIZE,
                                Short.MAX_VALUE)
                        .addComponent(button3)))
        .addContainerGap(DEFAULT_SIZE, Integer.MAX_VALUE)
);

The container gaps at both ends and the preferred gaps between buttons expand proportionally. Their maximum size is set to Short.MAX_VALUE or Integer.MAX_VALUE, which allows them to grow as the window expands.

The difference between the container gap's default size and Integer.MAX_VALUE is its ability to grow. Putting a resizable gap at both ends centers the components horizontally. When the window is resized, all extra space is distributed equally before and after the components.

Resizable gaps before and after window resize:

Before:
|[--gap--]  [btn1]  [--gap--]  [btn2]  [--gap--]  [btn3]  [--gap--]|

After window grows:
|[----gap----]  [btn1]  [----gap----]  [btn2]  [----gap----]  [btn3]  [----gap----]|
Resizable gaps
Figure: Resizable gaps

Indent

In addition to related and unrelated gaps, we have indented gaps in GroupLayout. Indented gaps are horizontal distances of two components where one of them is positioned underneath the other with an indent.

com/zetcode/GroupLayoutIndentEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.LayoutStyle;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import java.awt.EventQueue;

import static javax.swing.GroupLayout.Alignment.BASELINE;

public class GroupLayoutIndentEx extends JFrame {

    public GroupLayoutIndentEx() {

        initUI();
    }

    private void initUI() {

        var pane = (JComponent) getContentPane();
        pane.setBorder(new EmptyBorder(15, 15, 85, 85));

        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        var ampLbl = new JLabel("Amplitude");
        var offLbl = new JLabel("Off-set");
        var freqLbl = new JLabel("Frequency");
        var phsLbl = new JLabel("Phase");
        var unit1 = new JLabel("V");
        var unit2 = new JLabel("V");
        var unit3 = new JLabel("Hz");
        var unit4 = new JLabel("rad");

        var field1 = new JTextField(12);
        var field2 = new JTextField(12);
        var field3 = new JTextField(12);
        var field4 = new JTextField(12);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        gl.setHorizontalGroup(gl.createParallelGroup()
                .addComponent(ampLbl)
                .addGroup(gl.createSequentialGroup()
                        .addPreferredGap(ampLbl, field1,
                                LayoutStyle.ComponentPlacement.INDENT)
                        .addComponent(field1)
                        .addComponent(unit1))
                .addComponent(offLbl)
                .addGroup(gl.createSequentialGroup()
                        .addPreferredGap(offLbl, field2,
                                LayoutStyle.ComponentPlacement.INDENT)
                        .addComponent(field2)
                        .addComponent(unit2))
                .addComponent(freqLbl)
                .addGroup(gl.createSequentialGroup()
                        .addPreferredGap(freqLbl, field3,
                                LayoutStyle.ComponentPlacement.INDENT)
                        .addComponent(field3)
                        .addComponent(unit3))
                .addComponent(phsLbl)
                .addGroup(gl.createSequentialGroup()
                        .addPreferredGap(phsLbl, field4,
                                LayoutStyle.ComponentPlacement.INDENT)
                        .addComponent(field4)
                        .addComponent(unit4)));

        gl.setVerticalGroup(gl.createSequentialGroup()
                        .addComponent(ampLbl)
                        .addGroup(gl.createParallelGroup(BASELINE)
                                .addComponent(field1)
                                .addComponent(unit1))
                        .addComponent(offLbl)
                        .addGroup(gl.createParallelGroup(BASELINE)
                                .addComponent(field2)
                                .addComponent(unit2))
                        .addComponent(freqLbl)
                        .addGroup(gl.createParallelGroup(BASELINE)
                                .addComponent(field3)
                                .addComponent(unit3))
                        .addComponent(phsLbl)
                        .addGroup(gl.createParallelGroup(BASELINE)
                                .addComponent(field4)
                                .addComponent(unit4)));

        gl.linkSize(field1, field2, field3, field4);

        pack();

        setTitle("Indents");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutIndentEx();
            ex.setVisible(true);
        });
    }
}

In the example, we have indented text fields below the label components.

var pane = (JComponent) getContentPane();
pane.setBorder(new EmptyBorder(15, 15, 85, 85));

We set some borders for the content pane.

gl.setHorizontalGroup(gl.createParallelGroup()
        .addComponent(ampLbl)
        .addGroup(gl.createSequentialGroup()
                .addPreferredGap(ampLbl, field1,
                        LayoutStyle.ComponentPlacement.INDENT)
                .addComponent(field1)
                .addComponent(unit1))
        ...

Indented gaps are created in the horizontal layout within a sequential group. We use the LayoutStyle.ComponentPlacement.INDENT component placement type.

Indent
Figure: Indent

Alignments

The GroupLayout manager has the following alignment constants: LEADING, CENTER, TRAILING, and BASELINE. These constants are passed to the createParallelGroup method.

Horizontal alignment

In the following example we align buttons horizontally; horizontal alignment is defined in the horizontal layout.

com/zetcode/GroupLayoutHorAlignEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import java.awt.EventQueue;
import java.awt.Font;

import static javax.swing.GroupLayout.Alignment.CENTER;
import static javax.swing.GroupLayout.Alignment.LEADING;
import static javax.swing.GroupLayout.Alignment.TRAILING;

public class GroupLayoutHorAlignEx extends JFrame {

    public GroupLayoutHorAlignEx() {

        initUI();
    }

    private void initUI() {

        var pane = (JComponent) getContentPane();
        pane.setBorder(new EmptyBorder(20, 20, 20, 20));

        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var largeFont = new Font(Font.SANS_SERIF, Font.PLAIN, 18);
        UIManager.put("Button.font", largeFont);
        UIManager.put("Label.font", largeFont);

        var leftLbl = new JLabel("[Left]");
        var button1 = new JButton("--");
        var button2 = new JButton("----");
        var button3 = new JButton("--------");
        var button4 = new JButton("----");
        var button5 = new JButton("--");

        var centLbl = new JLabel("[Center]");
        var button6 = new JButton("--");
        var button7 = new JButton("----");
        var button8 = new JButton("--------");
        var button9 = new JButton("----");
        var button10 = new JButton("--");

        var rightLbl = new JLabel("[Right]");
        var button11 = new JButton("--");
        var button12 = new JButton("----");
        var button13 = new JButton("--------");
        var button14 = new JButton("----");
        var button15 = new JButton("--");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(LEADING)
                        .addComponent(leftLbl)
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3)
                        .addComponent(button4)
                        .addComponent(button5))
                .addGroup(gl.createParallelGroup(CENTER)
                        .addComponent(centLbl)
                        .addComponent(button6)
                        .addComponent(button7)
                        .addComponent(button8)
                        .addComponent(button9)
                        .addComponent(button10))
                .addGroup(gl.createParallelGroup(TRAILING)
                        .addComponent(rightLbl)
                        .addComponent(button11)
                        .addComponent(button12)
                        .addComponent(button13)
                        .addComponent(button14)
                        .addComponent(button15)));

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(leftLbl)
                        .addComponent(centLbl)
                        .addComponent(rightLbl))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button6)
                        .addComponent(button11))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button2)
                        .addComponent(button7)
                        .addComponent(button12))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button3)
                        .addComponent(button8)
                        .addComponent(button13))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button4)
                        .addComponent(button9)
                        .addComponent(button14))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button5)
                        .addComponent(button10)
                        .addComponent(button15)));

        pack();

        setTitle("Horizontal alignment");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutHorAlignEx();
            ex.setVisible(true);
        });
    }
}

We have three groups of buttons. They are horizontally aligned to the left, centre, and right.

var largeFont = new Font(Font.SANS_SERIF, Font.PLAIN, 18);
UIManager.put("Button.font", largeFont);
UIManager.put("Label.font", largeFont);

We set the font size of the buttons and labels to a larger size.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup(LEADING)
                .addComponent(leftLbl)
                .addComponent(button1)
                .addComponent(button2)
                .addComponent(button3)
                .addComponent(button4)
                .addComponent(button5))
        .addGroup(gl.createParallelGroup(CENTER)
                .addComponent(centLbl)
                .addComponent(button6)
                .addComponent(button7)
                .addComponent(button8)
                .addComponent(button9)
                .addComponent(button10))
        .addGroup(gl.createParallelGroup(TRAILING)
                .addComponent(rightLbl)
                .addComponent(button11)
                .addComponent(button12)
                .addComponent(button13)
                .addComponent(button14)
                .addComponent(button15))
);

In the horizontal direction, the layout consists of one sequential group of three parallel groups of components. The LEADING alignment in the horizontal direction aligns the components to the left. The components in the second group are aligned to the centre. The TRAILING constant aligns the third group to the right.

Horizontal alignment
Figure: Horizontal alignment

Vertical alignment

The next example aligns components vertically. Components can be aligned vertically in the vertical layout.

com/zetcode/GroupLayoutVertAlignEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;

import java.awt.EventQueue;
import java.awt.Font;

import static javax.swing.GroupLayout.Alignment.CENTER;
import static javax.swing.GroupLayout.Alignment.LEADING;
import static javax.swing.GroupLayout.Alignment.TRAILING;

public class GroupLayoutVertAlignEx extends JFrame {

    public GroupLayoutVertAlignEx() {

        initUI();
    }

    private void initUI() {

        var pane = (JComponent) getContentPane();
        pane.setBorder(new EmptyBorder(20, 20, 20, 20));

        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var largeFont = new Font(Font.SANS_SERIF, Font.PLAIN, 18);
        UIManager.put("Button.font", largeFont);
        UIManager.put("Label.font", largeFont);

        var leftLbl = new JLabel("[Top]");
        var button1 = new JButton("1");
        var button2 = new JButton("<html>1<br>2</html>");
        var button3 = new JButton("<html>1<br>2<br>3</html>");

        var centLbl = new JLabel("[Center]");
        var button4 = new JButton("1");
        var button5 = new JButton("<html>1<br>2</html>");
        var button6 = new JButton("<html>1<br>2<br>3</html>");

        var rightLbl = new JLabel("[Bottom]");
        var button7 = new JButton("1");
        var button8 = new JButton("<html>1<br>2</html>");
        var button9 = new JButton("<html>1<br>2<br>3</html>");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(leftLbl)
                        .addComponent(centLbl)
                        .addComponent(rightLbl))
                .addGroup(gl.createParallelGroup()
                        .addComponent(button1)
                        .addComponent(button4)
                        .addComponent(button7))
                .addGroup(gl.createParallelGroup(LEADING, false)
                        .addComponent(button2)
                        .addComponent(button5)
                        .addComponent(button8))
                .addGroup(gl.createParallelGroup(LEADING, false)
                        .addComponent(button3)
                        .addComponent(button6)
                        .addComponent(button9))
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(LEADING)
                        .addComponent(leftLbl)
                        .addComponent(button1)
                        .addComponent(button2)
                        .addComponent(button3))
                .addGroup(gl.createParallelGroup(CENTER)
                        .addComponent(centLbl)
                        .addComponent(button4)
                        .addComponent(button5)
                        .addComponent(button6))
                .addGroup(gl.createParallelGroup(TRAILING)
                        .addComponent(rightLbl)
                        .addComponent(button7)
                        .addComponent(button8)
                        .addComponent(button9))
        );

        pack();

        setTitle("Vertical alignment");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutVertAlignEx();
            ex.setVisible(true);
        });
    }
}

In the horizontal direction, the layout consists of a sequential group of four parallel groups of three components. In the vertical direction, we have a sequential group of three parallel groups of four components.

var button2 = new JButton("<html>1<br>2</html>");
var button3 = new JButton("<html>1<br>2<br>3</html>");

HTML tags are used to create buttons of different heights. When we use HTML code, the maximum width of a button changes to Integer.MAX_VALUE. To prevent buttons with HTML code from growing horizontally, some parallel groups use false for their resizable parameter.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup(LEADING)
                .addComponent(leftLbl)
                .addComponent(button1)
                .addComponent(button2)
                .addComponent(button3))
        .addGroup(gl.createParallelGroup(CENTER)
                .addComponent(centLbl)
                .addComponent(button4)
                .addComponent(button5)
                .addComponent(button6))
        .addGroup(gl.createParallelGroup(TRAILING)
                .addComponent(rightLbl)
                .addComponent(button7)
                .addComponent(button8)
                .addComponent(button9))
);

In the vertical direction, we create a sequential group of three parallel groups of four components. The LEADING constant aligns the first group to the top. The CENTER constant aligns the second group vertically to the centre. The TRAILING constant aligns the third group to the bottom.

Vertical alignment
Figure: Vertical alignment

Baseline alignment

Baseline alignment is aligning components along the baseline of the text that they contain. The following example aligns two components along their baseline.

com/zetcode/GroupLayoutBaselineEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.WindowConstants;
import java.awt.EventQueue;

import static javax.swing.GroupLayout.Alignment.BASELINE;

public class GroupLayoutBaselineEx extends JFrame {

    private JLabel display;
    private JComboBox<String> box;
    private String[] distros;

    public GroupLayoutBaselineEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        distros = new String[] {"Easy", "Medium", "Hard"};
        box = new JComboBox<>(distros);
        display = new JLabel("Level:");

        gl.setAutoCreateContainerGaps(true);
        gl.setAutoCreateGaps(true);

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(display)
                .addComponent(box,
                        GroupLayout.DEFAULT_SIZE,
                        GroupLayout.DEFAULT_SIZE,
                        GroupLayout.PREFERRED_SIZE)
        );

        gl.setVerticalGroup(gl.createParallelGroup(BASELINE)
                .addComponent(box, GroupLayout.DEFAULT_SIZE,
                        GroupLayout.DEFAULT_SIZE,
                        GroupLayout.PREFERRED_SIZE)
                .addComponent(display)
        );

        pack();

        setTitle("Baseline alignment");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutBaselineEx();
            ex.setVisible(true);
        });
    }
}

We have a label and a combo box. Both components contain text. We align these two components along the baseline of their text.

gl.setVerticalGroup(gl.createParallelGroup(BASELINE)
        .addComponent(box, GroupLayout.DEFAULT_SIZE,
                GroupLayout.DEFAULT_SIZE,
                GroupLayout.PREFERRED_SIZE)
        .addComponent(display)
);

The baseline alignment is achieved with the BASELINE parameter passed to the createParallelGroup method in the vertical layout. This ensures the label and the combo box are aligned along the baseline of their text.

GroupLayout baseline alignment
Figure: GroupLayout baseline alignment

Corner buttons example

The following example places two buttons in the bottom-right corner of the window. The buttons are made the same size.

com/zetcode/GroupLayoutCornerButtonsEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingConstants;
import javax.swing.WindowConstants;
import java.awt.Dimension;
import java.awt.EventQueue;

import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;

public class GroupLayoutCornerButtonsEx extends JFrame {

    public GroupLayoutCornerButtonsEx() {

        initUI();
    }

    private void initUI() {

        setPreferredSize(new Dimension(300, 200));

        var cpane = getContentPane();
        var gl = new GroupLayout(cpane);
        cpane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var okButton = new JButton("OK");
        var closeButton = new JButton("Close");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addPreferredGap(RELATED,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(okButton)
                .addComponent(closeButton)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addPreferredGap(RELATED,
                        GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(gl.createParallelGroup()
                        .addComponent(okButton)
                        .addComponent(closeButton))
        );

        gl.linkSize(SwingConstants.HORIZONTAL, okButton, closeButton);

        pack();

        setTitle("Buttons");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutCornerButtonsEx();
            ex.setVisible(true);
        });
    }
}

The example creates the corner buttons with the GroupLayout manager.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addPreferredGap(RELATED,
                GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addComponent(okButton)
        .addComponent(closeButton)
);

In the horizontal layout, we add a stretchable gap followed by the two buttons. The stretchable gap pushes the buttons to the right edge of the window. The gap is created with the addPreferredGap method, which takes the gap type, the preferred size, and the maximum size. The difference between the maximum and preferred values determines how much the gap can stretch. When both values are equal, the gap has a fixed size.

Stretchable gap pushing buttons to the corner:

Horizontal layout:
|[←─stretchable gap──]  [OK]  [Close]|

Vertical layout:
|[←─stretchable gap──]|
|[OK]  [Close]          |

In the horizontal direction, the stretchable gap occupies all available space to the left of the buttons, pushing them to the right edge. In the vertical direction, a stretchable gap above the button row pushes them to the bottom edge. This places the buttons in the bottom-right corner.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addPreferredGap(RELATED,
                GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addGroup(gl.createParallelGroup()
                .addComponent(okButton)
                .addComponent(closeButton))
);

In a vertical layout, we add a stretchable gap and a parallel group of two components. Again, the gap pushes the group of buttons to the bottom.

gl.linkSize(SwingConstants.HORIZONTAL, okButton, closeButton);

The linkSize method makes both buttons the same size. We only need to set their width because their height is already the same by default.

GroupLayout corner buttons
Figure: GroupLayout corner buttons

Password example

The following layout is common in form-based applications that contain label and text field pairs.

com/zetcode/GroupLayoutPasswordEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import java.awt.EventQueue;

import static javax.swing.GroupLayout.Alignment.BASELINE;
import static javax.swing.GroupLayout.Alignment.TRAILING;

public class GroupLayoutPasswordEx extends JFrame {

    public GroupLayoutPasswordEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        var serviceLbl = new JLabel("Service:");
        var userNameLbl = new JLabel("User name:");
        var passwordLbl = new JLabel("Password:");

        var field1 = new JTextField(10);
        var field2 = new JTextField(10);
        var field3 = new JTextField(10);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(TRAILING)
                        .addComponent(serviceLbl)
                        .addComponent(userNameLbl)
                        .addComponent(passwordLbl))
                .addGroup(gl.createParallelGroup()
                        .addComponent(field1)
                        .addComponent(field2)
                        .addComponent(field3))
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(BASELINE)
                        .addComponent(serviceLbl)
                        .addComponent(field1))
                .addGroup(gl.createParallelGroup(BASELINE)
                        .addComponent(userNameLbl)
                        .addComponent(field2))
                .addGroup(gl.createParallelGroup(BASELINE)
                        .addComponent(passwordLbl)
                        .addComponent(field3))
        );

        pack();

        setTitle("Password application");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutPasswordEx();
            ex.setVisible(true);
        });
    }
}

The requirements are: labels must be right-aligned horizontally, and each label must be aligned vertically to the baseline of its corresponding text field.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup(TRAILING)
                .addComponent(serviceLbl)
                .addComponent(userNameLbl)
                .addComponent(passwordLbl))
        .addGroup(gl.createParallelGroup()
                .addComponent(field1)
                .addComponent(field2)
                .addComponent(field3))
);

Horizontally, the layout consists of two parallel groups packed inside a sequential group. The labels and text fields are placed in separate parallel groups. The label group uses the GroupLayout.Alignment.TRAILING alignment, which right-aligns the labels.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup(BASELINE)
                .addComponent(serviceLbl)
                .addComponent(field1))
        .addGroup(gl.createParallelGroup(BASELINE)
                .addComponent(userNameLbl)
                .addComponent(field2))
        .addGroup(gl.createParallelGroup(BASELINE)
                .addComponent(passwordLbl)
                .addComponent(field3))
);

In the vertical layout, we ensure that each label is aligned with its corresponding text field along the text baseline. This is done by grouping each label and its field into a parallel group with GroupLayout.Alignment.BASELINE alignment.

GroupLayout password example
Figure: GroupLayout password example

Windows example

The following example creates the Windows layout using the GroupLayout manager. The layout comes from the JDeveloper application.

com/zetcode/GroupLayoutWindowsEx.java
package com.zetcode;

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.EventQueue;

public class GroupLayoutWindowsEx extends JFrame {

    public GroupLayoutWindowsEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var windows = new JLabel("Windows");

        var area = new JTextArea(17, 25);
        area.setEditable(false);
        area.setBorder(BorderFactory.createLineBorder(Color.gray));

        var activateButton = new JButton("Activate");
        var closeButton = new JButton("Close");
        var helpButton = new JButton("Help");
        var okButton = new JButton("OK");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(windows)
                        .addComponent(area)
                        .addComponent(helpButton))
                .addGroup(gl.createParallelGroup()
                        .addComponent(activateButton)
                        .addComponent(closeButton)
                        .addComponent(okButton))
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(windows)
                .addGroup(gl.createParallelGroup()
                        .addComponent(area)
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(activateButton)
                                .addComponent(closeButton)))
                .addGroup(gl.createParallelGroup()
                        .addComponent(helpButton)
                        .addComponent(okButton))
        );

        gl.linkSize(activateButton, closeButton,
                helpButton, okButton);

        pack();

        setTitle("Windows");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutWindowsEx();
            ex.setVisible(true);
        });
    }
}

The layout consists of one label, four buttons, and a text area. The buttons have the same size.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup()
                .addComponent(windows)
                .addComponent(area)
                .addComponent(helpButton))
        .addGroup(gl.createParallelGroup()
                .addComponent(activateButton)
                .addComponent(closeButton)
                .addComponent(okButton))
);

The horizontal layout consists of a sequential group of two parallel groups of three components.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addComponent(windows)
        .addGroup(gl.createParallelGroup()
                .addComponent(area)
                .addGroup(gl.createSequentialGroup()
                        .addComponent(activateButton)
                        .addComponent(closeButton)))
        .addGroup(gl.createParallelGroup()
                .addComponent(helpButton)
                .addComponent(okButton))
);

The vertical layout starts with a single component. Then, we add a parallel group of a single component and a sequential group of two components. Finally, we add a parallel group of two components.

Windows example
Figure: Windows example

Review example

The Review example demonstrates the usage of the BASELINE alignment.

com/zetcode/GroupLayoutReviewEx.java
package com.zetcode;

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.EventQueue;

import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
import static javax.swing.GroupLayout.Alignment.BASELINE;

public class GroupLayoutReviewEx extends JFrame {

    public GroupLayoutReviewEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var title = new JLabel("Title");
        var author = new JLabel("Author");
        var review = new JLabel("Review");

        var field1 = new JTextField();
        var field2 = new JTextField();
        var area = new JTextArea(12, 18);
        area.setBorder(BorderFactory.createLineBorder(Color.gray));

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(title)
                        .addComponent(author)
                        .addComponent(review))
                .addGroup(gl.createParallelGroup()
                        .addComponent(field1)
                        .addComponent(field2)
                        .addComponent(area))
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(BASELINE)
                        .addComponent(title)
                        .addComponent(field1))
                .addGroup(gl.createParallelGroup(BASELINE)
                        .addComponent(author)
                        .addComponent(field2))
                .addPreferredGap(UNRELATED)
                .addGroup(gl.createParallelGroup()
                        .addComponent(review)
                        .addComponent(area))
        );

        pack();

        setTitle("Review");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutReviewEx();
            ex.setVisible(true);
        });
    }
}

This layout consists of three labels, two text fields, and a text area.

gl.setHorizontalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup()
                .addComponent(title)
                .addComponent(author)
                .addComponent(review))
        .addGroup(gl.createParallelGroup()
                .addComponent(field1)
                .addComponent(field2)
                .addComponent(area))
);

In this code excerpt, we create two parallel groups inside a sequential group. In the first parallel group, the labels have different sizes. The LEADING is the default alignment of a ParallelGroup. For the horizontal axis with a left-to-right orientation, this means alignment to the left edge. In the second parallel group, all components have the same width, so the alignment has no effect.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addGroup(gl.createParallelGroup(BASELINE)
                .addComponent(title)
                .addComponent(field1))
        .addGroup(gl.createParallelGroup(BASELINE)
                .addComponent(author)
                .addComponent(field2))
        .addPreferredGap(UNRELATED)
        .addGroup(gl.createParallelGroup()
                .addComponent(review)
                .addComponent(area))
);

The BASELINE constant makes the components aligned along their baselines. It also prevents the text fields from growing vertically.

Review example
Figure: Review example

Go To Class example

The next example creates the Go To Class window layout using the GroupLayout manager. The layout comes from the Eclipse application.

com/zetcode/GroupLayoutGoToClassEx.java
package com.zetcode;

import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

import java.awt.Color;
import java.awt.EventQueue;

import static javax.swing.GroupLayout.Alignment.BASELINE;
import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;

public class GroupLayoutGoToClassEx extends JFrame {

    public GroupLayoutGoToClassEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var name = new JLabel("Class Name");
        var field = new JTextField();
        var matching = new JLabel("Matching classes");

        var area = new JTextArea(20, 20);
        area.setBorder(BorderFactory.createLineBorder(Color.gray));

        var sensitive = new JCheckBox("Case Sensitive");
        var nested = new JCheckBox("Nested Classes");
        var nonproject = new JCheckBox("Non-Project Classes");
        var okButton = new JButton("OK");
        var closeButton = new JButton("Close");

        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(name)
                                .addComponent(field))
                        .addComponent(matching)
                        .addComponent(area)
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(sensitive)
                                .addComponent(nested)
                                .addComponent(nonproject))
                        .addGroup(gl.createSequentialGroup()
                                .addPreferredGap(RELATED, GroupLayout.DEFAULT_SIZE,
                                        Short.MAX_VALUE)
                                .addComponent(okButton)
                                .addComponent(closeButton))));

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(BASELINE)
                        .addComponent(name)
                        .addComponent(field))
                .addPreferredGap(UNRELATED)
                .addComponent(matching)
                .addPreferredGap(UNRELATED)
                .addComponent(area)
                .addPreferredGap(UNRELATED)
                .addGroup(gl.createParallelGroup()
                        .addComponent(sensitive)
                        .addComponent(nested)
                        .addComponent(nonproject))
                .addPreferredGap(UNRELATED)
                .addGroup(gl.createParallelGroup()
                        .addComponent(okButton)
                        .addComponent(closeButton)));

        gl.linkSize(okButton, closeButton);

        pack();

        setTitle("Go To Class");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutGoToClassEx();
            ex.setVisible(true);
        });
    }
}

In the layout, we have two labels, one text field, one text area, three check boxes, and two buttons.

.addGroup(gl.createSequentialGroup()
        .addPreferredGap(RELATED,
                GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        .addComponent(okButton)
        .addComponent(closeButton))

The resizable preferred gap pushes the two buttons to the right of the window. The gap is resizable between GroupLayout.DEFAULT_SIZE and Short.MAX_VALUE.

.addPreferredGap(UNRELATED)
.addComponent(matching)
.addPreferredGap(UNRELATED)

When we set the automatic creation of gaps, the manager creates gaps between the components for us. We can modify the outcome with additional gaps. Here the code adds fixed preferred gaps above and below the Matching classes label.

Go To Class example
Figure: Go To Class example

Tags example

The next example has two list components. One of them stores available tags, the other one selected tags. There is one button between the list components.

com/zetcode/GroupLayoutTagsEx.java
package com.zetcode;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;

import java.awt.EventQueue;

import static javax.swing.GroupLayout.Alignment.CENTER;
import static javax.swing.GroupLayout.DEFAULT_SIZE;

public class GroupLayoutTagsEx extends JFrame {

    public GroupLayoutTagsEx() {

        initUI();
    }

    private void initUI() {

        var pane = getContentPane();
        var gl = new GroupLayout(pane);
        pane.setLayout(gl);

        gl.setAutoCreateGaps(true);
        gl.setAutoCreateContainerGaps(true);

        var avLbl = new JLabel("Available");
        var tagsLbl = new JLabel("Tags");
        var selLbl = new JLabel("Selected");

        var newBtn = new JButton("New");
        var moveBtn = new JButton(">>");
        var remBtn = new JButton("Remove");

        var leftList = new JList<>();
        var spleft = new JScrollPane(leftList);
        var rightList = new JList<>();
        var spright = new JScrollPane(rightList);

        gl.setHorizontalGroup(gl.createParallelGroup(CENTER)
                .addComponent(tagsLbl)
                .addGroup(gl.createSequentialGroup()
                        .addGroup(gl.createParallelGroup(CENTER)
                                .addComponent(avLbl)
                                .addComponent(spleft, 100, 200,
                                        Short.MAX_VALUE)
                                .addComponent(newBtn))
                        .addComponent(moveBtn)
                        .addGroup(gl.createParallelGroup(CENTER)
                                .addComponent(selLbl)
                                .addComponent(spright, 100, 200,
                                        Short.MAX_VALUE)
                                .addComponent(remBtn)))
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(tagsLbl)
                .addGroup(gl.createParallelGroup(CENTER)
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(avLbl)
                                .addComponent(spleft, 100, 250,
                                        DEFAULT_SIZE)
                                .addComponent(newBtn))
                        .addComponent(moveBtn)
                        .addGroup(gl.createSequentialGroup()
                                .addComponent(selLbl)
                                .addComponent(spright, 100, 250,
                                        DEFAULT_SIZE)
                                .addComponent(remBtn)))
        );

        pack();

        setTitle("Tags");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(() -> {

            var ex = new GroupLayoutTagsEx();
            ex.setVisible(true);
        });
    }
}

The components that need to be centred are placed into parallel groups with the GroupLayout.Alignment.CENTER alignment.

gl.setHorizontalGroup(gl.createParallelGroup(CENTER)
        .addComponent(tagsLbl)
        .addGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(CENTER)
                        .addComponent(avLbl)
                        .addComponent(spleft, 100, 200, Short.MAX_VALUE)
                        .addComponent(newBtn))
                .addComponent(moveBtn)
                .addGroup(gl.createParallelGroup(CENTER)
                        .addComponent(selLbl)
                        .addComponent(spright, 100, 200, Short.MAX_VALUE)
                        .addComponent(remBtn)))
);

The base group of the horizontal layout is a centred parallel group. This causes the tags label to be centred. The layout is then divided into a sequential group of two parallel groups and a button located between them.

gl.setVerticalGroup(gl.createSequentialGroup()
        .addComponent(tagsLbl)
        .addGroup(gl.createParallelGroup(CENTER)
                .addGroup(gl.createSequentialGroup()
                        .addComponent(avLbl)
                        .addComponent(spleft, 100, 250, DEFAULT_SIZE)
                        .addComponent(newBtn))
                .addComponent(moveBtn)
                .addGroup(gl.createSequentialGroup()
                        .addComponent(selLbl)
                        .addComponent(spright, 100, 250, DEFAULT_SIZE)
                        .addComponent(remBtn)))
);

The vertical layout is a sequential group of a component and a centred parallel group. Here the CENTER alignment makes the Move button vertically centred.

Tags example
Figure: Tags example

In this article, we have explored the built-in GroupLayout manager. We covered horizontal and vertical group definitions, resolution-independent gaps (fixed, automatic, and resizable), indented gaps, parallel and sequential grouping, alignment options (leading, centre, trailing, and baseline), link sizing, and several practical examples including grid layouts, form layouts, and complex window arrangements from real-world applications.

List all Java tutorials.