The JFC Swing Tutorial: A Guide to Constructing GUIs (2nd Edition)

 <  Day Day Up  >  

The SpringLayout [21] class was added in v1.4 to support layout in GUI builders. SpringLayout is very flexible and can emulate many of the features of other layout managers.

[21] SpringLayout API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpringLayout.html.

This section starts with a simple example showing all of the things you need to create your first spring layout ”and what happens when you forget them! Later it presents utility methods that let you lay out components in a couple of different grid types. Figures 24 through 26 show the SpringBox , SpringForm , and SpringCompactGrid examples, which illustrate three possible uses of SpringLayout .

Figure 24. The SpringBox application uses SpringLayout to produce a layout similar to what BoxLayout or FlowLayout would produce.

Figure 26. SpringCompactGrid presents components in a grid without forcing all of them to be the same size .

Figure 25. The SpringForm application has five rows of label “text field pairs.

How Spring Layouts Work

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

Distances between edges are represented by Spring [22] 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 in a SpringLayout.Constraints [23] object, which is like a java.awt.Rectangle except that its values are Spring objects rather than integers.

[22] SpringLayout API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/Spring.html.

[23] SpringLayout.Constraints API documentation: http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpringLayout.Constraints.html.

Example: SpringDemo

Now we'll take you through the typical steps of specifying the constraints for a Spring-Layout container. The first example, SpringDemo1.java , [24] is an extremely simple application that features a label and a text field in a content pane controlled by SpringLayout . Here's the relevant code:

[24] You can find the source files for SpringDemo1 here: JavaTutorial/uiswing/layout/example-1dot4/index.html#SpringDemo1 .

public class SpringDemo1 { ... //Where the GUI is created: Container contentPane = frame.getContentPane() SpringLayout layout = new SpringLayout(); contentPane.setLayout(layout); contentPane.add(new JLabel("Label: ")); contentPane.add(new JTextField("Text field", 15)); ... frame.pack(); frame.setVisible(true); }

Figure 27 shows what the GUI looks like when it first comes up.

Figure 27. SpringDemo1 ”the GUI starts out so small because the parent has no initial size.

Figure 28 shows what it looks like when it's resized to be bigger.

Figure 28. SpringDemo1 after it has been resized ”the components overlap because all are at (0,0).

Obviously, we have some problems. Not only does the frame come up too small, but even when it's resized the components are all located at (0,0). This happens because we didn't set any springs specifying the components' positions and the width of the container. One small consolation is that 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 , [25] improves the situation a bit by specifying locations for each component so that the components appear in a single row with 5 pixels between them. The following code specifies the location of the label:

[25] You can find the source files for SpringDemo2 here: JavaTutorial/uiswing/layout/example-1dot4/index.html#SpringDemo2 .

//Adjust constraints for the label so it's at (5,5). layout.putConstraint(SpringLayout.WEST, label, 5, SpringLayout.WEST, contentPane); layout.putConstraint(SpringLayout.NORTH, label, 5, SpringLayout.NORTH, contentPane);

The first putConstraint call specifies that the label's west (left) edge should be 5 pixels from its container's west edge. This translates to an x coordinate of 5. The second putConstraint call sets up a similar relationship between the north (top) edges of the label and its container, resulting in a y coordinate of 5.

Here's the code that sets up the location of the text field:

//Adjust constraints for the text field so it's at //(<label's right edge> + 5, 5). layout.putConstraint(SpringLayout.WEST, textField, 5, SpringLayout.EAST, label); layout.putConstraint(SpringLayout.NORTH, textField, 5, SpringLayout.NORTH, contentPane);

The first putConstraint call puts the text field's west (left) edge 5 pixels away from the label's east (right) edge. The second putConstraint call is just like the code that set the label's y coordinate, and it has the same effect for the text field.

We still have the problem of the container coming up too small. But when we resize the window, the components are in the right place, as seen in Figure 29.

Figure 29. SpringDemo2 , an improved application with all of the components in their correct positions.

To make the container initially appear at the right size, we need to set the springs that define the east (right) and south (bottom) edges of the container itself. SpringDemo3 shows how to do this. You can run SpringDemo3 using Java Web Start or compile and run the example yourself. [26]

[26] To run SpringDemo3 using Java Web Start, click the SpringDemo3 link on the RunExamples/layout.html page on the CD. You can find the source files here: JavaTutorial/uiswing/layout/example-1dot4/index.html#SpringDemo3 .

Here's the code that sets the container's springs:

layout.putConstraint(SpringLayout.EAST, contentPane, 5, SpringLayout.EAST, textField); layout.putConstraint(SpringLayout.SOUTH, contentPane, 5, SpringLayout.SOUTH, textField);

The first putConstraint call puts the container's right edge 5 pixels to the right of the text field's right edge. The second one puts its bottom edge 5 pixels beyond the bottom edge of the tallest component (which, for simplicity's sake, we've assumed is the text field).

Finally, the window comes up at the right size (see Figure 30).

Figure 30. SpringDemo3 with the window (or "container") now at the 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 (Figure 31). In this case, the spring layout has given all of the extra space to the text field.

Figure 31. A screenshot of SpringDemo3 after it has been enlarged.

Although it seems like it treats labels and text fields differently, SpringLayout has no special knowledge of any Swing or AWT components. Instead, it relies on the values of a component's minimum, preferred, and maximum size properties. The next section discusses how SpringLayout 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 it controls. These springs are essentially covers for the component's getMinimumSize , getPreferredSize , and getMaximumSize methods. By "covers" we mean not only that the springs are initialized with the appropriate values from these methods but also that they 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 component's relevant size methods. That way it 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 that the component shouldn't be stretched . JLabel and JButton are examples of components implemented this way, which is why the label in SpringDemo3 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 of the extra space to the only springs that can grow ”those determining the size of the text field.

Alternative Expressions

The SpringDemo examples used the SpringLayout method putConstraint to set the springs associated with each component. putConstraint is a convenience method that lets you modify springs without using the full SpringLayout API. Here, again, is the code from SpringDemo2 that sets the location of the label:

layout.putConstraint(SpringLayout.WEST, label, 5, SpringLayout.WEST, contentPane); layout.putConstraint(SpringLayout.NORTH, label, 5, SpringLayout.NORTH, contentPane);

Here's equivalent code that uses the SpringLayout.Constraints and Spring classes directly:

SpringLayout.Constraints labelCons = layout.getConstraints(label); labelCons.setX(Spring.constant(5)); labelCons.setY(Spring.constant(5));

To see the entire SpringDemo3 demo converted to use this API, look at SpringDemo4.java . [27] That file also includes a general-purpose method called setContainerSize , which you can use to make sure a container controlled by SpringLayout has enough space for all of its components.

[27] You can find the SpringDemo4 source file and a link that lets you run it using Java Web Start here: JavaTutorial/uiswing/layout/example-1dot4/index.html#SpringDemo4 .

As the preceding code snippets imply, SpringLayout and SpringLayout.Constraints tend to use different conventions for describing springs. The SpringLayout API uses edges, which are specified by these constants:

  • SpringLayout.NORTH (top edge)

  • SpringLayout.SOUTH (bottom edge)

  • SpringLayout.EAST (right edge)

  • SpringLayout.WEST (left edge)

The SpringLayout.Constraints class knows about edges, but only has Spring objects for the following properties:

  • x

  • y

  • width

  • height

Each Constraints object maintains the following relationships between its springs and the edges they represent:

west = x north = y east = x + width south = y + height

If you're confused , don't worry. The next section presents utility methods you can use to accomplish some common layout tasks without knowing anything about the SpringLayout API.

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 of 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 ” makeGrid and makeCompactGrid ”are defined in SpringUtilities. java . [28] Both work by grouping components into rows and columns and by using the Spring.max method to make a width or height spring that enlarges a row or column enough to fit all 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. By contrast, in the makeGrid method, the width and height springs are shared by all components, forcing them to be all the same size.

[28] You can find SpringUtilities.java here: JavaTutorial/uiswing/layout/example-1dot4/SpringUtilities.java .

You can run SpringGrid using Java Web Start or compile and run the example yourself. [29] This first example displays several numbers in text fields. The center text field is much wider than the others. Just as with GridLayout , having one large cell forces all cells to be equally large (see Figure 32).

[29] To run SpringGrid using Java Web Start, click the SpringGrid link on the RunExamples/layout.html page on the CD. You can find the source files here: JavaTutorial/uiswing/layout/example-1dot4/index.html#SpringGrid .

Figure 32. The SpringGrid GUI.

Here's 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 , that uses the makeCompactGrid method instead of makeGrid (see Figure 33). This example displays lots of numbers to show off SpringLayout 's ability to minimize the space required.

Figure 33. The SpringCompactGrid GUI.

You can run SpringCompactGrid using Java Web Start or compile and run the example yourself. [30] Here's the code that creates and lays out the text fields in SpringCompactGrid :

[30] To run SpringCompactGrid using Java Web Start, click the SpringCompactGrid link on the Run-Examples/layout.html page on the CD. You can find the source files here: JavaTutorial/uiswing/layout/example-1dot4/index.html#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

One of the handiest uses for makeCompactGrid is associating labels with components, where the labels are in one column and the components are in another (see Figure 34).

Figure 34. SpringForm uses the makeCompactGrid method to associate labels with components.

You can run SpringForm using Java Web Start or compile and run the example yourself. [31] Here's the code that creates and lays out the label “text field pairs in SpringForm :

[31] To run SpringForm using Java Web Start, click the SpringForm link on the RunExamples/layout.html page on the CD. You can find the source files here: JavaTutorial/uiswing/layout/example-1dot4/index.html#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], JLabel.TRAILING); 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're using a real layout manager instead of absolute positioning, the layout manager responds dynamically to changes in the components involved. For example, if the names of the labels are localized, the SpringLayout produces a configuration that gives the first column more or less room as needed. And, as Figure 35 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.

Figure 35. SpringForm after it has been enlarged.

Our last example of the makeCompactGrid method, in SpringBox.java , shows some buttons configured to be laid out in a single row (see Figure 36).

Figure 36. The SpringBox application.

You can run SpringBox using Java Web Start or compile and run the example yourself. [32]

[32] To run SpringBox using Java Web Start, click the SpringBox link on the RunExamples/layout.html page on the CD. You fcan find the source files here: JavaTutorial/uiswing/layout/example-1dot4/index.html#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 SpringLayout return the same results that BoxLayout would. The following 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 (see Figure 37). This is an odd special case that's worth noting, as you may run into it by accident in your first layouts.

Figure 37. SpringBox after it has been enlarged.

Nothing moved! That's because none of the components (buttons) or the spacing between them was defined to be stretchable. In this case, the SpringLayout calculates a maximum size for the parent container that's equal to its preferred size, meaning that the parent container itself isn't stretchable. It might be less confusing if the AWT refused to resize a window that wasn't 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 can't do anything sensible here, because none of the components take up the required space. Instead of crashing, it just does nothing, leaving all of the components as they were.

The SpringLayout API

The API for SpringLayout is larger than that for most layout managers. It's spread across three classes. Table 11 lists the constructors and methods for the SpringLayout class; Table 12, for the SpringLayout.Constraints class; and Table 13, for the Spring class. Also consult the API documentation:

http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpringLayout.html

http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/SpringLayout.Constraints.html

http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/Spring.html

Table 11. SpringLayout Constructors and Methods

Constructor or Method

Purpose

SpringLayout()

Create a SpringLayout instance.

SpringLayout.Constraints getConstraints(Component)

Get the constraints (set of springs) associated with the specified component.

Spring getConstraint(String, Component)

Get the spring for an edge of a component. The first argument specifies the edge and must be one of the SpringLayout constants: NORTH , SOUTH , EAST , or WEST .

void putConstraint(String, Component, int, String, Component) void putConstraint(String, Component, Spring, String, Component)

Defines relationships between the edges of two components. The first two arguments specify the first component and its affected edge; the last two, the second component and its affected edge. The third argument specifies the spring that determines the distance between the two. When the third argument is an integer, a constant spring is created to provide a fixed distance between the component edges.

Table 12. SpringLayout.Constraints Constructors and Methods

Constructor or Method

Purpose

SpringLayout.Constraints() SpringLayout.Constraints(Spring, Spring) SpringLayout.Constraints(Spring, Spring, Spring, Spring)

Create a SpringLayout.Constraints instance. The first two arguments, if present, specify the X and Y springs, respectively; the second two, height and width springs, respectively. Omitting an argument causes the corresponding spring to be null, which SpringLayout generally replaces with suitable defaults.

Spring getConstraint(String) Spring getHeight() Spring getWidth() Spring getX() Spring getY() void setConstraint(String, Spring) void setHeight(Spring) void setWidth(Spring) void setX(Spring) void setY(Spring)

Get or set the specified spring. The string argument to the getConstraint and setConstraint methods specifies an edge name and must be one of the SpringLayout constants NORTH , SOUTH , EAST , or WEST .

Table 13. Spring Methods

Method

Purpose

static Spring constant(int) static Spring constant(int, int, int)

Create a spring that doesn't track a component's sizes. The three-argument version creates a spring with its minimum, preferred, and maximum values set to the specified values, in that order. The one-argument version creates a spring with its minimum, preferred, and maximum values all set to the specified integer.

static Spring sum(Spring, Spring) static Spring max(Spring, Spring) static Spring minus(Spring)

Create a spring that is the result of some mathematical manipulation. The sum method adds two springs. The max method returns a spring whose value is always greater than or equal to the values of the two arguments. The minus method returns a spring running in the opposite direction of the argument. The minus method can be used to create an argument to the sum method, letting you get the difference between two springs.

int getMinimumValue() int getPreferredValue() int getMaximumValue()

Gets the corresponding value from the spring. For a SpringLayout -created spring that automatically tracks a component, these methods result in calls to the component's corresponding get Xxx Size method.

setValue(int)

Sets the spring's current value.

Examples That Use SpringLayout

The following table lists some examples that use spring layout.

Example

Where Described

Notes

SpringDemo3

This section

Uses SpringLayout to create a row of evenly spaced , natural-size components.

SpringGrid

This section

Uses SpringLayout and the makeGrid utility method to create a layout where all components are the same size.

SpringCompactGrid

This section

Uses SpringLayout and the makeCompactGrid utility method to create a layout where all of the components in a row have the same height and all of the components in a column have the same width.

SpringForm

This section

Uses SpringLayout and makeCompactGrid to align label “text field pairs.

SpringBox

This section

Uses SpringLayout and makeCompactGrid to demonstrate laying out a single row of components and what happens when no springs can grow.

SpinnerDemo

How to Use Spinners (page 357)

Uses SpringLayout and makeCompactGrid to lay out rows of label “spinner pairs.

TextInputDemo

How to Use Formatted Text Fields (page 221)

Uses SpringLayout and makeCompactGrid to lay out rows of labeled components. The components are a mix of text fields, formatted text fields, and spinners.

 <  Day Day Up  >  

Категории