Main Page

Previous Next

Container Layout Managers

An object called a layout manager determines the way that components are arranged in a container. All containers will have a default layout manager but you can choose a different layout manager when necessary. There are many layout manager classes provided in the java.awt and javax.swing packages, so we will introduce those that you are most likely to need. It is possible to create your own layout manager classes, but creating layout managers is beyond the scope of this book. The layout manager for a container determines the position and size of all the components in the container: you should not change the size and position of such components yourself. Just let the layout manager take care of it.

Since the classes that define layout managers all implement the LayoutManager interface, you can use a variable of type LayoutManager to store any of them if necessary. We will look at six layout manager classes in a little more detail. The names of these classes and the basic arrangements that they provide are as follows:

Layout Manager

Description

FlowLayout

Places components in successive rows in a container, fitting as many on each row as possible, and starting on the next row as soon as a row is full. This works in much the same way as your text processor placing words on a line. Its primary use is for arranging buttons although you can use it with other components. It is the default layout manager for JPanel objects.

BorderLayout

Places components against any of the four borders of the container and in the center. The component in the center fills the available space. This layout manager is the default for the contentPane in a JFrame, JDialog, or JApplet object.

CardLayout

Places components in a container one on top of the other – like a deck of cards. Only the 'top' component is visible at any one time.

GridLayout

Places components in the container in a rectangular grid with the number of rows and columns that you specify.

GridBagLayout

This also places the components into an arrangement of rows and columns but the rows and columns can vary in length. This is a complicated layout manager with a lot of flexibility in how you control where components are placed in a container.

BoxLayout

This arranges components either in a row or in a column. In either case the components are clipped to fit if necessary, rather than wrapping to the next row or column. The BoxLayout manager is the default for the Box container class.

SpringLayout

Allows components to have their position defined by 'springs' or 'struts' fixed to an edge of the container or another component in the container.

The BoxLayout, SpringLayout, and Box classes are defined in the javax.swing package. The other layout manager classes in the list above are defined in java.awt.

One question to ask is why do we need layout managers at all? Why don't we just place components at some given position in a container? The basic reason is to ensure that the GUI elements for your Java program are displayed properly in every possible Java environment. Layout managers automatically adjust components to fit the space available. If you fix the size and position of each of the components, they could run into one another and overlap if the screen area available to your program is reduced.

To set the layout manager of a container, you can call the setLayout() method for the container. For example, you could change the layout manager for the container object aWindow of type JFrame to flow layout with the statements:

FlowLayout flow = new FlowLayout();
aWindow.getContentPane().setLayout(flow);

Remember that we can't add components directly to a JFrame object – we must add them to the content pane for the window. The same goes for JDialog and JApplet objects.

With some containers you can set the layout manager in the constructor for that container, as we shall see in later examples. Let's look at how the layout managers work, and how to use them in practice.

The Flow Layout Manager

The flow layout manager places components in a row, and when the row is full, it automatically spills components onto the next row. The default positioning of the row of components is centered in the container. There are actually three possible row-positioning options that you specify by constants defined in the class. These are FlowLayout.LEFT, FlowLayout.RIGHT, and FlowLayout.CENTER – this last option being the default.

The flow layout manager is very easy to use, so let's jump straight in and see it working in an example.

Try It Out – Using a Flow Layout Manager

As we said earlier, this layout manager is used primarily to arrange a few components whose relative position is unimportant. Let's implement a TryFlowLayout program based on the TryWindow example:

import javax.swing.JFrame;
import javax.swing.JButton;

import java.awt.Toolkit;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.FlowLayout;

public class TryFlowLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Flow Layout"); 

  public static void main(String[] args) {
    Toolkit theKit = aWindow.getToolkit();         // Get the window toolkit
    Dimension wndSize = theKit.getScreenSize();    // Get screen size
    // Set the position to screen center & size to half screen size
    aWindow.setBounds(wndSize.width/4, wndSize.height/4,   // Position
                      wndSize.width/2, wndSize.height/2);  // Size
    aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    FlowLayout flow = new FlowLayout();            // Create a layout manager
    Container content = aWindow.getContentPane();  // Get the content pane
    content.setLayout(flow);                       // Set the container layout mgr

    // Now add six button components
    for(int i = 1; i <= 6; i++)
      content.add(new JButton("Press " + i));      // Add a Button to content pane

    aWindow.setVisible(true);                      // Display the window
  }
}

Since it is based on the TryWindow class, only the new code is highlighted. The new code is quite simple. We create a FlowLayout object and make this the layout manager for aWindow by calling setLayout(). We then add six JButton components of a default size to aWindow in the loop.

If you compile and run the program you should get a window similar to the following:

Click To expand

The Button objects are positioned by the layout manager flow. As you can see, they have been added to the first row in the window, and the row is centered. You can confirm that the row is centered and see how the layout manger automatically spills the components on to the next row once a row is full by reducing the size of the window.

Click To expand

Here the second row is clearly centered. Each button component has been set to its preferred size, which comfortably accommodates the text for the label. The centering is determined by the alignment constraint for the layout manager, which defaults to CENTER.

It can also be set to RIGHT or LEFT by using a different constructor. For example, you could have created the layout manager with the statement:

FlowLayout flow = new FlowLayout(FlowLayout.LEFT); 

The flow layout manager then left-aligns each row of components in the container. If you run the program with this definition and resize the window, it will look like:

Click To expand

Now the buttons are left aligned. Two of the buttons have spilled from the first row to the second because there is insufficient space across the width of the window to accommodate them all.

The flow layout manager in the previous examples applies a default gap of 5 pixels between components in a row, and between one row and the next. You can choose values for the horizontal and vertical gaps by using yet another FlowLayout constructor. You can set the horizontal gap to 20 pixels and the vertical gap to 30 pixels with the statement:

FlowLayout flow = new FlowLayout(FlowLayout.LEFT, 20, 30); 

If you run the program with this definition of the layout manager, when you resize the window you will see the components distributed with the spacing specified.

You can also set the gaps between components and rows explicitly by calling the setHgap() or the setVgap() method. To set the horizontal gap to 35 pixels, you would write:

flow.setHgap(35);                   // Set the horizontal gap

Don't be misled by this. You can't get differential spacing between components by setting the gap before adding each component to a container. The last values for the gaps between components that you set for a layout manager will apply to all the components in a container. The methods getHgap() and getVgap() will return the current setting for the horizontal or vertical gap as a value of type int.

The initial size at which the application window is displayed is determined by the values we pass to the setBounds() method for the JFrame object. If you want the window to assume a size that just accommodates the components it contains, you can call the pack() method for the JFrame object. Add the following line immediately before the call to setVisible():

aWindow.pack();

If you recompile and run the example again, the application window should fit the components.

As we've said, you add components to an applet created as a JApplet object in the same way as for a JFrame application window. We can verify this by adding some buttons to an example of an applet. We can try out a Font object and add a border to the buttons to brighten them up a bit at the same time.

Try It Out – Adding Buttons to an Applet

We can define the class for our applet as follows:

import javax.swing.JButton;
import javax.swing.JApplet;

