Chapter 8

All About GridBaglayout and Other Layout managers


CONTENTS


The AWT library includes classes for automated layout management of windows. The layout manager classes are a set of classes that lay out widgets on forms and windows. Moreover, these classes recompute the layout when the user resizes a window. You might be asking yourself why an automated layout manager is needed. Why not just use a layout editor to position widgets on the window? The answer is that the layout manager not only helps you create an initial layout. If you are developing a Java program where the user must be allowed to resize windows, the layout manager classes can minimize your work, because you don't have to write your own code for recomputing the layout after resize events. For static, nonresizeable windows, you might be better off using a layout editor (such as a layout editor provided by a Java development environment).

The most powerful layout manager is the GridBagLayout class. This manager provides advanced functions for specifying widget resizing and repositioning as the window size changes. Before you explore the GridBagLayout class and other layout manager classes, let's first examine the concept of automated layout.

Automated Layout and the AWT Layout Manager

Although you can use static layouts defined with, for instance, layout editors for windows of fixed size, there are still reasons for taking the layout manager approach. Because Java is designed to be platform-independent, it can be difficult to find a layout that is acceptable on every platform. Components look somewhat different on each platform because the AWT uses native widgets in the window system of the platform. Layout managers can assist you in creating well-designed user interfaces on every platform by recomputing the layout dynamically.

Selecting an appropriate layout manager is sometimes difficult. The AWT includes a set of standard layout managers, which you can configure to a certain extent. The best strategy often is to use the simplest layout manager that is sufficient for your layout task.

To take advantage of a layout manager, you should instantiate a layout manager class, such as FlowLayout, GridBagLayout, and so on. The next step is to associate it with the container on which it should operate. The method setLayout(LayoutManager) sets the layout manager for a container. For some layout managers, you can specify the layout strategy by providing parameters to the constructor of the layout manager or by setting parameters in the layout manager.

Once you have set the layout manager for a container, the latter will invoke the layout manager just before the components are drawn. Resizing windows and adding components to the container will cause the layout manager to recompute the layout. If one of the basic layout classes in AWT is sufficient, you may want to use it instead of GridBagLayout. If you need a more sophisticated layout than what is provided by a single layout manager, it is possible to use a combination of several layout managers, where components of an overall layout are themselves containers with their own layout managers.

Basic Layout Classes

In addition to GridBagLayout, the AWT includes four basic layout managers: FlowLayout, BorderLayout, GridLayout, and CardLayout. These layout managers are simpler to use, but less powerful than GridBagLayout.

FlowLayout

The FlowLayout class is a straightforward layout manager that lays out components linewise from left to right. When a line of components is filled, FlowLayout creates a new line and continues laying out components on the next line. The layout strategy is similar to the way a word processor wraps text on a page. Use the FlowLayout class in situations where it is important to line up components horizontally and where you want the layout manager to wrap the lines for you. Figure 8.1 shows a sample layout produced by FlowLayout.

Figure 8.1 : Sample FlowLayout.

The constructors for FlowLayout allow several layout options. You can control the alignment of components by specifying an alignment code (which is defined as a constant in the FlowLayout class). The possible alignments are FlowLayout.CENTER, FlowLayout.LEFT, and FlowLayout.RIGHT. You also can specify the horizontal and vertical gaps between components. Use the constructor FlowLayout() to create a FlowLayout with a centered alignment. The constructor FlowLayout(int align) creates a FlowLayout with the specified alignment, and the constructor FlowLayout(int align, int hgap, int vgap) creates a FlowLayout with the specified alignment, horizontal gap, and vertical gap, respectively.

BorderLayout

The BorderLayout class enables you to specify where on the border of a container each component should be placed. By naming the component members North, South, West, East, and Center, you can control the location of the components. Specify the component names with the add() method when you add components to the container. The BorderLayout class lays out the North, South, West, and East components using their preferred sizes. BorderLayout resizes the Center component to fill the remaining center space. Use BorderLayout when you need to group components on the borders of a container, such as positioning scrollbars on the bottom and right side of a container. Figure 8.2 shows a sample layout generated by BorderLayout.

Figure 8.2 : Sample Borderlayout.

