The JavaTM Tutorial
Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Trail: Creating a GUI with JFC/Swing
Lesson: Laying Out Components Within a Container

How to Use SpringLayout

The SpringLayout (in the API reference documentation) class was added in v 1.4 to support layout in GUI builders. SpringLayout is a very flexible layout manager that can emulate many of the features of other layout managers. This section starts with a simple example showing all the things you need to remember to create your first spring layout — and what happens when you forget them! Later it creates some reusable code to set up spring layout so that it lays out components in a couple of different types of grids.

Here are pictures of some of the layouts we will cover:

The SpringCompactGrid application presents components in a grid without forcing all components to be the same size..

The SpringForm application has 5 rows of label-textfield pairs.

The SpringBox application uses a SpringLayout to produce something similar to what a BoxLayout would produce.

How Spring Layouts Work

Spring layouts do their job by defining relationships between the edges of components. For example, you might define that the left edge of one component is a fixed distance (5 pixels, say) from the right edge of another component. By default, a spring layout defines the width and height of a component (the distance between its left and right edges and between its top and bottom edges) to be somewhere between the component's minimum and maximum sizes — if possible, at its preferred size.

Distances between edges are represented by Spring objects. Each spring has four properties — its minimum, preferred, and maximum values, and its actual (current) value. The springs associated with each component are collected into a SpringLayout.Constraints object. The SpringLayout.Constraints object is like a java.awt.Rectangle, except that its values are Spring objects rather than integers.

Example: SpringDemo

This section takes you through the typical steps of specifying the constraints for a container that uses SpringLayout. The first example, SpringDemo1.java (in a .java source file), is an extremely simple application that features a label and a text field in a content pane controlled by a spring layout. Here's the relevant code:

public class SpringDemo1 {
    public static void main(String[] args) {
        ...
        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new SpringLayout());
        contentPane.add(new JLabel("Label: "));
        contentPane.add(new JTextField("Text field", 15));
        ...
        frame.pack();
        frame.setVisible(true);
    }
}

Here's what the GUI looks like when it first comes up:

SpringDemo1 -- the parent has no initial size!

Here's what it looks like when it's resized to be bigger:

SpringDemo1 -- all the components are at (0, 0)!

Obviously, we have some problems. Not only does the frame come up way too small, but even when it's resized the components all are located at (0,0). This happens because we have set no springs specifying the components' positions and the width of the container. One small consolation is that at least the components are at their preferred sizes — we get that for free from the default springs created by SpringLayout for each component.

Our next example, SpringDemo2.java (in a .java source file), improves the situation a bit by specifying x and y locations for each component. In this example, we'll specify that the components should appear in a single row, with 5 pixels between them. The following code specifies the locations:

//Adjust constraints for the label so it's at (5,5).
SpringLayout.Constraints labelCons =
        layout.getConstraints(label);
labelCons.setX(Spring.constant(5));
labelCons.setY(Spring.constant(5));

//Adjust constraints for the text field so it's at
//(<label's right edge> + 5, 5).
SpringLayout.Constraints textFieldCons =
        layout.getConstraints(textField);
textFieldCons.setX(Spring.sum(
        Spring.constant(5),
        labelCons.getConstraint(SpringLayout.EAST)));
textFieldCons.setY(Spring.constant(5));

The setX and setY calls set the springs that determine the x and y coordinates of each component in the container. The y coordinate is always 5. The x coordinate is set so that the components are always 5 pixels apart, horizontally. We use the Spring.sum method to create a Spring that depends on the values of other springs.

The previous example still has the problem of the container coming up too small. But when we resize the window, the components are in the right place:

SpringDemo2 -- at least now all the components are in the right position!

To make the container initially appear at the right size, we need to set the springs that define the right (SpringLayout.EAST) and bottom (SpringLayout.SOUTH) edges of the container itself. SpringDemo3 shows how to do this. You can run SpringDemo3 using Java Web Start (in the Creating a GUI with JFC/Swing trail) and find its code in SpringDemo3.java (in a .java source file). Here is the code that sets the container's springs:

//Adjust constraints for the content pane.
setContainerSize(contentPane, 5);
...
public static void setContainerSize(Container parent,
                                    int pad) {
    SpringLayout layout = (SpringLayout) parent.getLayout();
    Component[] components = parent.getComponents();
    Spring maxHeightSpring = Spring.constant(0);
    SpringLayout.Constraints pCons = layout.getConstraints(parent);

    //Set the container's max X to the max X
    //of its rightmost component + padding.
    Component rightmost = components[components.length - 1];
    SpringLayout.Constraints rCons =
            layout.getConstraints(rightmost);
    pCons.setConstraint(
            SpringLayout.EAST,
            Spring.sum(Spring.constant(pad),
                       rCons.getConstraint(SpringLayout.EAST)));

    //Set the container's max Y to the max Y of its tallest
    //component + padding.
    for (int i = 0; i < components.length; i++) {
        SpringLayout.Constraints cons =
            layout.getConstraints(components[i]);
        maxHeightSpring = Spring.max(maxHeightSpring,
                                     cons.getConstraint(
                                            SpringLayout.SOUTH));
    }
    pCons.setConstraint(
            SpringLayout.SOUTH,
            Spring.sum(Spring.constant(pad),
                       maxHeightSpring));
}

Finally, the window comes up at the right size:

SpringDemo3 -- the parent now HAS a correct initial size!

When we make the window larger we can see the spring layout in action, distributing the extra space between the available components.

SpringDemo3 enlarged

In this case the spring layout has chosen to give all the extra space to the text field. Although it seems like the spring layout treats labels and text fields differently, spring layout has no special knowledge of any Swing or AWT components. It relies on the values of a components minimum, preferred, and maximum size properties. The next section discusses how spring layout uses these properties, and why they can cause uneven space distribution.

Springs and Component Size

A SpringLayout object automatically installs Springs for the height and width of each component that the SpringLayout controls. These springs are essentially covers for the component’s getMinimumSize, getPreferredSize, and getMaximumSize methods. By "covers" we mean that not only are the springs initialized with the appropriate values from these methods, but also that the springs track those values. For example, the Spring object that represents the width of a component is a special kind of spring that simply delegates its implementation to the relevant size methods of the component. That way the spring stays in sync with the size methods as the characteristics of the component change.

When a component's getMaximumSize and getPreferredSize methods return the same value, SpringLayout interprets this as meaning that the component should not be stretched. JLabel and JButton are examples of components implemented this way. For this reason, the label in the SpringDemo3 example doesn't stretch.

The getMaximumSize method of some components, such as JTextField, returns the value Integer.MAX_VALUE for the width and height of its maximum size, indicating that the component can grow to any size. For this reason, when the SpringDemo3 window is enlarged, SpringLayout distributes all the extra space to the only springs that can grow — those determining the size of the text field.

Utility Methods for Grids

Because the SpringLayout class was created for GUI builders, setting up individual springs for a layout can be cumbersome to code by hand. This section presents a couple of methods you can use to install all the springs needed to lay out a group of components in a grid. These methods emulate some of the features of the GridLayout, GradBagLayout, and BoxLayout classes.

The two methods, called makeGrid and makeCompactGrid, are defined in SpringUtilities.java (in a .java source file). Both methods work by grouping the components together into rows and columns and using the Spring.max method to make a width or height spring that makes a row or column big enough for all the components in it. In the makeCompactGrid method the same width or height spring is used for all components in a particular column or row, respectively. In the makeGrid method, by contrast, the width and height springs are shared by all components, forcing them all to be the same size.

Let’s see these methods in action. Our first example, implemented in the source file SpringGrid.java (in a .java source file), displays a bunch of numbers in text fields. The center text field is much wider than the others. Just as with GridLayout, having one large cell forces all the cells to be equally large. You can run SpringGrid using Java Web Start (in the Creating a GUI with JFC/Swing trail).

SpringGrid

Here is the code that creates and lays out the text fields in SpringGrid:

JPanel panel = new JPanel(new SpringLayout());
...
for (int i = 0; i < 9; i++) {
    JTextField textField = new JTextField(Integer.toString(i));
    ...//when i==4, put long text in the text field...
    panel.add(textField);
}
...
SpringUtilities.makeGrid(panel,
                         3, 3, //rows, cols
                         5, 5, //initialX, initialY
                         5, 5);//xPad, yPad

Now let's look at an example, in the source file SpringCompactGrid.java (in a .java source file), that uses the makeCompactGrid method instead of makeGrid. This example displays lots of numbers to show off spring layout's ability to minimize the space required. You can run SpringCompactGrid using Java Web Start (in the Creating a GUI with JFC/Swing trail). Here's what the SpringCompactGrid GUI looks like:

SpringCompactGrid

Here is the code that creates and lays out the text fields in SpringCompactGrid:

JPanel panel = new JPanel(new SpringLayout());