import java.awt.Font;
import java.awt.Container;
import java.awt.FlowLayout;

import javax.swing.border.BevelBorder;

public class TryApplet extends JApplet {
  public void init() {
    Container content = getContentPane();                // Get content pane
    content.setLayout(new FlowLayout(FlowLayout.RIGHT)); // Set layout

    JButton button;                                      // Stores a button
    Font[] fonts = { new Font("Arial", Font.ITALIC, 10), // Two fonts
                     new Font("Playbill", Font.PLAIN, 14)
                   };

    BevelBorder edge = new BevelBorder(BevelBorder.RAISED); // Bevelled border

    // Add the buttons using alternate fonts
    for(int i = 1; i <= 6; i++) {
      content.add(button = new JButton("Press " + i));  // Add the button
      button.setFont(fonts[i%2]);                // One of our own fonts
      button.setBorder(edge);                    // Set the button border
    }
  }
}

Of course, to run the applet we will need an .html file containing the following:

<APPLET CODE="TryApplet.class" WIDTH=300 HEIGHT=200>
</APPLET>

This specifies the width and height of the applet – you can use your own values here if you wish. You can save the file as TryApplet.html.

Once you have compiled the applet source code using javac, you can execute it with the appletviewer program by entering the following command from the folder the .html file and class are in:

appletviewer TryApplet.html

You should see the AppletViewer window displaying our applet.

The arrangement of the buttons is now right justified in the flow layout. We have the button labels alternating between the two fonts that we created. The buttons also look more like buttons with a beveled edge.

How It Works

As we saw in Chapter 1, an applet is executed rather differently from a Java program and it is not really an independent program at all. The browser (or appletviewer in this case) initiates and controls the execution of the applet. An applet does not require a main() method. To execute the applet, the browser first creates an instance of our applet class, TryApplet, and then calls the init() method for it. This method is inherited from the Applet class (the base for JApplet) and you typically override this method to provide your own initialization.

We need the import statement for java.awt in addition to that for javax.swing because our code refers to the Font, Container, and FlowLayout classes.

Before creating the buttons, we create a BevelBorder object that we will use to specify the border for each button. In the loop that adds the buttons to the content pane for the applet, we select one or other of the Font objects we have created depending on whether the loop index is even or odd, and then set edge as the border by calling the setBorder()member. This would be the same for any component. Note how the size of each button is automatically adjusted to accommodate the button label. Of course, the font selection depends on the two fonts being available on your system, so if you don't have the ones that appear in the code, change it to suit what you have.

The buttons look much better with raised edges. If you wanted them to appear sunken, you would specify BevelBorder.LOWERED as the constructor argument. You might like to try out a SoftBevelBorder too. All you need to do is use the class name, SoftBevelBorder, when creating the border.

Using a Border Layout Manager

The border layout manager is intended to place up to five components in a container. Possible positions for these components are on any of the four borders of the container and in the center. Only one component can be at each position. If you add a component at a position that is already occupied, the previous component will be displaced. A border is selected by specifying a constraint that can be NORTH, SOUTH, EAST, WEST, or CENTER. These are all final static constants defined in the BorderLayout.class.

You can't specify the constraints in the BorderLayout constructor since a different constraint has to be applied to each component. You specify the position of each component in a container when you add it using the add() method. We can modify the earlier application example to add five buttons to the content pane of the application window in a border layout:

Try It Out – Testing the BorderLayout Manager

Make the following changes to TryFlowLayout.java to try out the border layout manager and exercise another border class:

import javax.swing.JFrame;
import javax.swing.JButton;

import java.awt.Toolkit;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.BorderLayout;

import javax.swing.border.EtchedBorder;

public class TryBorderLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Border Layout"); 

  public static void main(String[] args) {
    Toolkit theKit = aWindow.getToolkit();       // Get the window toolkit
    Dimension wndSize = theKit.getScreenSize();  // Get screen size

    // Set the position to screen center & size to half screen size
    aWindow.setBounds(wndSize.width/4, wndSize.height/4,   // Position
                      wndSize.width/2, wndSize.height/2);  // Size
    aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    BorderLayout border = new BorderLayout();        // Create a layout manager
    Container content = aWindow.getContentPane();   // Get the content pane
    content.setLayout(border);                    // Set the container layout mgr
    EtchedBorder edge = new EtchedBorder(EtchedBorder.RAISED);  // Button border   
    // Now add five JButton components and set their borders
    JButton button;
    content.add(button = new JButton("EAST"), BorderLayout.EAST); 
    button.setBorder(edge);
    content.add(button = new JButton("WEST"), BorderLayout.WEST); 
    button.setBorder(edge);
    content.add(button = new JButton("NORTH"), BorderLayout.NORTH); 
    button.setBorder(edge);
    content.add(button = new JButton("SOUTH"), BorderLayout.SOUTH); 
    button.setBorder(edge);
    content.add(button = new JButton("CENTER"), BorderLayout.CENTER);
    button.setBorder(edge);

    aWindow.setVisible(true);                              // Display the window
  }
}

If you compile and execute the example, you will see the window shown below.

You can see here how a raised EtchedBorder edge to the buttons looks.

How It Works

Components laid out with a border layout manager are extended to fill the space available in the container. The "NORTH" and "SOUTH" buttons are the full width of the window and the "EAST" and "WEST" buttons occupy the height remaining unoccupied once the "NORTH" and "SOUTH" buttons are in place. It always works like this, regardless of the sequence in which you add the buttons – the "NORTH" and "SOUTH" components occupy the full width of the container and the "CENTER" component takes up the remaining space. If there are no "NORTH" and "SOUTH" components, the "EAST" and "WEST" components will extend to the full height of the container.

The width of the "EAST" and "WEST" buttons is determined by the space required to display the button labels. Similarly, the "NORTH" and "SOUTH" buttons are determined by the height of the characters in the labels.

You can alter the spacing between components by passing arguments to the BorderLayout constructor – the default gaps are zero. For example, you could set the horizontal gap to 20 pixels and the vertical gap to 30 pixels with the statement:

content.setLayout(new BorderLayout(20, 30));

Like the flow layout manager, you can also set the gaps individually by calling the methods setHgap() and setVgap() for the BorderLayout object. For example:

BorderLayout border = new BorderLayout();  // Construct the object
content.setLayout(border);                 // Set the layout
border.setHgap(20);                        // Set horizontal gap

This sets the horizontal gap between the components to 20 pixels and leaves the vertical gap at the default value of zero. You can also retrieve the current values for the gaps with the getHgap() and getVgap() methods.

Using a Card Layout Manager

The card layout manager generates a stack of components, one on top of the other. The first component that you add to the container will be at the top of the stack, and therefore visible, and the last one will be at the bottom. You can create a CardLayout object with the default constructor, CardLayout(), or you can specify horizontal and vertical gaps as arguments to the constructor. The gaps in this case are between the edge of the component and the boundary of the container. We can see how this works in an applet:

Try It Out – Dealing Components