There are two constructors for BorderLayout: BorderLayout() and BorderLayout(int hgap, int vgap). The first constructor creates a basic BorderLayout, and the second creates a BorderLayout with horizontal and vertical gaps.

GridLayout

The GridLayout class lays out components as a matrix according to a grid. GridLayout places a component on each position in the grid. The order in which you add components to the container is important, because GridLayout fills the grid from left to right and from top to bottom. Use the GridLayout when you need a matrix-like layout, such as a matrix of TextFields. Figure 8.3 shows a sample GridLayout.

Figure 8.3 : Sample GridLayout.

GridLayout has the constructors GridLayout(int rows, int cols) and GridLayout(int rows, int cols, int hgap, int vgap). The first creates a GridLayout with the specified number of rows and columns. The second enables you also to specify the horizontal and vertical gaps. Specifying the number of columns is important, because GridLayout uses this information when placing components on the grid.

CardLayout

The CardLayout class enables you to define a set of alternative cards that are displayed in the container. Each card is typically a container that can include several components. Unlike other layout mangers, CardLayout does not lay out components geometrically. It shows and hides the appropriate components but does not change the location of them. Use the CardLayout class when you need to display alternative sets of components on a panel, such as when you are implementing slide-show applets and preference panels with several alternative forms.

Because CardLayout shows and hides containers, you must inform the layout manager when you want to change the current card. For this purpose, CardLayout provides several methods for controlling the cards programmatically. Table 8.1 shows the control methods that CardLayout supports. You can create a panel of buttons that control the panel by calling these methods.

Table 8.1. CardLayout methods.
Layout Manager MethodDescription Parameters
first(Container) Show the first cardThe parent container
last(Container) Show the last cardThe parent container
next(Container) Show the next cardThe parent container
previous(Container) Show the previous cardThe parent container
show(Container, String) Show a named cardThe parent container and the name of the card

A Layout Manager Example

Let's consider an example of how we can use basic layout managers. By combining several layout managers, we can achieve quite complex layouts. Figure 8.4 shows a sample window layout, which is produced by a combination of four layout managers.

Figure 8.4 : A sample layout created by a combination of layout managers.

Here is the code that creates this window:

import java.awt.*;
import java.util.*;
import java.applet.Applet;
public class ComboEx extends Applet {

    public void init() {
        /* Use BorderLayout as the overall layout */
        setLayout(new BorderLayout());

        /* Add the table of name, e-mail, and URL */
        Panel t = new Panel();
        t.setLayout(new GridLayout(4,3));
        // Add column headers...
        t.add(new Label("Name"));
        t.add(new Label("E-mail"));
        t.add(new Label("URL"));
        // Add nine text fields...
        for (int i = 1; i <= 9; i++) t.add(new TextField());
        add("Center",t);

        /* Add the ranking numbers to the left */
        Panel r = new Panel();
        r.setLayout(new GridLayout(4,1));
        r.add(new Label("No."));
        r.add(new Label("1"));
        r.add(new Label("2"));
        r.add(new Label("3"));
        add("West",r);

        /* Add control buttons at the bottom */
        Panel control = new Panel();
        control.setLayout(new FlowLayout());
        control.add(new Button(" OK "));
        control.add(new Button("Cancel"));
        control.add(new Button("Revert"));
        add("South", control);
    }

    public static void main(String args[]) {
      Frame f = new Frame("Layout Combination Example");
      ComboEx ce = new ComboEx();
      ce.init();
      f.add("Center", ce)
      f.pack();
      f.resize(f.preferredSize());
      f.show();
    }
}

Note that BorderLayout controls the overall layout of the window. The left position of this layout is a GridLayout with the "No." label and the numbers 1-3. The center position is a second GridLayout with the column labels and nine text-entry fields. Finally, the south position is a FlowLayout with the control buttons for the "OK", "Cancel", and "Revert" operations.

The GridBagLayout Class

The GridBagLayout class is a powerful layout manager that lays out components based on a grid. You can think of GridBagLayout as an advanced version of GridLayout. The major difference between GridLayout and GridBagLayout is that GrigBagLayout supports components of different sizes, and you can specify layout options for each component. Use the GridBagLayout when you need tabular layouts (or layouts that can be thought of as matrices) and when it is important to specify the resizing behavior of each component.

