13.3 Layout Managers

If a container has more than one component, the position and size of each of these components can be controlled by a layout manager. Swing has a number of different layout managers and even allows you to create your own if you want to do so. Here, however, we will examine only three.

Before we start to examine layout managers, we need something to arrange. An object that is simple to create and display is a button, a region of the screen that can be "pressed" by clicking on a mouse. Java has more than one class of button; our buttons are going to be objects constructed from the JButton class in the package javax.swing. In this section, we will only be creating and arranging buttons. We defer our discussion of how to use them until the next section.

FlowLayout

The objects of Java's simplest layout manager are constructed from the class FlowLayout in the package java.awt. With FlowLayout, components are arranged rather like words are arranged by a word processor using a centred alignment - from left to right, and from top to bottom, with each row centred between the left and right sides of the container. To construct a FlowLayout in a JFrame object called frame, we could write

frame.setLayout(new FlowLayout());

Once we have a manager for a JFrame we can add components using the instance method add of the class JFrame. For example, to add a button with the label "Help" to the JFrame object called frame, we could first construct a new button by writing

JButton helpButton = new JButton("Help"); and then add it to our panel by writing

frame.add(helpButton);

In this section, because we are not doing anything with our buttons, we have no need for button identifiers. We will, therefore, abbreviate the code for creating and adding a button to something like

frame.add(new JButton("Help"));

Example 1

This program uses a FlowLayout manager to create and arrange five buttons in a window

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

public class FlowDemo
{
   public FlowDemo()
   {
      JFrame frame = new JFrame("Flow Layout");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(400,100);
      frame.setLayout(new FlowLayout());
      frame.add(new JButton("A"));
      frame.add(new JButton("B"));
      frame.add(new JButton("C"));
      frame.add(new JButton("D"));
      frame.add(new JButton("E"));
      frame.setVisible(true);
   }
   public static void main(String[] args)
   {
      new FlowDemo();
   }
} 

The output of the program is shown in the next illustration. Any button that we create has a “preferred” size — enough space for the label plus a border. A FlowLayout manager uses this information to size and position the buttons. Here the buttons are arranged in a single row, centred at the top of the window. The order in which we added the buttons determines the order in which they appear in the window.

If we resize the window to make it narrower, the positions of the buttons are changed automatically, as shown here.

BorderLayout

The second layout manager that we will examine is BorderLayout. Here, the panel is divided into five regions identified by the constants NORTH, SOUTH, EAST, WEST, and CENTER in the BorderLayout class. To add a component, we use a version of the overloaded method add that has two parameters: the component to be added and its position. NORTH and SOUTH occupy the entire top and bottom of the area, EAST and WEST occupy the remaining right and left sides, and CENTER takes whatever is left in the interior.