Because of the way a card layout works, we need a way to interact with the applet to switch from one component to the next. We will implement this by enabling mouse events to be processed, but we won't explain the code that does this in detail here. We will leave that to the next chapter.

Try the following code:

import javax.swing.JApplet;
import javax.swing.JButton;

import java.awt.Container;
import java.awt.CardLayout;

import java.awt.event.ActionEvent;                   // Classes to handle events
import java.awt.event.ActionListener;

public class TryCardLayout extends JApplet implements ActionListener {                     
  CardLayout card = new CardLayout(50,50);           // Create layout

  public void init() {
    Container content = getContentPane();
    content.setLayout(card);                         // Set card as the layout mgr
    JButton button;                                  // Stores a button
    for(int i = 1; i <= 6; i++) {
      content.add(button = new JButton("Press " + i), "Card" + i); // Add a button
      button.addActionListener(this);                // Add listener for button
    }
  }

  // Handle button events
  public void actionPerformed(ActionEvent e) {
      card.next(getContentPane());                  // Switch to the next card
  }
}

If you run the program the applet should be as shown below. Click on the button – and the next button will be displayed.

How It Works

The CardLayout object, card, is created with horizontal and vertical gaps of fifty pixels. In the init() method for our applet, we set card as the layout manager and add six buttons to the content pane. Note that we have two arguments to the add() method. Using card layout requires that you identify each component by some Object. In this case we pass a String object as the second argument to the add() method. We use an arbitrary string for each consisting of the string "Card" with the sequence number of the button appended to it.

Within the loop we call the addActionListener() method for each button to identify our applet object as the object that will handle events generated for the button (such as clicking on it with the mouse). When you click on a button, the actionPerformed() method for the applet object will be called. This just calls the next() method for the layout object to move the next component in sequence to the top. We will look at event handling in more detail in the next chapter.

The argument to the next()method identifies the container as the TryCardLayout object that is created when the applet starts. The CardLayout class has other methods that you can use for selecting from the stack of components:

Method

Description

void previous(Container parent)

Selects the previous component in the container, parent.

void first(Container parent)

Selects the first component in the container, parent.

void last(Container parent)

Selects the last component in the container, parent.

void show(Container parent, String name)

Selects the component in the container, parent, associated with the String object, name. This must be one of the String objects specified when you called the add() method to add components.

Using the next() or previous() methods you can cycle through the components repeatedly, since the next component after the last is the first, and the component before the first is the last.

The String object that we supplied when adding the buttons identifies each button and can be used to switch to any of them. For instance, you could switch to the button associated with "Card4" before the applet is displayed by adding the following statement after the loop that adds the buttons:

    card.show(content, "Card4");    // Switch to button "Card4"

This calls the show() method for the layout manager. The first argument is the container and the second argument is the object identifying the component to be at the top.

Using a Grid Layout Manager

A grid layout manager arranges components in a rectangular grid within the container. There are three constructors for creating GridLayout objects:

Constructor

Description

GridLayout()

Creates a grid layout manager that will arrange components in a single row (that is, a single column per component) with no gaps between components.

GridLayout(int rows, int cols)

Creates a grid layout manager that arranges components in a grid with rows number of rows and cols number of columns, and with no gaps between components.

GridLayout(int rows, int cols, int hgap, int vgap)

Creates a grid layout manager that arranges components in a grid with rows number of rows and cols number of columns, and with horizontal and vertical gaps between components of hgap and vgap pixels, respectively.

In the second and third constructors shown above, you can specify either the number of rows, or the number of columns as zero (but not both). If you specify the number of rows as zero, the layout manager will provide as many rows in the grid as are necessary to accommodate the number of components you add to the container. Similarly, setting the number of columns as zero indicates an arbitrary number of columns. If you fix both the rows and the columns, and add more components to the container than the grid will accommodate, the number of columns will be increased appropriately.

We can try out a grid layout manager in a variation of a previous application:

Try It Out – Gridlocking Buttons

Make the highlighted changes to TryWindow.java.

import javax.swing.JFrame;
import java.awt.*;
import javax.swing.border.EtchedBorder;
public class TryGridLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Grid Layout"); 

  public static void main(String[] args) {
    Toolkit theKit = aWindow.getToolkit();          // Get the window toolkit
    Dimension wndSize = theKit.getScreenSize();     // Get screen size

    // Set the position to screen center & size to half screen size
    aWindow.setBounds(wndSize.width/4, wndSize.height/4,   // Position
                      wndSize.width/2, wndSize.height/2);  // Size
    aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    GridLayout grid = new GridLayout(3,4,30,20);   // Create a layout manager
    Container content = aWindow.getContentPane();  // Get the content pane
    content. setLayout(grid);                      // Set the container layout mgr

    EtchedBorder edge = new EtchedBorder(EtchedBorder.RAISED);  // Button border   

    // Now add ten Button components
    JButton button;                                             // Stores a button
    for(int i = 1; i <= 10; i++) {
      content.add(button = new JButton("Press " + i));          // Add a Button 
      button.setBorder(edge);                                   // Set the border
    }
      
    aWindow.setVisible(true);                              // Display the window
  }
}

We create a grid layout manager, grid, for three rows and four columns, and with horizontal and vertical gaps between components of 30 and 20 pixels respectively. With ten buttons in the container, the application window will be as shown below.

Using a BoxLayout Manager

The BoxLayout class defines a layout manager that arranges components in either a single row or a single column. You specify whether you want a row-wise or a columnar arrangement when creating the BoxLayout object. The BoxLayout constructor requires two arguments. The first is a reference to the container to which the layout manager applies, and the second is a constant value that can be either BoxLayout.X_AXIS for a row arrangement, or BoxLayout.Y_AXIS for a column arrangement.

Components are added from left to right in a row, or from top to bottom in a column. Components in the row or column do not spill onto the next row or column when the row is full. When this occurs, the layout manager will reduce the size of components or even clip them if necessary and keep them all in a single row or column. With a row of components, the box layout manager will try to make all the components the same height, and try to set a column of components to the same width.

The container class, Box, is particularly convenient when you need to use a box layout since it has a BoxLayout manager built in. It also has some added facilities providing more flexibility in the arrangement of components than other containers, such as JPanel objects, provide. The Box constructor accepts a single argument that specifies the orientation as either BoxLayout.X_AXIS or BoxLayout.Y_AXIS. The class also has two static methods, createHorizontalBox()and createVerticalBox(), that each return a reference to a Box container with the orientation implied.

As we said earlier a container can contain another container, so you can easily place a Box container inside another Box container to get any arrangement of rows and columns that you want. Let's try that out.

Try It Out – Boxes Containing Boxes

We will create an application that has a window containing a column of radio buttons on the left, a column of checkboxes on the right, and a row of buttons across the bottom. Here's the code:

import javax.swing.*;

import java.awt.Toolkit;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.BorderLayout;

import javax.swing.border.Border;

