Views: Layout Managers

Advanced Programming/Practicum
15-200


Introduction In this lecture we will examine four different layout managers; each manages the layout (relative sizes and positions) of any components added into the view of a GUI application (every layout manager is associated with one JPanel). The four clases are FlowLayout (the simplest of the four, but not used much in fancier GUIs), BoxLayout (for stacking components horizontally or vertically), GridLayout (for making 2-d grids of equal-sized component -often buttons), and BorderLayout (the default for the content pane of a JFrame, with positioning in the center, and the four compass directions).

We will also discuss how to create a variety of control components, change their visual attributes, and place them in the view. We will not yet explore how to associate action with these components: we will use these components in views for their visual properties only.

In addition, we will explore layout managers in more detail by discussing how to write our own simple layout managers. Finally, we will examine the use of structuring nested JPanels, each with layout managers, to build a hierarchical view of arbitrary complexity.

There is a Layout Manager Demos that we can use to explore all of these concepts. We can also easily change the code it contains to experiment with other layout manager combinations. Please download and unzip this file.


Layout Managers Layout managers all implement the following simple interface
  public interface LayoutManager {
    Dimension minimumLayoutSize     (Container parent);
    Dimension preferredLayoutSize   (Container parent);
    void      layoutContainer       (Container parent);
    void      addLayoutComponent    (String name, Component comp);
    void      removeLayoutComponent (Component comp);
  }
Recall that a Container object can hold lots of Component objects, each of which is displayable; its layout manager (set via the setLayoutManager method in the Container class) controls the overall display size of the container (via minimumLayoutSize and preferredLayoutSize) and where each of its components are displayed (via layoutContainer). We typically use a JPanel for a Container, inheriting the neccessary methods. Its layout manager can examine all its components, via the getComponents method in the Container class. It determine how they fit together, via the layoutContainer method; this method assigns a visually pleasing 2-d location to each component, via the setBounds method defined in the Component class.

In fact, some more advanced layout manager implement the following subinterface, which adds the following methods.

  public interface LayoutManager2 extends LayoutManager {
    void      addLayoutComponent  (Component comp, Object constraints) 
    float     getLayoutAlignmentX (Container target) 
    float     getLayoutAlignmentY (Container target) 
    void      invalidateLayout    (Container target) 
    Dimension maximumLayoutSize   (Container target) 
  }
Each of these methods uses constraints to help layout the Components in a Containter.

As USERS of layout managers, we do not need to be concerned with the details of how a layout manager works, but at the end of this lecture we will write out own simple layout manager by implementing LayoutManager. As USERS, we construct JPanels, add layout managers to them, and then add components that are managed. These compoents may themselves be containers managing subcomponents.

So, let's bring the focus back to JPanels. We can set the layout manager of a JPanel by calling its setLayout method, passing it a reference to an object constructed from any class implementing LayoutManager; we can add visual components to display in this JPanel by repeatedly calling its add method. In the rest of this section we will discuss specific layout managers provided in the standard Java library. The JavaDoc of these layout managers contains more details their use. Typically, each of these classes implements LayoutManager (for use by Java) and constructors and other methods (which programmers use to control it).

Each of the following layout managers has a special project folder for exploring it in Layout Manager Demos: FlowLayout, BoxLayout, GridLayout, and BorderLayout.

A FlowLayout is constructed possibly using an alignment parameter: CENTER (the default), LEFT, and RIGHT. The programmer can also specify a horizontal and veritical gap between components (which by default is 5 pixels in both directions). In its simplest form, when a component is added, it is placed to the right in the panel (if there is room) and is placed on the next line if there is not (subject to the current gap state). The components are aligned accordingly.

A BoxLayout is constructed using a mandator axis parameter. When a component is added, it is placed to the right of (X_AXIS) or beneath (Y_AXIS) the previously added component. In fact, it requires as its first parameter to be the container that it is laying out. For example: somePanel.setLayout( new BoxLayout(somePanel,BoxLayout.X_AXIS) ); Unlike FlowLayout, it does not create other lines to show components: components may become invisible if they go beyond the horizontal or vertical end of their container. Although BoxLayout doesn't use gaps, it is possible to control spacing by using Box objects (and gluing their components together) or more simply by using Box.Filler objects (whose constructor uses Dimensions to specify minimum, preferred, and maximum sizes).

A GridLayout is constructed (using parameter that specify the number of rows and columns in the grid: one and exactly one of these parameters can be zero, meaning "any number": 0,3 means any number of rows at three columns per row; 3,0 means any number of columns at three rows per column). If both are zero, it throws IllegalArgumentException; if neither is zero and more components are added than there was room specified for, extra columns are created. Like FlowLayout, the programmer can specify a horizontal and veritical gap between components (which by default is 0 in both directions). When a components is added, it is placed in the next position in the grid, which is filled row by row, from the first to last column.