Basic Concepts

GridBagLayout supports a rectangular grid of cells. Each component of a GridBagLayout can occupy one or more cells. Because GridBagLayout enables you to specify layout properties for each component, you must associate components managed by GridBagLayout with instances of the class GridBagConstraints. These instances specify how GridBagLayout should lay out components in the matrix. Let's examine how you can set up a GridBagLayout. The constructor GridBagLayout() creates a GridBagLayout instance. You can then use the method setConstraints(Component, GridBagConstraints) to associate components with constraints. In addition to the setConstraints(Component, GridBagConstraints) method, GridBagLayout provides a set of methods to manage constraints. Table 8.2 shows the constraint management methods.

Table 8.2. Constraint management methods for GridBagLayout.
Layout Manager MethodDescription Parameters
setConstraints(Component, GridBagConstraints) Associate constraints with a component The component andthe constraints
getConstraints(Component) Get the constraints for a component (a copy of the GridBagConstraints instance is returned) The component to get constraints from
lookupConstraints(Component) Get the constraints for a component (the actual GridBagConstraints instance is returned) The component to get constraints from

Typically, you set up instances of the GridBagConstraints class before associating them with the components using the setConstraints(Component, GridBagConstraints) method.

GridBagConstraints

The GridBagConstraints class enables you to specify constraints for each component of the container. The GridBagConstraints class provides several options for specifying the behavior of member components. You specify the constraints by setting instance variables of the GridBagConstraints object. GridBagConstraints has three major variable categories that execute the following:

The variables gridx and gridy control the component position on the grid (the cell in which the component is placed). The variables gridwidth and gridheight determine the component size in terms of grid cells. The variables fill and anchor control the position of a component within its display area. The variables ipadx, ipady, and insets specify the padding. Finally, the variables weightx and weighty control the distribution of space among cells. Here is a detailed description of each variable:

gridx, gridy

Use these variables to specify explicitly where on the grid the layout manager should place the component. The upper-left cell is the origin, which has the location gridx = 0, gridy = 0. The default value is GridBagConstraints.RELATIVE, which specifies that the component should be placed at the next location relative to the last component added to the container. (In this case, the next location is just to the right, or just below, the previous component.)

gridwidth, gridheight

Use gridwidth and gridheight to specify the size of the components display area. You specify this size in number of grid cells. For example, the values gridwidth = 2 and gridheight = 1 mean that the component display area is two cells wide and one cell high in the grid. The default value of gridwidth and gridheight is 1. To specify that a component is the last one in its row or column, you can set gridwidth and gridheight to GridBagConstraints.REMAINDER. You can use the value GridBagConstraints.RELATIVE to indicate that the component is next to the last one in the row or column.

fill

Use fill to specify how GridBagLayout should resize components when the display area is larger than the component. Set fill to GridBagConstraints.HORIZONTAL to make the component sufficiently wide to fill its display area (without changing the component height). Set fill to GridBagConstraints.VERTICAL to make the component sufficiently tall to fill its display area (without changing the component width). Set fill to GridBagConstraints.BOTH to make the component fill the display area completely. Thus, the value GridBagConstraints.BOTH is a combination of GridBagConstraints.HORIZONTAL and GridBagConstraints.VERTICAL. The default value of fill is GridBagConstraints.NONE, which specifies no fill for the component.

anchor

Use anchor to specify where a component should be placed if it is smaller than the display area. GridBagLayout attaches the component to the specified location. The following are the possible values:

   GridBagConstraints.CENTER (default)
   GridBagConstraints.NORTH
   GridBagConstraints.NORTHEAST
   GridBagConstraints.EAST
   GridBagConstraints.SOUTHEAST
   GridBagConstraints.SOUTH
   GridBagConstraints.SOUTHWEST
   GridBagConstraints.WEST
   GridBagConstraints.NORTHWEST

ipadx, ipady

Use ipadx and ipady to enlarge the minimum size of components. GridBagLayout adds ipadx pixels to the left and right of the minimum size of the component. Similarly, GridBagLayout adds ipady pixels to the bottom and top of the minimum size of the component. Thus, GridBagLayout increases the minimum width and height by ipadx*2 and ipady*2 pixels, respectively.