public class TryBoxLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Box Layout");

  public static void main(String[] args) {
    Toolkit theKit = aWindow.getToolkit();       // Get the window toolkit
    Dimension wndSize = theKit.getScreenSize();  // Get screen size

    // Set the position to screen center & size to half screen size
    aWindow.setBounds(wndSize.width/4, wndSize.height/4,   // Position
                      wndSize.width/2, wndSize.height/2);  // Size
    aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // Create left column of radio buttons
    Box left = Box.createVerticalBox();
    ButtonGroup radioGroup = new ButtonGroup();             // Create button group
    JRadioButton rbutton;                                   // Stores a button
    radioGroup.add(rbutton = new JRadioButton("Red"));      // Add to group
    left.add(rbutton);                                      // Add to Box
    radioGroup.add(rbutton = new JRadioButton("Green"));
    left.add(rbutton);
    radioGroup.add(rbutton = new JRadioButton("Blue"));
    left.add(rbutton);
    radioGroup.add(rbutton = new JRadioButton("Yellow"));
    left.add(rbutton);

    // Create right columns of checkboxes
    Box right = Box.createVerticalBox();
    right.add(new JCheckBox("Dashed"));
    right.add(new JCheckBox("Thick"));
    right.add(new JCheckBox("Rounded"));

    // Create top row to hold left and right
    Box top = Box.createHorizontalBox();
    top.add(left);
    top.add(right);
    // Create bottom row of buttons
    JPanel bottomPanel = new JPanel();
    Border edge = BorderFactory.createRaisedBevelBorder();  // Button border   
    JButton button;
    Dimension size = new Dimension(80,20);
    bottomPanel.add(button = new JButton("Defaults"));
    button.setBorder(edge);
    button.setPreferredSize(size);
    bottomPanel.add(button = new JButton("OK"));
    button.setBorder(edge);
    button.setPreferredSize(size);
    bottomPanel.add(button = new JButton("Cancel"));
    button.setBorder(edge);
    button.setPreferredSize(size);

    // Add top and bottom panel to content pane
    Container content = aWindow.getContentPane();    // Get content pane
    content.setLayout(new BorderLayout());           // Set border layout manager
    content.add(top, BorderLayout.CENTER);
    content.add(bottomPanel, BorderLayout.SOUTH);

    aWindow.setVisible(true);                        // Display the window
  }
}

When you run this example and try out the radio buttons and checkboxes, it should produce a window something like that shown overleaf.

It's not an ideal arrangement, but we will improve on it.

How It Works

The shaded code is of interest – the rest we have seen before. The first block creates the left column of radio buttons providing a color choice. A Box object with a vertical orientation is used to contain the radio buttons. If you tried the radio buttons you will have found that only one of them can ever be selected. This is the effect of the ButtonGroup object that is used – to ensure radio buttons operate properly, you must add them to a ButtonGroup object.

The ButtonGroup object ensures that only one of the radio buttons it contains can be selected at any one time. Note that a ButtonGroup object is not a component – it's just a logical grouping of radio buttons – so you can't add it to a container. We must still independently add the buttons to the Box container that manages their physical arrangement. The Box object for the right-hand group of JCheckBox objects works in the same way as that for the radio buttons.

Both the Box objects holding the columns are added to another Box object that implements a horizontal arrangement to position them side-by-side. Note how the vertical Box objects adjust their width to match that of the largest component in the column. That's why the two columns are bunched towards the left side. We will see how to improve on this in a moment.

We use a JPanel object to hold the buttons. This has a flow layout manager by default, which suits us here. Calling the setPreferredSize() method for each button sets the preferred width and height to that specified by the Dimension object, size. This ensures that, space permitting, each button will be 80 pixels wide and 20 pixels high.

We have introduced another way of obtaining a border for a component here. The BorderFactory class (defined in the javax.swing package) contains static methods that return standard borders of various kinds. The createBevelBorder() method returns a reference to a BevelBorder object as type Border – Border being an interface that all border objects implement. We use this border for each of the buttons. We will try some more of the methods in the BorderFactory class later.

To improve the layout of the application window, we can make use of some additional facilities provided by a Box container.

Struts and Glue

The Box class contains static methods to create an invisible component called a strut. A vertical strut has a given height in pixels and zero width. A horizontal strut has a given width in pixels and zero height. The purpose of these struts is to enable you to insert space between your components, either vertically or horizontally. By placing a horizontal strut between two components in a horizontally arranged Box container, you fix the distance between the components. By adding a horizontal strut to a vertically arranged Box container, you can force a minimum width on the container. You can use a vertical strut in a horizontal box to force a minimum height.

Click To expand

Note that although vertical struts have zero width, they have no maximum width so they can expand horizontally to have a width that takes up any excess space. Similarly, the height of a horizontal strut will expand when there is excess vertical space available.

A vertical strut is returned as an object of type Component by the static createVerticalStrut() method in the Box class . The argument specifies the height of the strut in pixels. To create a horizontal strut, you use the createHorizontalStrut() method.

We can space out our radio buttons by inserting struts between them:

    // Create left column of radio buttons
    Box left = Box.createVerticalBox();
    left.add(Box.createVerticalStrut(30));              // Starting space
    ButtonGroup radioGroup = new ButtonGroup();         // Create button group
    JRadioButton rbutton;                               // Stores a button
    radioGroup.add(rbutton = new JRadioButton("Red"));  // Add to group
    left.add(rbutton);                                  // Add to Box
    left.add(Box.createVerticalStrut(30));              // Space between
    radioGroup.add(rbutton = new JRadioButton("Green"));
    left.add(rbutton);
    left.add(Box.createVerticalStrut(30));              // Space between
    radioGroup.add(rbutton = new JRadioButton("Blue"));
    left.add(rbutton);
    left.add(Box.createVerticalStrut(30));              // Space between
    radioGroup.add(rbutton = new JRadioButton("Yellow"));
    left.add(rbutton);

The extra statements add a 30 pixel vertical strut at the start of the columns, and a further strut of the same size between each radio button and the next. We can do the same for the checkboxes:

    // Create right columns of checkboxes
    Box right = Box.createVerticalBox();
    right.add(Box.createVerticalStrut(30));             // Starting space
    right.add(new JCheckBox("Dashed"));
    right.add(Box.createVerticalStrut(30));             // Space between
    right.add(new JCheckBox("Thick"));
    right.add(Box.createVerticalStrut(30));             // Space between
    right.add(new JCheckBox("Rounded"));

If you run the example with these changes the window will look like this:

It's better, but far from perfect. The columns are now equally spaced in the window because the vertical struts have assumed a width to take up the excess horizontal space that is available. The distribution of surplus space vertically is different in the two columns because the number of components is different. We can control where surplus space goes in a Box object with glue. Glue is an invisible component that has the sole function of taking up surplus space in a Box container.

While the name gives the impression that it binds components together, it, in fact, provides an elastic connector between two components that can expand or contract as necessary, so it acts more like a spring. Glue components can be placed between the actual components in the Box and at either or both ends. Any surplus space that arises after the actual components have been accommodated is distributed between the glue components added. If you wanted all the surplus space to be at the beginning of a Box container, for instance, you should first add a single glue component in the container.

