The task of a layout manager is to position and size each component based on the size of its container. Each component has a preferred size that can be used to determine the real estate it wishes to occupy, as well as a minimum and maximum size. The preferred size is especially useful for components that contain user-visible strings whose size can change between development and deployment due to different fonts or different locale values, as the GUI is dynamically translated through resource bundles.
Layout managers are powerful classes for dynamically recalculating the size and position of the GUI components at runtime; however, they often lead to a less than optimal design-time experience. With WYSIWYG development tools, it frequently happens that developers position the mouse where they wish to drop a new component, only to see the entire GUI reorganize and the component placed in another location. This is due to the indirection involved in translating the developer's gesture to the correct constraint for the layout manager.
JDK 1.4 introduces a new layout manager - javax.swing.SpringLayout. The motivation behind building it was to create a layout manager that made it easier to design GUIs, was powerful enough to emulate the behavior of all the existing JRE layout managers, and be easily extensible where a new rule was required.
The most pleasant user experience with a design tool occurs when you set the layout manager to null. Then you can place your component exactly where you want it using the mouse and set its size using resize handles. The problem with not having a layout manager is that the explicit bounds of the component are fixed constants set at design time. With SpringLayout you can specify each component's x, y, width, and height, but instead of using fixed coordinates, the constraints are springs. These springs can be derived from the edges of existing components, and are able to flex under tension and compression when the window size is changed.
To illustrate SpringLayout this article demonstrates how to use springs to anchor edges of components and align widths, and how to create complex springs.
To specify how each component should be positioned and sized by the layout manager you must use a constraint object, which is an instance of the inner class javax.swing.SpringLayout.Constraints. A SpringLayout.Constraints object contains six springs - for the component's width and height and each of its four edges.
A spring is analogous to a physical spring that connects the edge of the component to another point on the GUI. Each edge is controlled by a single spring, so springs are best thought of as directional. For example, a spring that fixes the left edge of a component to the right edge of another doesn't operate in reverse. When the first component's right edge moves, this affects the second component's left edge, but not vice versa.
Springs can be fixed struts that have a constant value, or else can be flexed under tension and compression. To determine how elastic a spring is, it has a preferred, minimum, and maximum value. If the minimum value is the same as the preferred value, the spring is rigid under compression, and, likewise, if the maximum value is the same as its preferred value the spring can't be expanded. If no spring is specified for a component's width or height, the default spring created defers its preferred minimum and maximum values to the component. In addition to using individual springs to anchor and size components, springs can be combined to create compound springs whose elasticity is the sum, maximum or minimum, of its arguments.
When designing a GUI using SpringLayout, draw a diagram of the components in the positions you want them to be in, and then mentally determine what will keep them in those positions. Usually this will be a fixed length spring between the component's left and top edge relative to its nearest neighbor's edges. This is very useful for components that contain user-visible strings, such as buttons and labels where the exact size isn't known until runtime, because the font used or the actual string, if it has been externalized in a locale-specific bundle, can vary.
To show how to use springs, this article builds a simple GUI that illustrates some of the common types. The screen images have red lines drawn on them to help you visualize each spring. This approach, drawing springs between components, is an intuitive way to design a GUI where you begin by drawing the components at their absolute positions and sizes and then add lines to represent the springs. A GUI builder could employ this method where you drop a control at the position you want it placed, and springs are automatically created between it and its nearest neighbors.
The simplest spring is a fixed value that positions an edge a constant distance from its parent container. Figure 1 shows a button that has two constant springs used to position it at 5,10 inside a frame's contentPane. The SpringLayout class automatically generates a Constraints object for every component that's added to its container, so in order to change any of the springs you can query the Constraints object and set new values. To create a constant fixed spring, use the static method Spring.constant(int).
JButton okButton = new JButton("OK");
SpringLayout.Constraints okCst =
Springs Between Edges
In Figure 1 the springs for the OK button were fixed lengths of 5 and 10. Even though no springs were specified for the width and height of the button, some were created automatically based on the preferred, minimum, and maximum size. These springs can be queried and used in the constraints of other components so you can position them relative to each other. For example, a Cancel button can be added to the content pane, whose x spring is the right-edge spring of the OK button. To query the spring for an edge, the method public Spring getConstraint(String edgeName) can be used that takes arguments of "North", "East", "South", and "West" for each edge. To position the Cancel button so its left edge is a fixed distance away from the right edge of the OK button, a compound spring is used. The method Spring.sum(Spring spring1,Spring spring) creates a compound spring whose value is the sum of two arguments. By combining the east edge of the OK button with a constant spring of 5, a spring is created that will always be five larger than the right edge of the OK button. This spring is then used as the x spring of the Cancel button.
JButton cButton = new JButton("Cancel");
SpringLayout.Constraints cCst =
The result of this is shown in Figure 2 where the Cancel button is positioned to the right of the OK button with a space of 5 pixels in between.
Sizing the Window with Springs
In addition to allowing you to specify constraints for a container's components, you can specify a constraint for the container that allows you to control its edges. The content pane's right edge can be set with a compound spring as 20 away from the right edge of the cancel button. To do this, retrieve the spring for the layout manager's container, and then set its east spring.
SpringLayout.Constraints pnlCst =
For the content pane's bottom edge a spring can be put between it and the bottom of the buttons. This spring can make the bottom of the contentPane be a distance of 15 away from the buttons, and because the OK and Cancel buttons are the same height and have the same y spring, the content pane's bottom edge can spring from either one. A compound sum spring is created that adds the south spring of the cancel button's constraint to a fixed spring of 15.
When there are springs controlling the edges of the contentPane, the window can be packed to give it an initial size (see Figure 3). In the previous examples where there were no springs for the edges of the contentPane, the window was given a fixed size of 200 by 200.
The content pane's edges can have a spring between them and a specific control, such as the Cancel button. However, if there is no single control that determines the edges of the content pane, a compound spring can be created. This compound spring needs to determine which is greater, the south spring of the OK button or the Cancel button. The method Spring.max(Spring spring1, Spring spring2) is used to create a single spring whose value is the greater of the two arguments.
Springs for Widths and Heights
You can also use springs to set a component's size, as well as its position. For example, instead of the OK and Cancel button each being their preferred size, you want them to be the same size. The size used will need to be the larger of each button's preferred size so that neither label is clipped and a compound spring can be created based on the maximum of the two widths.
Spring widthSpring = Spring.max(
Because the code changes the spring that specifies the width of the OK button, and this spring is also used to calculate the distance between the OK and Cancel buttons, the constraint between the two buttons must be defined after setting their width springs. This ensures that the x spring of the cancel constraint is not based on its original width spring, but is derived from the max spring instead. The order of setting springs that form input to compound springs is important, and the following code must be done after the OK button's width spring is set.
Likewise the spring that's created between the content pane's right edge and the right edge of the Cancel button must be defined after the width spring is established. Generally, it's a good idea to specify any explicit width or height springs before defining the positional springs between components to ensure that no queried width or height springs are later replaced. Because the word "Cancel" is longer than "OK", the OK button's width is increased to match that of the Cancel button. However, if in a different language the lengths were reversed, the two buttons would always remain the same width. Figure 4 shows that the OK and Cancel buttons are the same size, and that the overall window size still remains 20 and 15 away from the Cancel button.
The ability to compute a component's width and height using a compound spring gives you a level of control not previously possible with other layout managers. In GridBagLayout you can align widths of components by placing them in the same column, but with SpringLayout you can align widths where the controls are side by side as shown in Figure 4. Sometimes the requirement for laying out buttons may be a rule such as "Make the buttons have the same width but make this be no smaller than 60." This can be created by having a compound spring such as:
Spring widthSpring = Spring.max(
Components Resizing with the Container
For some components, such as JTextArea, the desired layout is to make as much use of the available space as possible. To do this springs can be made between the east and south edge of the text area and its container. Listing 1 creates a JTextArea and adds constant springs between its east edge and the container, and its south edge and the container. The lines of code setting the springs between the Cancel button can be deleted as the contentPane's constraints are attached to the text area; although, if left there the panel will use the newest springs since only one spring can be held for each constraint in the SpringLayout.Constraint class, and setting a new one replaces any existing spring.
The panel opens with a default size based on the preferred size of the JTextArea (see Figure 5).
When the panel is resized, the JTextArea will be resized with it, expanding to make use of extra width or height. This occurs with the JTextArea and not with the OK and Cancel buttons due to the default width and height springs that were initially created for each component by the layout manager. A JButton's preferred, minimum, and maximum sizes are all the same value, so there is no "give" in an arrangement of buttons connected together with "struts." By contrast, a JTextArea's minimum and maximum and size are not the same, so the default width and height springs can expand and compress. After the panel changes size, the layout manager looks at the flexibility of all its springs and, based on their elasticity, calculates their values.
The JTextArea becomes larger when the window size is expanded, and smaller when the size is reduced. The fixed-edge springs remain at their value of 10, while the JTextArea shrinks to occupy the remaining space.
With a reduced window size the springs between the edges of the JTextArea and its neighboring edges remain 10 pixels wide, and the JTextArea reduces in size because its minimum size that provides the input to its width and height springs is 0 (see Figure 6). However, rather than have the springs between the JTextArea and its neighbors rigid, they can be made flexible so they'll compress when the window size is reduced. This way, when the window is made smaller, more of the actual space can be given to the JTextArea.
In addition to defining a single value for a constant spring, you can also set a minimum and maximum size, e.g., Spring.constant(0,10,20). If the minimum and maximum values are the same as the preferred value, which occurs when the single argument constructor is used, the spring becomes an inflexible strut. However, with a minimum and maximum size, when the window size is changed, the layout manager takes these values into account to determine the new actual values of each spring. To illustrate this all the springs used in the example between the JTextArea and its neighbors were set to be constant with a minimum of 0, a maximum of 20, and a preferred size of 10.
What occurs now is that when the window is reduced in size, the springs can be compressed. The layout manager looks at the compressibility of each spring, including the implicit spring that the JTextArea has for its width and height, to determine the size and position of the controls. When the window is made smaller, the springs compress and the JTextArea occupies more of the available real estate. When the window size was reduced to a height of 77, the JTextArea, with rigid springs around it, was made 26 high, enough to show two lines of text. However, the flexible springs mean that they can take the strain and allow the JTextArea to have a larger size (see Figure 7). The top and bottom Springs are compressed to a value of 4, and the JTextArea has 12 extra pixels available for its height, making it 28 high so it can display an extra row of text. If flexible springs were also used for the top of the two buttons, the window could be further optimized so that yet more of the more available space was allocated to the JTextArea under compression.
The rules by which the layout manager determines how much to compress each of the springs are based on how much it resists being compressed. When a spring is compressed, imagine that it provides an opposite force that increases as it reaches its minimum value. If the minimum and preferred values are the same, the force is an irresistible one and the spring is rigid.
The springs between the edges have a minimum value of 0 and a preferred of 10, and the JTextArea's height spring is based on its minimumSize's height of 0 and preferredSize's height of 5 rows of characters at the current font. Both of these springs are compressed as the window size decreases. When the window is increased, the edge springs have a maximum size of 20 and a preferred size of 10, however, the maximumSize of the JTextArea is based on the look and feel but is likely to be Integer.MAX_VALUE. Because the resistance of a spring increases as its value approaches its maximum value, for the JTextArea's width or height to become rigid the window would have to become enormous. Use of compressible springs between components allows you to create a GUI that reallocates its available spaces across its components as it changes size, so the user has more available working component area and the window remains effective over a longer range of overall sizes.
Convenience Methods for Attaching Springs Between Edges
To attach the Cancel button to the OK button and make them 5 apart:
This scenario, where you attach the edge of one component a fixed distance from another, occurs frequently when creating GUIs with SpringLayout, so there's a helper method
Component targetComponent, int distance,
String sourceEdge, Component sourceComponent);
cancelButton , 5, "East", okButton);
Creating Springs in the Correct Order
Because of the cyclic nature of springs, i.e., they are often calculated based on existing springs that are combined to create new compound springs, the order in which the constraints are set is important. For example, if you query the west or width spring of a component, should you then set its east spring, afterward this could affect either of the previously queried springs, making their values stale. Therefore, it's important to establish and query strings in the correct order. The analogy is that of a spreadsheet whose cells' values are based on calculations that include other cells' values as input.
An application window is made up of a set of components, and SpringLayout lets you add a set of springs into this picture. The springs define the positions and sizes of components, and can be anchored to other components' edges, or can be derived from calculations using existing springs.
We hope that you'll have some fun with SpringLayout and that it helps solve some of those difficult layout problems where things don't work out quite the way you want. In our minds at least, the holy grail of layout experiences would be a GUI builder that would allow the user to add constraints between components visually - rather than having to write code. This is quite a difficult problem; if you're working on a GUI builder or have ideas about the best way to do this - drop us a line!
Resources Online tutorial of SpringLayout:
JDK 1.4 SDK download:
Joe Winchester is a software developer working on WebSphere development tools for IBM in Hursley, UK.
Philip Milne is a software developer who worked at Sun as part of the Swing development team, and now works as a consultant in London.
JTextArea txtArea = new JTextArea(5,15);
SpringLayout.Constraints txtCst =