GroupLayout
is a layout manager that was developed for GUI builders such as Matisse, the GUI builder provided with the NetBeans IDE.
Although the layout manager was originally designed to suit the GUI builder needs, it also works well for manual coding.
This discussion will teach you how GroupLayout
works and show you
how you can use GroupLayout
to build GUIs, whether you choose to
use a GUI builder like Matisse or write your own code.
GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout
, then GridBagLayout
is recommended as the next most flexible and powerful layout manager.
GroupLayout
works with the
horizontal and vertical layouts separately. The layout is defined for each dimension independently. You do not need to worry about the vertical
dimension when defining the horizontal layout, and vice versa, as the layout along each axis is totally independent
of the layout along the other axis.
When focusing on just one dimension, you only have to solve half the problem at one time. This is easier than handling both dimensions at once.
This means, of course, that each component needs to be defined twice in the layout. If you forget to do this, GroupLayout
will generate
an exception.
GroupLayout
uses two types of arrangements -- sequential and parallel, combined with
hierarchical composition.
BoxLayout
or FlowLayout
would do along one
axis. The position of each component is defined as being relative to the
preceding component.
Usually, components placed in parallel in one dimension are in a sequence in the other, so that they do not overlap.
What makes these two arrangements powerful is that they can be nested
hierarchically. For this purpose GroupLayout
defines layout
groups.
A group is either sequential or parallel and may contain components, other groups, and gaps (discussed below).
The size of a sequential group is the sum of the sizes of the contained elements, and the size of a parallel group corresponds to the size of the largest element (although, depending on the elements and where the baseline lands, the size of a baseline-aligned group may be a bit larger than the largest element).
Defining a layout means defining how the components should be grouped by combining the sequential and parallel arrangements.
Let us use a simple example to see how it works in practice.
Let us start with something simple, just three components in a row:
We will express this layout using groups. Starting with the horizontal axis it is easy to see there is a sequential group of 3 components arranged from left to right. Along the vertical axis there is a parallel group of the same 3 components with the same location, size, and baseline:
In pseudo code, the layout specification might look like this (the real code is in the Writing Code section below):
horizontal layout = sequential group { c1, c2, c3 } vertical layout = parallel group (BASELINE) { c1, c2, c3 }
This illustrates a principle mentioned earlier: components grouped sequentially in one dimension usually form a parallel group in the other dimension.
Now let us add one more component, C4, left-aligned with C3:
Along the horizontal axis the new component occupies the same horizontal space as C3 so that it forms a parallel group with C3. Along the vertical axis C4 forms a sequential group with the original parallel group of the three components.
In pseudo code, the layout specification now looks like this:
horizontal layout = sequential group { c1, c2, parallel group (LEFT) { c3, c4 } } vertical layout = sequential group { parallel group (BASELINE) { c1, c2, c3 }, c4 }
Now you understand the most important aspects of designing layouts with GroupLayout
. There are just a few more details
to explain: how to add gaps, how to define size and resize behavior, how to define justified layout, and how to write real code.
A gap can be thought of as an invisible component of a certain size. Gaps of arbitrary size can be added to groups just like components or other groups. Using gaps you can precisely control the distance between components or from the container border.
GroupLayout
also defines automatic gaps that correspond to preferred distances between neighboring components
(or between a component and container border).
The size of such a gap is computed dynamically based on the look and feel the
application is using (the LayoutStyle
class is used for this). There
are two advantages to using automatic (preferred) gaps: you do not have to specify the
pixel sizes of the gaps, and they automatically adjust to the look and feel the UI runs with, reflecting the
actual look and feel guidelines.
GroupLayout
distinguishes between
(a) the preferred gap between two components and
(b) the preferred gap between a component and the
container border.
There are corresponding methods in the GroupLayout
API for adding these
gaps (addPreferredGap
and addContainerGap
). There are
three types of component gaps: related, unrelated and indented.
The LayoutStyle.ComponentPlacement
enum defines corresponding constants to be used as parameters of the
addPreferredGap
method: RELATED
, UNRELATED
and INDENT
. The difference between
related and unrelated gaps is just in size—the distance between unrelated components is a bit
bigger. Indented represents a preferred horizontal distance of two
components when one of them is positioned underneath the second with an indent.
As mentioned above, GroupLayout
can insert gaps
automatically—if you do not add your
own gaps explicitly, it adds the related preferred gaps for you. This is not the
default behavior, however. You have to turn this feature on by invoking setAutoCreateGaps(true)
and setAutoCreateContainerGaps(true)
on the GroupLayout
.
Then you will get correct spacing automatically.
Now, let us take a look at the actual code to create the layout described above.
Let us assume we have a container named panel
and the same four
components already presented (c1
, c2
, c3
, and c4
). First,
we create a new GroupLayout
object and
associate it with the panel:
GroupLayout layout = new GroupLayout(panel); panel.setLayout(layout);
We specify automatic gap insertion:
layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true);
Then, we define the groups and add the components. We establish a root group
for each dimension using the setHorizontalGroup
and setVerticalGroup
methods.
Groups are created via createSequentialGroup
and createParallelGroup
methods.
Components are added to groups by using the addComponent
method.
layout.setHorizontalGroup( layout.createSequentialGroup() .addComponent(c1) .addComponent(c2) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(c3) .addComponent(c4)) ); layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(c1) .addComponent(c2) .addComponent(c3)) .addComponent(c4) );
You can specify the alignment for parallel groups. It can be
one of the following constants defined in the GroupLayout.Alignment
enum: LEADING
, TRAILING
,
CENTER
, and BASELINE
. These constants are used for both dimensions and depend on whether the component
orientation is left-to-right or right-to-left (top-to-bottom or bottom-to-top). For example, if the horizontal (vertical) component orientation is
left-to-right (top-to-bottom) LEADING
means left (top) while TRAILING
means right (bottom).
CENTER
means "centered" in both dimensions. If you do not specify the alignment, LEADING
will be used.
The BASELINE
alignment is valid only in the vertical dimension.
GroupLayout.Alignment
constants.
Some comments about the code:
addComponent
methods used to fill the groups. The addComponent
method always returns the group on which it is called. Thanks to this you do not need to use local
variables to hold the groups.
createXXXGroup
methods. By following these simple rules, it is easier to add a new component or remove an existing one.
There is no limit on the number of resizable components in a layout.
The size of each component in a GroupLayout
is constrained by three values;
minimum size, preferred size and maximum size. These sizes control how the
component resizes within the layout. The GroupLayout.addComponent(...)
method allows the size
constraints to be specified.
If not specified explicitly, the layout asks the component for its default
sizes (by using the component's getMinimumSize()
, getPreferredSize()
and getMaximumSize()
methods). You do not need to
specify anything for most of the components, like making JTextField
resizable or JButton
fixed, because the components themselves have the desired resizing
behavior as default. On the other hand you can override the default behavior.
For example you can make a JTextField
fixed or JButton
resizable.
GroupLayout
defines constants that provide precise control over
resize behavior. They can be used as parameters in the addComponent(Component comp, int min, int pref, int max)
method. Here are two examples:
group.addComponent(component, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ...
This allows the component to resize between zero size (minimum) to any size (Short.MAX_VALUE
as maximum size means "infinite"). If we wanted the component not to shrink below its
default minimum size, we would use GroupLayout.DEFAULT_SIZE
instead of 0
in the second parameter.
group.addComponent(component, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) ...
In these examples the initial size of the component is not altered, its
default size is the component's preferred size. If we
wanted a specific size for the component, we would specify it in the second
parameter instead of using GroupLayout.DEFAULT_SIZE
.
Resizable gaps
Specifying size and resizability applies to gaps as well, including the preferred ones. For example, you can specify a preferred gap between two components that acts like a spring pushing the components away from each other (to the opposite sides of the container). The preferred distance of the two components is only used as the minimum size of the gap. See the following snippet:
layout.createSequentialGroup() .addComponent(c1) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(c2);
Resizable elements placed in a parallel group are stretched to fill the
space of the group determined by the largest element in the group, so they end up
aligned with the same size. GroupLayout
also provides control over whether
the enclosing parallel group itself should resize. If group resizing is suppressed,
it prevents the contained elements from growing over the preferred size of the group.
This way you can make a block of components align on both sides, or constrain individual
components to have the same size.
Let us try to achieve the same size for two components from our example (c3
and c4
in the horizontal
dimension):
layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) .addComponent(c3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(c4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE);
The underlying mechanism works as follows:
c4
in our example.
c3
is effectively stretched,
the size of c4
already corresponds to the size of the group.
As a result, c3
and c4
would have the same width.
The components would not resize further because the parallel group itself is not
resizable (the second parameter of the createParallelGroup
method, above, is false
).
Question for attentive readers: Why do we define both components in the
parallel group as resizable in this example? It seems enough to have just c3
resizable since c4
is not stretched anyway...
The answer is: because of platform and localization independence.
Otherwise we would have to rely on that c4
component always being bigger
than c3
. But this may change when the application runs on different
platform or is translated to another language. By having both components resizeable they adjust to each other, no matter which one is bigger at a given moment.
The previous case is special because the components are in the same parallel
group. But what if we wanted unrelated components to have the same size?
Clearly, the same size cannot always be ensured by grouping. The OK and Cancel buttons
in a row at the bottom of a dialog are a good example. For this purpose
GroupLayout
provides a linkSize
method. This method allows
the size of arbitrary components to be linked regardless of where they are placed.
The resulting size of the linked components is set according to the largest component. For example:
layout.linkSize(SwingConstants.HORIZONTAL, c3, c4);
In this example, the size is linked selectively for the horizontal dimension.
There are two important methods that you can use to make changes to your GUI at runtime, replace()
and
setHonorsVisibility()
. Using these two methods, you can exchange components or change the visibility of components
at runtime and have the GUI rearrange itself accordingly.
replace(Component existingComponent, Component newComponent)
replaces an existing component with a new one. One
of the common operations needed for dynamic layouts is the ability to replace components like this.
For example, perhaps a check box toggles between a component displaying a graph or a tree. GroupLayout
makes this scenario simple with the replace()
method. You can swap components without recreating all the groups.
Another common operation in user interfaces is to dynamically change the visibility of components.
Perhaps components are shown only as a user completes earlier portions of a form. To avoid components
shuffling around in such a scenario, space should be taken up regardless of the visibility of the components.
GroupLayout
offers two ways to configure how invisible components are treated. The
setHonorsVisibility(boolean)
method globally sets how invisible components are handled.
A value of true, the default, indicates invisible components are treated as if they are not there. On
the other hand, a value of false provides space for invisible components, treating them as though they
were visible. The setHonorsVisibility(Component,Boolean)
method can be used to configure the
behavior at the level of a specific component. To determine how visibility is handled, GroupLayout
first checks if a value has been specified for the Component, if not, it checks the setting of the global property.
GroupLayout
in the Java Standard Edition 6 consists
of three distinct bodies of work: the ability to get the baseline for a
component, the ability to get the preferred gap between components
(LayoutStyle
), and GroupLayout
.
This work was originally done as an open source project at
http://java.net/projects/swing-layout/
NetBeans 5.0 supports GroupLayout
by way of the swing-layout project. Because of the
success of this work, all three portions have been rolled into GroupLayout
in Java
Standard Edition version 6. The main difference between the GroupLayout
in Java SE 6 and swing-layout is in the package name and method
names. NetBeans 5.5 provides the ability to target either the
GroupLayout
in Java SE 6, or the GroupLayout
in swing-layout.
Which version NetBeans targets is determined by the version of the Java
platform your project targets. A project targeting Java SE 6 uses the
GroupLayout
in Java SE, otherwise GroupLayout
in swing-layout is used.