You create a component that represents glue by calling the createGlue() method for a Box object. You then add the glue component to the Box container in the same way as any other component wherever you want surplus space to be taken up. You can add glue at several positions in a row or column, and spare space will be distributed between the glue components. We can add glue after the last component in each column to make all the spare space appear at the end of each column of buttons. For the radio buttons we can add the statement,

// Statements adding radio buttons to left Box object
left.add(Box.createGlue());                      // Glue at the end

and similarly for the right box. The glue component at the end of each column of buttons will take up all the surplus space in each vertical Box container. This will make the buttons line up at the top. Running the program with added glue will result in the following application window.

It's better now, but let's put together a final version of the example with some additional embroidery.

Try It Out – Embroidering Boxes

We will use some JPanel objects with a new kind of border to contain the vertical Box containers.

import javax.swing.*;
import java.awt.*;
import javax.swing.border.*;

public class TryBoxLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Box Layout"); 

  public static void main(String[] args) {
    // Set up the window as before...

    // Create left column of radio buttons with struts and glue as above...
    // Create a panel with a titled border to hold the left Box container
    JPanel leftPanel = new JPanel(new BorderLayout());
    leftPanel.setBorder(new TitledBorder(
                                   new EtchedBorder(),    // Border to use
                                   "Line Color"));        // Border title
    leftPanel.add(left, BorderLayout.CENTER);

    // Create right columns of checkboxes with struts and glue as above...
    // Create a panel with a titled border to hold the right Box container
    JPanel rightPanel = new JPanel(new BorderLayout());
    rightPanel.setBorder(new TitledBorder(
                                   new EtchedBorder(),    // Border to use
                                   "Line Properties"));   // Border title
    rightPanel.add(right, BorderLayout.CENTER);

    // Create top row to hold left and right
    Box top = Box.createHorizontalBox();
    top.add(leftPanel);
    top.add(Box.createHorizontalStrut(5));         // Space between vertical boxes
    top.add(rightPanel);

    // Create bottom row of buttons
    JPanel bottomPanel = new JPanel();
    bottomPanel.setBorder(new CompoundBorder(
           BorderFactory.createLineBorder(Color.black, 1),         // Outer border
           BorderFactory.createBevelBorder(BevelBorder.RAISED)));  // Inner border

    // Create and add the buttons as before...
    Container content = aWindow.getContentPane();  // Set the container layout mgr
    BoxLayout box = new BoxLayout(content, BoxLayout.Y_AXIS); 
                                                   // Vertical for content pane
    content.setLayout(box);         // Set box layout manager
    content.add(top);
    content.add(bottomPanel);
    aWindow.setVisible(true);                      // Display the window
  }
}

The example will now display the window shown below.

Click To expand

How It Works

Both vertical boxes are now contained in a JPanel container. Since JPanel objects are Swing components, we can add a border, and this time we add a TitledBorder border that we create directly using the constructor. A TitledBorder is a border specified by the first argument to the constructor, plus a title that is a String specified by the second argument to the constructor. We use a border of type EtchedBorder here, but you can use any type of border.

We introduce space between the two vertically aligned Box containers by adding a horizontal strut to the Box container that contains them. If you wanted space at each side of the window, you could add struts to the container before and after the components.

The last improvement is to the panel holding the buttons along the bottom of the window. We now have a border that is composed of two types, one inside the other: a LineBorder and a BevelBorder. A CompoundBorder object defines a border that is a composite of two border objects, the first argument to the constructor being the outer border and the second being the inner border. The LineBorder class defines a border consisting of a single line of the color specified by its first constructor argument and a thickness in pixels specified by the second. There is a static method defined for the class, createBlackLineBorder() that creates a black line border that is one pixel wide, so we could have used that here.

Using a GridBagLayout Manager

The GridBagLayout manager is much more flexible than the other layout managers we have seen and, consequently, rather more complicated to use. The basic mechanism arranges components in an arbitrary rectangular grid, but the rows and columns of the grid are not necessarily the same height or width. A component is placed at a given cell position in the grid specified by the coordinates of the cell, where the cell at the top-left corner is at position (0, 0). A component can occupy more than one cell in a row and/or column in the grid, but it always occupies a rectangular group of cells.

Each component in a GridBagLayout has its own set of constraints. These are defined by an object of type GridBagConstraints that you associate with each component, before adding the component to the container. The location of each component, its relative size, and the area it occupies in the grid, are all determined by its associated GridBagConstraints object.

A GridBagConstraints object has no less than eleven public instance variables that may be set to define the constraints for a component. Since they also interact with each other there's more entertainment here than with a Rubik's cube. Let's first get a rough idea of what these instance variables in a GridBagConstraints object do:

Instance Variable

Description

gridx and gridy

Determines the position of the component in the container as coordinate positions of cells in the grid, where (0, 0) is the top-left position in the grid.

gridwidth and gridheight

Determines the size of the area occupied by the component in the container.

weightx and weighty

Determines how free space is distributed between components in the container.

anchor

Determines where a component is positioned within the area allocated to it in the container.

ipadx and ipady

Determines by how much the component size is to be increased above its minimum size.

fill

Determines how the component is to be enlarged to fill the space allocated to it.

insets

Specifies the free space that is to be provided around the component within the space allocated to it in the container.

That seems straightforward enough. We can now explore the possible values we can set for these and then try them out.

GridBagConstraints Instance Variables

A component will occupy at least one grid position, or cell, in a container that uses a GridBagLayout object, but it can occupy any rectangular array of cells. The total number of rows and columns, and thus the cell size, in the grid for a container is variable, and determined by the constraints for all of the components in the container. Each component will have a position in the grid plus an area it is allocated defined by a number of horizontal and vertical grid positions.

Click To expand

The top-left cell in a layout is at position (0, 0). You specify the position of a component by defining where the top-left cell that it occupies is, relative to either the grid origin, or relative to the last component that was added to the container. You specify the position of the top-left cell that a component occupies in the grid by setting values of type int for the gridx and gridy members of the GridBagConstraints object. The default value for gridx is GridBagConstraints.RELATIVE – a constant that places the top-left grid position for the component in the column immediately to the right of the previous component. The same value is the default for gridy, which places the next component immediately below the previous one.

You specify the number of cells occupied by a component horizontally and vertically by setting values for the gridwidth and gridheight instance variables for the GridBagConstraints object. The default value for both of these is 1. There are two constants you can use as values for these variables. With a value of GridBagConstraints.REMAINDER, the component will be the last one in the row or column. If you specify the value as GridBagConstraints.RELATIVE, the component will be the penultimate one in the row or column.

If the preferred size of the component is less than the display area, you can control how the size of the component is adjusted to fit the display area by setting the fill and insets instance variables for the GridBagConstraints object.

Variable

Description

fill

The value for this variable is of type int, and it determines how the size of the component is adjusted in relation to the array of cells it occupies. The default value of GridBagConstraints.NONE means that the component is not resized.

A value of GridBagConstraints.HORIZONTAL adjusts the width of the component to fill the display area.

A value of GridBagConstraints.VERTICAL adjusts the height of the component to fill the display area.

A value of GridBagConstraints.BOTH adjusts the height and the width to completely fill the display area.