A BorderLayout can be constructed with no parameters, or like FlowLayout the programmer can specify a horizontal and veritical gap between components (which by default is 0 in both directions). We can add a single component at each of the four compass directions (specified by the Strings "North", "South", "East", or "West", as well as at the "Center". Of course, the component we add can be a container, which contains multiple components (managed by another layout manager). The JavaDoc page illustrates this layout manager with the following picture.

A container in the north/south position can stretch horizontally; one in the east/west position can stretch vertically; the container at the center can stretch both vertically and horizontally. By default, the content pane of a JFrame has the BorderLayout manager. We often use it with JPanels to create more interesting GUIs. For example if we have a JPanel named buttons that uses a horizontal BoxLayout to store a series of buttons, we might write

  getContentPane().add(buttons, BorderLayout.NORTH);
at the top of the default border layout for the windo.

Finally, the GridbagLayout is a more complicated layout manager that is similar to the GridLayout, but offers much more control on how its components are layed out in the grid. It uses GridbagConstraints for this purpose. You might want to examine its Javadoc and experiment with it, but we can use JPanels inside JPanels to accomplish the equivalent: learning less but doing a bit more work.


Viewing Control Components Control components have two aspects: their visual aspect -how they appear on the screen (colors, fonts, etc)- and their control aspect -how we can process events linked to them (pressing buttons, etc). For example, it is very easy to construct JButton which appears on the screen as a button icon with the appropriate (optional inner icon) and text. The background of the button can be set to any color; the text can be written in any color, with any font. The button can be disabled: it shows in a slightly washed out color, and cannot be pressed (doesn't process the pressed event); it can enabled at any later time, and disabled again, etc.

In this lecture we will be concerned solely with the visual aspect of a control component; in the next lecture we will focus on their control aspect.

This final GUI testbed application automatically constructs and allows us to use a variety of control components.

  • JButton, which are discussed above.
  • JCheckBox, which we can check/uncheck.
  • JLabel, which we can view (but has no control aspect).
  • JRadioButton, which we can select or unselect.
  • JTextField, in which we can type text (if it is editable).

Each has many constructors. The most useful constructor for each has a single String parameter, which is the text shown in/with the control; actually, the most useful constructor for JTextField specifies only an int, specifying an empty text field with that many columns (spaces for characters).

Just as in the paintComponent method that we explored in the previous lecture, we can set various visual aspects of most of these control components: their foreground and background color, their font (if they are textual), and more specialized information (such as whether a JTextField is editable).

As a quick sidenote, an object from the ButtonGroup class can have a variety of control components (like JCheckBox and JRadioButton) added to it. It implements an EXCLUSIVE selection mechanism: whenever one of these control components is set, the others are automatically unset. The reason this class is explored here is that we can see the visual aspect of this grouping.


Stucturing Complex Views with Recursive JPanels Besides adding control components to containers, we can also add other containers to containers (because Container extends Component). Thus, we can design a GUI to consist of JPanels (each containing a layout manager for the control components added to it) nested inside other JPanels (containg their own layout managers), making the GUI look any way we wish. When Java calls a container's (e.g., JPanel's) paint method, it automatically calls the paint methods of all the components it contains (under the sizing/placement control of its layout manager).

For example, examine the following complicated GUI (ignore the special background/color/font information) taken from the program to download for this lecture.

At the top level, its content pane is composed of a BorderLayout that has information added to its north, center, and south (nothing has been added on its east or west).

  • At the north is a JPanel with a BoxLayout (Y_AXIS)
    • It contains two JTextField components.
  • At the center is a JPanel with a BoxLayout (X_AXIS) or a GridLayout (one row, two columns; the difference between the two is how they stretch).
    • It contains two sub JPanels.
      • On the left is a JPanel with a BoxLayout (Y_AXIS) containing a bunch of check/radio buttons.
      • On the right is a JPanel with a GridLayout (two rows by two columns) containing a bunch of buttons.
  • At the south is a JTextField (uneditable).
As with most GUIs, the code to build this on is long but simple: a big sequence of method calls; few if any other control structures. It declares many JPanels for structuring the view of the GUI, sets their layout managers appropriately, and then adds components to each (some components are other JPanels likewise constructed). By using nested JPanels in this way, we can build almost any view that we desire for a GUI.

The Recursive Layout Project We will go over in class the code for the GUI above; we will also see how we can make changes to it and generally experiment with the components supplied. It is in the Layout Manager Demos, in the project folder named recursive layout

Writing the TwoColumnLayout Manager To explore layout managers further, let's examine one that handles the layout for containers that have two columns: the left column contains labels and the right contains text fields (they should always be added in pairs). The labels should be right justified, and the text fields should be left justified. An example use of this layout manager appears below.

Basically, this manager computes a preferred size as follows. Each JLabel and JTextField has its own preferred size. The manager gets all of the components that is in the container that it manages and then loops through each of these components, accumulating the widest JLabel and widest JTextField: their sum is the preferred width of the entire container (with the horizontal gap added in as well). It also accumulates the height of the container, by adding together the biggest height of each label-text field pair (also adding the vertical gap between each pair). Besides returning the Dimension, it stores into its instance variables the widest JLabel and widest JTextField.

It lays out all these components using a very similar method. First it calls preferredLayoutSize to ensure the widest JLabel and widest JTextField are computed and stored into instance variables. Then it loops through each of its components, keeping track of where the top-left corner of each label-text field pair should appear; with these numbers it computes the eact positions of the label (right justifying it) and the field (left justtifying it). The mathematics is a bit convluted, but not too complicated.

We will go over in class the code for this class and its driver. It is in the Layout Manager Demos, in the project folder named TwoColumn Layout.


Problem Set To ensure that you understand all the material in this lecture, please solve the the announced problems after you read the lecture.

If you get stumped on any problem, go back and read the relevant part of the lecture. If you still have questions, please get help from the Instructor, a CA, or any other student.

  1. To be written