Insets

Use Insets to specify the minimum border between the component and its display area. The value must be an instance of the class Insets. You can use the constructor Insets(int, int, int, int) to create an Insets instance with top, left, bottom, and right insets. GridBagLayout then inserts the specified space between the edges of the component and its display area.

weightx, weighty

Use weightx and weighty to specify how GridBagLayout should distribute space. You can use numeric values for weightx and weighty to distribute space among columns (weightx) and rows (weighty). These weights determine how much extra space a row (or column) will get when the container expands. By setting the weightx and weighty values, you control how rows and columns scale. Rows (columns) with larger weights will grow faster than rows (columns) with smaller weights. Typically, weightx and weighty have values between 0.0 and 1.0. The default weight is zero (0.0), which means no growth. When all weights are zero, GridBagLayout places the components together at the center of the container. Thus, GridBagLayout puts space between the grid and the edges of the container. Note that the actual weight for each row (column) is a combination of the weights of each of the components in the row (column).

You may find the task of setting up these variables difficult. If you start modifying the values without a clear idea of how they affect the layout, you may find it difficult to get the layout and resizing behavior you want. The key to successful layout creation is to plan ahead and to design the layout before specifying it.

Make a mock-up on paper or draw it using a drawing program. Once you are satisfied with the mock-up design, you can proceed with creating a grid on top of the layout. Use this grid as the basis for assigning components to cells and adding components to correct cell positions. Determine how you want each component to behave inside its display area. Do you want the component to fill the area horizontally, vertically, or both? Do you want the component to attach to a certain side or corner of the area? Do you want to enlarge the size of components or to add space between components and the edges of their display areas? When you have answered these questions, you can determine the correct values for the GridBagConstraints variables. After you implement the initial version of your layout specification, you can then redesign your layout incrementally by modifying the variable values.

A GridBagLayout Example

As you learned in the previous section, taking advantage of GridBagLayout and GridBagConstraints is a matter of setting appropriate values for variables. By studying layout examples, you can learn more about how to set the layout variables correctly. Because comprehensive examples of the use of GridBagLayout often get confusing, let's look at a minimal layout for a window with two buttons. Once you understand how the variables in GridBagConstraints work, you can easily use this knowledge to create containers with many components.

You will first examine the source code and the resulting window and then learn how you can modify different GridBagConstraints variables to get alternative layouts and resizing behavior. Here is the code for the MinimalGridBag class:

import java.awt.*;
import java.util.*;
import java.applet.Applet;
public class MinimalGridBag extends Applet {
    protected void makebutton(String name,
                              GridBagLayout gridbag,
                              GridBagConstraints c) {
        Button button = new Button(name);
        gridbag.setConstraints(button, c);
        add(button);
    }
    public void init() {
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        setLayout(gridbag);
        c.weightx = 1.0;
        c.weighty = 1.0;

        makebutton("Button 1", gridbag, c);

        c.fill = GridBagConstraints.BOTH;
        makebutton("Button 2", gridbag, c);

    }
    public static void main(String args[]) {
     Frame f = new Frame("Minimal GridBag Layout Example");
     MinimalGridBag mgb = new MinimalGridBag();
     mgb.init();
     f.add("Center", mgb);
     f.pack();
     f.resize(f.preferredSize());
     f.show();
    }
}

Figure 8.5 shows the resulting window from the MinimalGridBag example. Initially, GridBagLayout sizes the container (window) to accommodate buttons 1 and 2. The buttons line up horizontally in a 2-by-1 grid.

Figure 8.5 : The layout generated by the MinimalGridBag example (before resizing by the user).

When the user enlarges the window, GridBagLayout regenerates the layout based on the GridBagConstraints specification. Figure 8.6 shows the enlarged window. The size of button 1 remains the same because the fill is GridBagConstraints.NONE (the default value). However, GridBagLayout expands button 2 to fill its display area, because fill is set to GridBagConstraints.BOTH. Note that weightx and weighty are set to 1.0 in this example. It is necessary to set them to a nonzero value to enable the grid cells to grow.