insets

This variable stores a reference to an object of type Insets. An Insets object defines the space allowed between the edges of the components and boundaries of the display area it occupies. Four parameter values to the class constructor define the top, left-side, bottom and right-side padding from the edges of the component. The default value is Insets(0, 0, 0, 0).

If you don't intend to expand a component to fill its display area, you may still want to enlarge the component from its minimum size. You can adjust the dimensions of the component by setting the following GridBagConstraints instance variables:

Variable

Description

ipadx

An int value that defines the number of pixels by which the top and bottom edges of the component are to be expanded. The default value is 0.

ipady

An int value that defines the number of pixels by which the left and right edges of the component are to be expanded. The default value is 0.

If the component is still smaller than its display area in the container, you can specify where it should be placed in relation to its display area by setting a value for the anchor instance variable of the GridBagConstraints object. Possible values are NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST, and CENTER, all of which are defined in the GridBagConstraints class.

The last GridBagConstraints instance variables to consider are weightx and weighty which are of type double. These determine how space in the container is distributed between components in the horizontal and vertical directions. You should always set a value for these, otherwise the default of 0 will cause the components to be bunched together adjacent to one another in the center of the container. The absolute values for weightx and weighty are not important. It is the relative values that matter. If you set all the values the same (but not zero), the space for each component will be distributed uniformly. Space is distributed in the proportions defined by the values.

For example, if three components in a row have weightx values of 1.0, 2.0, and 3.0, the first will get 1/6 of the total in the x direction, the second will get 1/3, and the third will get half. The proportion of the available space that a component gets in the x direction is the weightx value for the component divided by the sum of the weightx values in the row. This also applies to the weighty values for allocating space in the y direction.

We'll start with a simple example of placing two buttons in a window, and introduce another way of obtaining a standard border for a component.

Try It Out – Applying the GridBagConstraints Object

Make the following changes to the previous program and try out the GridBagLayout manager.

import javax.swing.*;
import java.awt.*; 
import javax.swing.border.Border;

public class TryGridBagLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Gridbag Layout"); 

  public static void main(String[] args) {
    Toolkit theKit = aWindow.getToolkit();       // Get the window toolkit
    Dimension wndSize = theKit.getScreenSize();  // Get screen size

    // Set the position to screen center & size to half screen size
    aWindow.setBounds(wndSize.width/4, wndSize.height/4,   // Position
                      wndSize.width/2, wndSize.height/2);  // Size
    aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    GridBagLayout gridbag = new GridBagLayout();    // Create a layout manager
    GridBagConstraints constraints = new GridBagConstraints();
    aWindow.getContentPane().setLayout(gridbag);   // Set the container layout mgr

    // Set constraints and add first button
    constraints.weightx = constraints.weighty = 10.0;
    constraints.fill = constraints.BOTH;            // Fill the space
    addButton("Press", constraints, gridbag);       // Add the button

    // Set constraints and add second button 
    constraints.gridwidth = constraints.REMAINDER; // Rest of the row
    addButton("GO", constraints, gridbag);         // Create and add button

    aWindow.setVisible(true);                              // Display the window
  }

  static void addButton(String label,
                        GridBagConstraints constraints,
                        GridBagLayout layout) {
    // Create a Border object using a BorderFactory method
    Border edge = BorderFactory.createRaisedBevelBorder(); 

    JButton button = new JButton(label);           // Create a button
    button.setBorder(edge);                        // Add its border
    layout.setConstraints(button, constraints);    // Set the constraints
    aWindow.getContentPane().add(button);          // Add button to content pane
  }
}

The program window will look like that shown below:

As you see, the left button is slightly wider than the right button. This is because the length of the button label affects the size of the button.

How It Works

Because the process will be the same for every button added, we have implemented the helper function addButton(). This creates a Button object, associates the GridBagConstraints object with it in the GridBagLayout object, and then adds it to the content pane of the frame window.

After creating the layout manager and GridBagConstraints objects we set the values for weightx and weighty to 10.0. A value of 1.0 would have the same effect. We set the fill constraint to BOTH to make the component fill the space it occupies. Note that when the setConstraints() method is called to associate the GridBagConstraints object with the button object, a copy of the constraints object is stored in the layout – not the object we created. This allows us to change the constraints object and use it for the second button without affecting the constraints for the first.

The buttons are more or less equal in size in the x direction (they would be exactly the same size if the labels were the same length) because the weightx and weighty values are the same for both. Both buttons fill the space available to them because the fill constraint is set to BOTH. If fill was set to HORIZONTAL, for example, the buttons would be the full width of the grid positions they occupy, but just high enough to accommodate the label, since they would have no preferred size in the y direction.

If we alter the constraints for the second button to:

// Set constraints and add second button
constraints.weightx = 5.0;                       // Weight half of first
constraints.insets = new Insets(10, 30, 10, 20); // Left 30 & right 20
constraints.gridwidth = constraints.RELATIVE;    // Rest of the row
addButton("GO", constraints, gridbag);           // Add button to content pane

the application window will be as shown:

Now the second button occupies one third of the space in the x direction – that is a proportion of 5/(5+10) of the total – and the first button occupies two thirds. Note that the buttons still occupy one grid cell each – the default values for gridwidth and gridheight of 1 apply – but the weightx constraint values have altered the relative sizes of the cells for the two buttons in the x direction.

The second button is also within the space allocated – ten pixels at the top and bottom, thirty pixels on the left and twenty on the right (set with the insets constraint). You can see that for a given window size here, the size of a grid position depends on the number of objects. The more components there are, the less space they will each be allocated.

Suppose we wanted to add a third button, the same width as the Press button, and immediately below it. We could do that by adding the following code immediately after that for the second button:

// Set constraints and add third button 
constraints.insets = new Insets(0,0,0,0);       // No insets
constraints.gridx = 0;                          // Begin new row
constraints.gridwidth = 1;                      // Width as "Press"
addButton("Push", constraints, gridbag);        // Add button to content pane

We reset the gridx constraint to zero to put the button at the start of the next row. It has a default gridwidth of 1 cell, like the others. The window would now look like:

Having seen how it looks now, clearly it would be better if the GO button were the height of Press and Push combined. To arrange them like this, we need to make the height of the GO button twice that of the other two buttons. The height of the Press button is 1 by default, so by making the height of the GO button 2, and resetting the gridheight constraint of the Push button to 1, we should get the desired result. Modify the code for the second and third buttons to:

// Set constraints and add second button
constraints.weightx = 5.0;                       // Weight half of first
constraints.gridwidth = constraints.REMAINDER;   // Rest of the row
constraints.insets = new Insets(10, 30, 10, 20); // Left 30 & right 20
constraints.gridheight = 2;                      // Height 2x "Press"
addButton("GO", constraints, gridbag);           // Add button to content pane

// Set constraints and add third button
constraints.gridx = 0;                         // Begin new row   
constraints.gridwidth = 1;                     // Width as "Press"
constraints.gridheight = 1;                    // Height as "Press"
constraints.insets = new Insets(0, 0, 0, 0);   // No insets
addButton("Push", constraints, gridbag);       // Add button to content pane