(Note: BorderLayout.NORTH = "North", BorderLayout.SOUTH = "South" and so on. So many programmers use those literals instead of the named constants. The advantage of the named constants is that the compiler will flag an error is you type it incorrectly. If you use the literals you won't find out until the program tries to run. Its usually better to find the error when you are compiling rather than running a program. It is still tempting to use the convenience of the shorter to type literals)

Example 2

This program creates five buttons and places one in each of the five regions of a BorderLayout.

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

public class BorderDemo
{
   public BorderDemo()
   {
      JFrame frame = new JFrame("Border Layout");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(300,200);
      frame.add(new JButton("Center"),BorderLayout.CENTER);
      frame.add(new JButton("West"),BorderLayout.WEST);
      frame.add(new JButton("East"),BorderLayout.EAST);
      frame.add(new JButton("North"),BorderLayout.NORTH);
      frame.add(new JButton("South"),BorderLayout.SOUTH);
      frame.setVisible(true);
   }
   public static void main(String[] args)
   {
      new BorderDemo();
   }
} 

In the program, the layout style of theframe is set by the statement

frame.setLayout(new BorderLayout());

The statement is not actually needed because BorderLayout is the default layout manager for a JFrame but inserting it does no harm and may serve as a useful reminder to readers of the program.

The resulting window is shown in the following diagram. Notice that the buttons, rather than being displayed at their preferred sizes, are sized to fill each region. If we were to resize the window, the regions (and the buttons) would expand or contract as required to keep the window filled. If the window is made too small, then one or more of the regions may not be visible but they will not be destroyed. If no component is added to one of the perimeter regions, the other regions will expand to occupy that space.

GridLayout

The last layout manager that we will be looking at is called GridLayout. With this manager, we specify the number of rows and columns in which the components are to be placed. The components are all the same size and their dimensions are set so that the full width and height of the container are utilized.

The constructor for the GridLayout has a parameter list of the form (int row, int col). Normally, Java only pays attention to the row specification, creating as many columns as necessary to fit the components into the region. If the value of row is greater than the number of components, blank space is left at the bottom of the region. If the value of row is zero, the layout manager pays attention to the value of col and creates a grid layout with the specified number of columns and the necessary number of rows (if there are enough components for the specified dimension).

Example 3

This program displays five buttons in a grid containing three rows and two columns.

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

public class GridDemo
{
   public GridDemo()
   {
      JFrame frame = new JFrame("Grid Layout");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(200,100);
      frame.setLayout(new GridLayout(3,2));
      frame.add(new JButton("A"));
      frame.add(new JButton("B"));
      frame.add(new JButton("C"));
      frame.add(new JButton("D"));
      frame.add(new JButton("E"));
      frame.setVisible(true);
   }
   public static void main(String[] args)
   {
      new GridDemo();
   }
} 

Since there are five components and we have specified that the grid should contain three rows, the resulting window contains two columns, only the first of which is entirely filled. The fact that we specified two columns in the GridLayout constructor is irrelevant. If we had used (3,0) or (3,10) rather than (3,2), we would have produced the same display. Notice the order in which the components have been placed in the window — row by row (sometimes called row major order).

Java has other layout managers but, even with the ones that we have discussed, complex layouts can be produced because we can nest layouts of any type within other layouts of any type.

Example 4

The following program illustrates a mixture of all three types of layouts that we have examined. In it, we have embedded both a BorderLayout and a FlowLayout within a GridLayout.

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

public class ComboDemo
{
   public ComboDemo()
   {
      JFrame frame = new JFrame("Combo Layout");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(500,200);
      frame.setLayout(new GridLayout(2,2));
      frame.add(new JButton("A"));
      JPanel borderPane = new JPanel();
      borderPane.setLayout(new BorderLayout());
      borderPane.add(new JButton("B-North"),BorderLayout.NORTH);
      borderPane.add(new JButton("B-Center"),BorderLayout.CENTER);
      borderPane.add(new JButton("B-West"),BorderLayout.WEST);
      borderPane.add(new JButton("B-East"),BorderLayout.EAST);
      borderPane.add(new JButton("B-South"),BorderLayout.SOUTH);
      frame.add(borderPane);
      JPanel flowPane = new JPanel();
      flowPane.setLayout(new FlowLayout());
      flowPane.add(new JButton("C-1"));
      flowPane.add(new JButton("C-2"));
      flowPane.add(new JButton("C-3"));
      flowPane.add(new JButton("C-4"));
      flowPane.add(new JButton("C-5"));
      flowPane.add(new JButton("C-6"));
      flowPane.add(new JButton("C-7"));
      frame.add(flowPane);
      frame.add(new JButton("D"));
      frame.setVisible(true);
   }
   public static void main(String[] args)
   {
      new ComboDemo();
   }
} 

Here is what it produces.

Of course, layout managers can place components other than buttons on the screen. The next example combines buttons with a graphical display. It also shows how we can control the positioning of items relative to the current size of the window.

Example 5

The following program displays a solid blue ellipse centred in a region between two buttons.

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

public class BorderDemo
{
   public BorderDemo()
   {
      JFrame frame = new JFrame("Graphics & Buttons");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(200,200);
      frame.add(new Drawing(),BorderLayout.CENTER);
      frame.add(new JButton("Start"),BorderLayout.NORTH);
      frame.add(new JButton("Stop"),BorderLayout.SOUTH);
      frame.setVisible(true);
   }

   class Drawing extends JComponent
   {
      public void paint (Graphics g)
      {
         g.setColor(Color.blue);
         g.fillOval(getWidth()/4,getHeight()/4,getWidth()/2,getHeight()/2);
      }
   }
   
   public static void main(String[] args)
   {
      new BorderDemo();
   }
} 

Here is the resulting image.

Example 5 also illustrates a new way of controlling the appearance of an image in a region. The positioning and dimensions of the ellipse are controlled by the methods getWidth and getHeight in the class Component. The ellipse is centred both horizontally and vertically, with its containing rectangle being half the width and height of the region in which it is displayed. Whenever a user resizes the window, the paint method is called automatically and the ellipse is redrawn, repositioned and resized appropriately.

Exercise 13.3

  1. Suppose that a grid layout is to be used to display fourteen buttons. How many rows and columns would be displayed in each case if the call to the grid layout constructor had the form shown?

    1. new GridLayout(3,5)
    2. new GridLayout(5,4)
    3. new GridLayout(4,0)
    4. new GridLayout(0,6)
  2. Write a program that will produce the following image in a window that is 140 pixels wide and 70 pixels high.

  3. Write a program that will produce the following display in a square window whose sides are 200 pixels in length.

  4. Write a program that will display two solid red circles in a window. The diameter of each circle should be one quarter of the smaller of the width and height of the region. One circle should be centred in the upper left quadrant while the other should be centred in the lower right quadrant. Resizing the window should cause the circles to resize appropriately.