Figure 8.6 : The layout after the user has enlarged the window.

As this example illustrates, it is easy to set up a minimal layout that uses GridBagLayout. The best way to learn more about how the GridBagConstraints variables work is to modify this example yourself and play with different settings. Because you may not currently have access to a computer running Java or have the time to perform these experiments, this chapter presents some of the possible modifications and their result.

Let's consider what happens to the layout if you change some of the variable values (by modifying the code, for example). In the remainder of this example, you make some controlled experiments with the variable values where you start with the previous code and vary the value of only one or two variables simultaneously. Figure 8.7 shows what happens if you change fill to GridBagConstraints.HORIZONTAL for button 2 and rerun the example. Button 2 now expands horizontally. (The original version used the value GridBagConstraints.BOTH, which makes the button 2 fill both horizontally and vertically.) Likewise, you can set fill to GridBagConstraints.VERTICAL for button 2. Figure 8.8 shows what happens. You can use different settings for fill to make components, such as lists of items and text fields, expand to accommodate more information as the user enlarges the window.

Figure 8.7 : Enlarged window with fill set to GridBagConstraints. HORIZONTAL for button 2.

Figure 8.8 : Enlarged window with fill set to GridBagConstraints. VERTICAL for button 2.

The anchor variable controls where in the display area a component should be placed. Because the default value for anchor is GridBagConstraints.CENTER, GridBagLayout centers 1 and 2 in the previous examples. Figure 8.9 shows what happens if you set anchor to GridBagConstraints.WEST for button 1 and enlarge the window. (Note that you start from the original example where fill is GridBagConstraints.BOTH.) If you want, you can try other values for anchor to place button 1 on other sides and in one of the corners.

Figure 8.9 : Enlarged window with anchor set to GridBagConstraints.WEST for Button 1.

Understanding how weightx and weighty work can sometimes be difficult, especially if you start with a complex layout. However, it is much easier if you consider a small example. Basically, the variables weightx and weighty control how cells in the grid scale when the container is resized. By using different values for weightx for the buttons in the example, you can control how the display areas scale when you resize the window.

Until now, you set weightx to 1.0 for buttons 1 and 2 to ensure that the display areas will scale. (You also set weighty to 1.0 to ensure vertical scaling.) Figure 8.10 shows what happens if you set weightx to 0.8 for button 1 and 0.2 for button 2 and then enlarge the window. Note that, because button 1 has more weight than button 2, the area for button 1 scales more rapidly than the area for button 2. However, the scaling for cells in grids with multiple rows is more complex than this example shows, because the weight for a column is calculated from the weight of all the cells in the column.

Figure 8.10 : Enlarged window with weightx set to 0.8 for button 1 and 0.2 for button 2.

GridBagConstraints provides variables for adding to the size of components and to add padding space around components. Let's examine what happens if you change these variables. The variables ipadx and ipady specify how much GridBagLayout should add to the size of a component. Figure 8.11 shows the result of adding internal padding to buttons 1 and 2 by setting ipadx and ipady to 50. GridBagLayout expands the cell size of the grid to accommodate the buttons. Figure 8.12 shows the result of enlarging the window in Figure 8.11. Button 1 keeps its size, and button 2 fills its display area.

Figure 8.11 : Layout with ipadx set to 50 pixels for buttons 1 and 2 (before enlargement of the window by the user).

Figure 8.12 : Enlarged window with ipadx set to 50 pixels for buttons 1 and 2.

In addition to adding to the size of widgets, you can instruct GridBagLayout to add external padding to the components. GridBagLayout will then maintain a minimum amount of space around the component. Figure 8.13 shows the result of setting insets to new Insets(20,20,20,20). GridBagLayout inserts 20 pixels of space between the component and its display area. In this case, GridBagLayout expands the cell size to accommodate the padded components. When the user enlarges the window, GridBagLayout maintains the padding space when adjusting the component sizes. Figure 8.14 shows the layout after enlargement of the window.

Figure 8.13 : Layout with insets set to 20 pixels on a each side of buttons 1 and 2 (before enlargement of the window by the user).

Figure 8.14 : Enlarged window with insets set to 20 pixels on each side of buttons 1 and 2.