With these code changes, the window will be:

We could also see the effect of padding the components out from their preferred size by altering the button constraints a little:

// Create constraints and add first button
constraints.weightx = constraints.weighty = 10.0;
constraints.fill = constraints.NONE;
constraints.ipadx = 30;                         // Pad 30 in x
constraints.ipady = 10;                         // Pad 10 in y
addButton("Press", constraints, gridbag);       // Add button to content pane

// Set constraints and add second button
constraints.weightx = 5.0;                       // Weight half of first
constraints.fill = constraints.BOTH;             // Expand to fill space
constraints.ipadx = constraints.ipady = 0;       // No padding
constraints.gridwidth = constraints.REMAINDER;   // Rest of the row
constraints.gridheight = 2;                      // Height 2x "Press"
constraints.insets = new Insets(10, 30, 10, 20); // Left 30 & right 20
addButton("GO", constraints, gridbag);           // Add button to content pane

// Set constraints and add third button
constraints.gridx = 0;                       // Begin new row 
constraints.fill = constraints.NONE;
constraints.ipadx = 30;                      // Pad component in x
constraints.ipady = 10;                      // Pad component in y
constraints.gridwidth = 1;                   // Width as "Press"
constraints.gridheight = 1;                  // Height as "Press"
constraints.insets = new Insets(0, 0, 0, 0); // No insets
addButton("Push", constraints, gridbag);     // Add button to content pane

With the constraints for the buttons as before, the window will look like:

Both the Push and the Press button occupy the same space in the container, but, because fill is set to NONE, they are not expanded to fill the space in either direction. The ipadx and ipady constraints specify by how much the buttons are to be expanded from their preferred size – by thirty pixels on the left and right, and ten pixels on the top and bottom. The overall arrangement remains the same.

You need to experiment with using GridBagLayout and GridBagConstraints to get a good feel for how the layout manager works because you are likely to find yourself using it quite often.

Using a SpringLayout Manager

You can set the layout manager for the content pane of a JFrame object, aWindow, to be a SpringLayout manager like this:

SpringLayout layout = new SpringLayout();            // Create a layout manager
Container content = aWindow.getContentPane();        // Get the content pane
content.setLayout(layout);

The layout manager defined by the SpringLayout class determines the position and size of each component in the container according to a set of constraints that are defined by Spring objects. Every component within a container using a SpringLayout manager has an object associated with it of type SpringLayout.constraints that can define constraints on the position of each of the four edges of the component. Before you can access the SpringLayout.constraints object for an object, you must first add the object to the container. For example:

JButton button = new JButton("Press Me");
content.add(button);

Now we can call the getConstraint() method for the SpringLayout object to obtain the object encapsulating the constraints:

SpringLayout.Constraints buttonConstr = layout.getConstraints(button);

To constrain the location and size of the button object, we will call methods of the buttonConstr object to set individual constraints.

Understanding Constraints

The top, bottom, left, and right edges of a component are referred to by their compass points, north, south, west, and east. When you need to refer to a particular edge in your code – for setting a constraint for instance, you use constants that are defined in the SpringLayout class, NORTH, SOUTH, WEST, and EAST respectively.

Click To expand

As the diagram shows, the position of a component is determined by a horizontal constraint on the x-coordinate of the component and a vertical constraint on the y-coordinate. These obviously also determine the location of the WEST and NORTH edges of the component, since the position determines where the top-left corner is located. The width and height are determined by horizontal constraints that relate the position of the EAST and SOUTH edges to the positions of the WEST and NORTH edges respectively. Thus the constraints on the positions of the EAST and SOUTH edges are determined by constraints that are derived from the others, as follows:

EAST-constraint = X-constraint + width-constraint

SOUTH-constraint = Y-constraint + height-constraint

You can set the X, Y, width, and height constraints independently as we shall see in a moment, and you can also set a constraint explicitly for any edge. If you set a constraint on the SOUTH or EAST edges of a component, the Y or X constraint will be adjusted if necessary to ensure the relationships above still hold.

Defining Constraints

The Spring class in the javax.swing package defines an object that represents a constraint. A Spring object is defined by three integer values that relate to the notional length of the spring: the minimum value, the preferred value, and the maximum value. A Spring object will also have an actual value that lies between the minimum and the maximum, and that will determine the location of the edge to which it applies. You can create a Spring object like this:

Spring spring = Spring.constant(10, 30, 50);    // min=10, pref=30, max=50

The static constant() method creates a Spring object from the three arguments that are the minimum, preferred, and maximum values for the object. If all three values are equal, the object is called a strut because its value is fixed at the common value you set for all three. There's an overloaded version of the constant() method for creating struts:

Spring strut = Spring.constant(40); // min, pref, and max all set to 40

The Spring class also defines static methods that operate on Spring objects:

sum(Spring spr1, Spring spr2)

Returns a reference to a new Spring object that has minimum, preferred, and maximum values that are the sum of the corresponding values of the arguments.

minus (Spring spr)

Returns a reference to a new Spring object with minimum, preferred, and maximum values that are the same magnitude as those of the argument but with opposite signs.

max(Spring spr1, Spring spr2)

Returns a reference to a new Spring object that has minimum, preferred, and maximum values that are the maximum of the corresponding values of the arguments.

Setting Constraints for a Component

The setX() and setY() methods for a SpringLayout.Constraints object set the constraints for the WEST and NORTH edges of the component respectively. For example:

Spring xSpring = Spring.constant(5,10,20);       // Spring we'll use for X
Spring ySpring = Spring.constant(3,5,8);         // Spring we'll use for Y
buttonConstr.setX(xSpring);                      // Set the WEST edge constraint
buttonConstr.setY(xSpring);                      // Set the NORTH edge constraint

The setX() method defines a constraint between the WEST edge of the container, and the WEST edge of the component. Similarly, the setY() method defines a constraint between the NORTH edge of the container and the NORTH edge of the component. This fixes the location of the component in relation to the origin of the container.

Click To expand

To set the width and height of the component, you call the setWidth() and setHeight() methods for its SpringLayout.Constraints object:

Spring wSpring = Spring.constant(30,50,70);    // Spring we'll use for width
Spring hSpring = Spring.constant(15);          // Strut we'll use for height
buttonConstr.setWidth(wSpring);                // Set component width constraint
buttonConstr.setHeight(hSpring);               // Set component height constraint

The width constraint is applied between the WEST and EAST edges and the height constraint applies between the component's NORTH and SOUTH edges. Since we have specified a strut for the height, there is no leeway on this constraint; its value is fixed at 15.

If you want to explicitly set an edge constraint for a component, you call the setConstraint() method for the component's SpringLayout.Constraints object:

layout.getConstraints(newButton)
      .setConstraint(StringLayout.EAST, Spring.sum(xSpring, wSpring));

This statement ties the EAST edge of the newButton component to the WEST edge of the container by a Spring object that is the sum of xSpring and wSpring.

You can also set constraints between pairs of vertical or horizontal edges where one edge can belong to a different component from the other. For instance, we could add another button to the container like this:

JButton newButton = new JButton("Push");
content.add(newButton);

We can now constrain its WEST and NORTH edges by tying the edges to the EAST and SOUTH edges of button. We use the putConstraint() method for the SpringLayout object to do this:

SpringLayout.Constraints newButtonConstr = layout.getConstraints(newButton);
layout.putConstraint(SpringLayout.WEST,
                     newButton,
                     xSpring,
                     SpringLayout.EAST,
                     button);    

The first two arguments to the putConstraint() method for the layout object are the edge specification and a reference to the dependent component respectively. The third argument is a Spring object defining the constraint. The fourth and fifth arguments specify the edge and a reference to the component to which the dependent component is anchored. Obviously, since constraints can only be horizontal or vertical, both edges should have the same orientation. There is an overloaded version of the putConstraint() method where the third argument is a value of type int that defines a fixed distance between the edges.

Let's look at a simple example using a SpringLayout object as the layout manager.

Try It Out – Using a SpringLayout Manager

Here's the code for an example that displays six buttons in a window.

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SpringLayout;
import javax.swing.Spring;

import java.awt.Container; 
import java.awt.Dimension; 
import java.awt.Toolkit;

public class TrySpringLayout {
  // The window object
  static JFrame aWindow = new JFrame("This is a Spring Layout"); 

  public static void main(String[] args) {
    Toolkit theKit = aWindow.getToolkit();         // Get the window toolkit
    Dimension wndSize = theKit.getScreenSize();    // Get screen size
 
    // Set the position to screen center & size to half screen size
    aWindow.setBounds(wndSize.width/4, wndSize.height/4,   // Position
                      wndSize.width/2, wndSize.height/2);  // Size
    aWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    SpringLayout layout = new SpringLayout();      // Create a layout manager
    Container content = aWindow.getContentPane();  // Get the content pane
    content.setLayout(layout);                     // Set the container layout mgr

    JButton[] buttons = new JButton[6];            // Array to store buttons
    for(int i = 0; i < buttons.length; i++) {
      buttons[i] = new JButton("Press " + (i+1));
      content.add(buttons[i]);      // Add a Button to content pane
    }

    Spring xSpring = Spring.constant(5,15,25);    // x constraint for 1st button
    Spring ySpring = Spring.constant(10,30, 50);  // y constraint for first button
    Spring wSpring = Spring.constant(30,80,130);  // Width constraint for buttons

    // Connect x,y for first button to left and top of container by springs
    SpringLayout.Constraints buttonConstr = layout.getConstraints(buttons[0]);
    buttonConstr.setX(xSpring); 
    buttonConstr.setY(ySpring); 

    // Set width and height of buttons and hook buttons together
    for(int i = 0 ; i< buttons.length ; i++) {
      buttonConstr = layout.getConstraints(buttons[i]);
      buttonConstr.setHeight(ySpring);      // Set the button height constraint 
      buttonConstr.setWidth(wSpring);       // and its width constraint 

      // For buttons after the first tie W and N edges to E and N of predecessor
      if(i>0) {                    
        layout.putConstraint(SpringLayout.WEST, buttons[i],
                             xSpring,SpringLayout.EAST, buttons[i-1]);    
        layout.putConstraint(SpringLayout.NORTH, buttons[i],
                             ySpring,SpringLayout.SOUTH, buttons[i-1]);
      }
    }
    aWindow.setVisible(true);                      // Display the window
  }
}

When you compile and run this you should get a window with the buttons laid out as shown below.

Click To expand

How It Works

After adding six buttons to the content pane of the window, we define two Spring objects that we will use to position the first button:

Spring xSpring = Spring.constant(5,15,25);    // x constraint for 1st button
Spring ySpring = Spring.constant(10,30, 50);  // y constraint for first button

We also define a spring we will use to determine the width of each button:

Spring wSpring = Spring.constant(30,80,130);  // Width constraint for buttons

We then set the location of the first button relative to the container:

// Connect x,y for first button to left and top of container by springs
SpringLayout.Constraints buttonConstr = layout.getConstraints(buttons[0]);
buttonConstr.setX(xSpring); 
buttonConstr.setY(ySpring);

This fixes the first button. We can define the positions of each the remaining buttons relative to its predecessor. We do this by adding constraints between the NORTH and WEST edges of each button and the SOUTH and EAST edges of its predecessor. This is done in the for loop after setting the width and height constraints for each button :

// Set width and height of buttons and hook buttons together
for(int i = 0 ; i< buttons.length ; i++) {
  buttonConstr = layout.getConstraints(buttons[i]);
  buttonConstr.setHeight(ySpring);      // Set the button height constraint 
  buttonConstr.setWidth(wSpring);       // and its width constraint 

  // For buttons after the first tie W and N edges to E and N of predecessor
  if(i>0) {                    
    layout.putConstraint(SpringLayout.WEST,buttons[i],
                         xSpring,SpringLayout.EAST, buttons[i-1]);    
    layout.putConstraint(SpringLayout.NORTH,buttons[i],
                         ySpring,SpringLayout.SOUTH, buttons[i-1]);
  } // end if
} // end for

This places each component after the first relative to the bottom right corner of its predecessor so the buttons are laid out in a cascade fashion.

Relating the Container Size to the Components

Of course, the size of the application window in our example is independent of the components within it. If you resize the window the springs have no effect. If you call pack() for the aWindow object before calling its setVisible() method, the window will shrink to a width and height just accommodating the title bar so you won't see any of the components. This is because SpringLayout does not adjust the size of the container by default so the effect of pack() is as though the content pane was empty.

We can do much better than this. We can set constraints on the edges of the container using springs that will control its size. We can therefore place constraints on the height and width of the container in terms of the springs that we used to determine the size and locations of the components. This will have the effect of relating all the springs that determine the size and position of the buttons to the size of the application window. Try adding the following code to the example immediately preceding the call to setVisible() for the window object:

SpringLayout.Constraints constr = layout.getConstraints(content);
constr.setConstraint(SpringLayout.EAST,
                     Spring.sum(buttonConstr.getConstraint(SpringLayout.EAST),
                                Spring.constant(15)));
constr.setConstraint(SpringLayout.SOUTH,
                     Spring.sum(buttonConstr.getConstraint(SpringLayout.SOUTH),
                                Spring.constant(10)));
aWindow.pack();

This sets the constraint on the EAST edge of a container that is the Spring constraining the EAST edge of the last button plus a strut 15 units long. This positions the right edge of the container 15 units to the right of the right edge of the last button. The bottom edge of the container is similarly connected by a fixed link, 10 units long, to the bottom edge of the last button. If you recompile with these additions and run the example again, you should find that not only is the initial size of the window set to accommodate all the buttons, but also when you resize the window the size and positions of the buttons adapt accordingly. Isn't that nice?

The SpringLayout manager is extremely flexible and can do much of what the other layout mangers can do if you choose the constraints on the components appropriately. It's well worth experimenting to see the effect of various configurations of springs on your application.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor


Самая свежая информация windows internet explorer windows xp на сайте.