int rows = 10;
int cols = 10;
for (int r = 0; r < rows; r++) {
    for (int c = 0; c < cols; c++) {
        int anInt = (int) Math.pow(r, c);
        JTextField textField =
                new JTextField(Integer.toString(anInt));
        panel.add(textField);
    }
}

//Lay out the panel.
SpringUtilities.makeCompactGrid(panel, //parent
                                rows, cols,
                                3, 3,  //initX, initY
                                3, 3); //xPad, yPad

The makeCompactGrid method is surprisingly applicable to scenarios where we might not think to use it. In SpringForm.java (in a .java source file) we can see that we can lay out a simple form where one of the rows needs to be higher than the others. Not only is each of the elements in the second column the right distance from the widest component in column one, their row heights are all lined up too. You can run SpringForm using Java Web Start (in the Creating a GUI with JFC/Swing trail).

SpringForm

Here is the code that creates and lays out the label-text field pairs in SpringForm:

String[] labels = {"Name: ", "Fax: ", "Email: ", "Address: "};
int numPairs = labels.length;

//Create and populate the panel.
JPanel p = new JPanel(new SpringLayout());
for (int i = 0; i < numPairs; i++) {
    JLabel l = new JLabel(labels[i]);
    ...//when i==1, make the text field's font bigger...
    p.add(l);
    JTextField textField = new JTextField(10);
    l.setLabelFor(textField);
    p.add(textField);
}

//Lay out the panel.
SpringUtilities.makeCompactGrid(p,
                                numPairs, 2, //rows, cols
                                6, 6,        //initX, initY
                                6, 6);       //xPad, yPad

Because we are using a real layout manager instead of absolute positioning, the layout manager responds dynamically to changes in components involved. For example, if the names of the labels are localized, the spring layout produces a configuration that gives the first column more or less room, as needed. And as the following figure shows, when the window is resized, the flexibly sized components — the text fields — take all the excess space, while the labels stick to what they need.

SpringForm enlarged

Our last example of the makeCompactGrid method, in SpringBox.java (in a .java source file), shows some buttons configured to be laid out in a single row. You can run SpringBox using Java Web Start (in the Creating a GUI with JFC/Swing trail).

SpringBox

Note that the behavior is almost identical to that of BoxLayout in the case of a single row. Not only are the components laid out as BoxLayout would arrange them but the minimum, preferred, and maximum sizes of the container that uses the SpringLayout return the same results that BoxLayout would. Here is the call to makeCompactGrid that produces this layout:

//Lay out the buttons in one row and as many columns
//as necessary, with 6 pixels of padding all around.
SpringUtilities.makeCompactGrid(contentPane, 1,
                                contentPane.getComponentCount(),
                                6, 6, 6, 6);

Let's look at what happens when we resize this window. This is an odd special case that's worth taking note of as you may run into it by accident in your first layouts.

SpringBox resized

Nothing moved! That’s because none of the components (buttons) or the spacing between them was defined to be stretchable. In this case the spring layout calculates a maximum size for the parent container that is equal to its preferred size, meaning the parent container itself is not stretchable. It would perhaps be less confusing if the AWT refused to resize a window that was not stretchable, but it doesn’t. Maximum and minimum sizes for windows are "recommendations" that the AWT defies when given suitable user input. The layout manager cannot do anything sensible here as none of the components will take up the required space. Instead of crashing, it just does nothing, leaving all the components as they were.

The SpringLayout API

The SpringLayout class has a single constructor, which takes no argument:
public SpringLayout()
[PENDING: List the important API for SpringLayout (in the API reference documentation), SpringLayout.Constraints (in the API reference documentation), and Spring (in the API reference documentation).]

Examples that Use SpringLayout

The following table lists some examples that use spring layout.

Example Where Described Notes
SpringDemo3 This page Uses SpringLayout to create a row of evenly spaced, natural-size components.
SpringGrid This page Uses SpringLayout and the makeGrid utility method to create a layout where all the components are the same size.
SpringCompactGrid This page Uses SpringLayout and the makeCompactGrid utility method to create a layout where all the components in a row have the same height, and all components in a column have the same width.
SpringForm This page Uses SpringLayout and makeCompactGrid to align label-text field pairs.
SpringBox This page Uses SpringLayout and makeCompactGrid to demonstrate laying out a single row of components, and what happens when no springs can grow.


Previous Page Lesson Contents Next Page Start of Tutorial > Start of Trail > Start of Lesson Search
Feedback Form

Copyright 1995-2003 Sun Microsystems, Inc. All rights reserved.