Once you learn how to use the variables of GridBagConstraints, creating larger layouts is straightforward. To succeed in specifying larger layouts, however, you must carefully plan and design the layout before you begin assigning values to variables of GridBagConstraints.

Creating Your Own Layout Manager

In certain situations, you may want to create your own layout manager. Fortunately, the AWT package enables programmers to implement new layout managers. For instance, if none of the available standard layout manager classes provide the functionality you need, you can develop new layout managers that perform the required task. Because the GridBagLayout class is complex and somewhat difficult to use, you might have some ideas for simple, yet powerful, layout managers that work better for your layout job. You and others can then reuse these layout managers in several applets and applications.

There are two basic strategies for creating a layout manager. The first strategy is to subclass a preexisting layout manager. Here, your subclass implements the required functionality by modifying the behavior of the basic layout manager class. The second strategy is to create a new layout manager from scratch. In this approach, you develop your layout manager by creating a class that implements the LayoutManager interface.

The advantage of subclassing a standard layout manager class is that you can take advantage of the methods defined in the standard layout manager by inheriting them. However, the design of these standard classes is not very "open." It is difficult to reuse code in layout managers because a monolithic method, layoutContainer(Container), is responsible for performing the layout calculations. Basically, you have to rewrite this method for each layout manager you develop by subclassing standard layout managers.

Given the difficulties of modifying the behavior of layout classes by subclassing them, you might as well develop a new layout manager class that implements the LayoutManager interface. Using this strategy, you can even create your own hierarchy of layout manager classes, which inherit properties among each other.

The LayoutManager interface specifies methods for adding named components to the layout, removing components from the layout, calculating minimum and preferred layout sizes, and computing the layout. Table 8.3 describes the methods in the LayoutManager interface. Note that the LayoutManager is an interface with abstract methods; therefore, you must define them in your layout manager.

Table 8.3. LayoutManager methods.
LayoutManager MethodDescription Parameters
addLayoutComponent
(String, Component)
Adds a new component to the layoutA string describing the component name and the component
layoutContainer
(Container)
Lays out a containerThe container to lay out
minimumLayoutSize
(Container)
Calculates the minimum sizeThe container in question
preferredLayoutSize
(Container)
Calculates the preferred sizeThe container in question
removeLayoutComponent
(Component)
Removes a component from the layoutThe component to remove

Here's an example of how you can create a layout manager that lays out components diagonally:

class DiagonalLayout extends Object implements LayoutManager {

  public void addLayoutComponent(String name, Component comp) {
  }

  public void removeLayoutComponent(Component comp) {
  }

  public Dimension preferredLayoutSize(Container parent) {
    int l = parent.countComponents();
    Rectangle r = parent.getComponent(l-1).bounds();
    return new Dimension(r.x + r.width, r.y + r.height);
  }

  public Dimension minimumLayoutSize(Container parent) {
    return preferredLayoutSize(parent);
  }

  public void layoutContainer(Container parent) {
    int l = parent.countComponents();
    for (int i = 0; i < l; i++) {
      Component c = parent.getComponent(i);
      c.move(50*i,50*i);
    }
  }

}

In this layout manager, the layoutContainer method iterates over the components and moves each component to the appropriate location (which is determined by the component index). The layout manager calculates the preferred size by getting the lower-right corner of the last component (which is the same as the size). Figure 8.15 shows the resulting layout for a container with seven buttons. Although this layout manager does not change the layout dynamically as the user resizes the window, you can easily modify it to do so.

Figure 8.15 : Layout generated by DiagonalLayout.

As you have seen, implementing a new layout manager is also a straightforward task. What is more difficult, however, is to design a layout manager that is both powerful and easy to use. The advantage of the AWT design is that once you have developed an appropriate layout manager, it is easy to reuse it for many situations.

Summary

Layout managers automate the layout task by calculating window layouts dynamically. The AWT provides predefined managers that you can configure to get the window resizing behavior you want. For simple layout tasks, the FlowLayout, BorderLayout, GridLayout, and CardLayout managers work best. For advanced layout tasks, the powerful and general GridBagLayout manager is better than the basic layout managers. If these layout managers are insufficient for your task, the AWT enables you to define new layout managers.