Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java Q&A


September 1996: Java Q& A

How Do I Create a Layout Manager?

Cliff Berg

Cliff, vice president of technology of Digital Focus, can be contacted at [email protected]. To submit questions, check out the Java Developer FAQ website at http://www.digitalfocus.com/faq/.


When building a user interface for an application, anticipating the requirements for a variety of displays is a common dilemma. User input fields and controls may look fine on one kind of monitor or computer, yet look too sparse, crowded, or otherwise ill-adjusted on another. Even within a given platform, the user always has the freedom to resize the application's window, possibly destroying the intended layout in the process. This is even more problematic when the application has to run on multiple platforms, like a Java program does.

The Java user-interface toolkit, known as the "Abstract Window Toolkit" (AWT), provides a set of classes that let you build a resizing methodology into an application, without programming it all from scratch. These classes all derive from an interface (Java's parlance for an abstract class) called LayoutManager. There are five predefined layout-manager implementations, each with its own policies for laying out GUI components. You can mix and match these, choosing a different one for each control panel with the application, thereby giving each panel its own method of rearranging components when the window size changes.

This sounds great, except that the five layout managers that come with the Java AWT are not always the ones you might want. For example, one of the five, BorderLayout, lets you arrange components along the four sides of the window (or panel), with another in the center. This is well-suited for the top-level window layout in many situations, in which the main application area sits in the center, with a title or banner of some kind at the top, and controls at the bottom, left, and right. It falls short, however, if you want to add more than one component along a single edge. To do that, you have to create another panel, place that on the desired edge, and then place the components on the panel. While this is not difficult, the layout does not have to be too complex before one starts to think, "If only the layout manager did this or that...."

Java's extensibility makes it possible to create a layout manager that does exactly what you want. Indeed, this was intended, and the LayoutManager interface defines exactly the methods that you need to write to create your own customized layout manager. The layout manager you create becomes part of your application-it need not be incorporated into the Java environment.

The most widely used layout manager is FlowLayout, which implements the policy of placing components from left to right, either right justified, centered, or left justified, and flows them to successive rows if sufficient space is not available. However, it doesn't let you create new rows via program control. It also doesn't let you add space between unrelated components without having to go to the trouble of creating a whole separate panel with its own layout manager. These are the features that I'll incorporate into the layout manager presented here.

Before I describe this layout manager, first consider how a layout manager is actually used. The Java AWT defines an object class called Component that represents a user-interface (UI) component. All UI elements are derived from the Component class. A special kind of component that can contain other components is known as a Container. The Container class derives from Component. Elements such as windows, frames, and control panels that may contain other components all derive from Container. Thus, it is the container that owns a collection of components, and the container that is ultimately responsible for laying out those components. To accomplish this, container objects use layout managers, and each container object may have exactly one layout manager for deciding how to lay out its components. In Example 1, a new panel is created, which, because Panel derives from Container, may contain other components. A layout manager object, of type BorderLayout, is created, and the panel's layout manager is set to this. The panel will now use this BorderLayout object for helping it to arrange its subcomponents. Subsequently, a button object is created and added to the panel. At this time, the panel will notify the BorderLayout object that a button has been added. Later, when the panel is resized as a result of a user action or a call to resize(), the panel will call upon the BorderLayout to help it decide where to place the button in the newly resized panel.

SpaceLayout

I'll now present a simple layout manager called SpaceLayout, which implements precisely these policies. To use it, you just create an instance of it within your program, just as you would with any of the predefined layout managers, and then call setLayout(); see Example 2(a). All layout managers must define the methods in Example 2(b).

These are the methods used by other classes in the AWT (in particular, the Container classes) when they need to call upon a layout manager to help them place components-normally you do not call these methods directly within your code. Instead, you call the method Container.add() and other methods for the window, frame, panel, or other user-interface container objects that contain the components. These methods, in turn, call the layout-manager methods, using the layout manager that has been specified for that container.

If you correctly implement these methods, you should have a working layout manager.

Some layout managers implement a simple stateless policy in which the placement of one component can be determined immediately from the location of the component to its left or right. In this case, it is not necessary for the layout manager to store any information about each component, so the methods addLayoutComponent() and removeLayoutComponent() can do nothing. The container keeps its own list of what components it contains, so you do not have to worry that the container will forget what UI elements it encloses. That is not the job of the layout manager-it only implements policies for layout. It does not define the structure of the UI, or the component hierarchy.

Some layout managers, like GridBagLayout and SpaceLayout, allow you to define parameters or constraints for each component to be laid out. These constraints need to be maintained in a list or persistent structure that relates them back to the actual components. SpaceLayout uses a hash table for this purpose: the class java.util.Hashtable. The method addLayoutComponent(), which is called by a Container object automatically when a component is added to that container, has the main job of creating a constraint attribute object for the component being added, and associates the constraint with the component via the hash table.

Note that there are two variations of the Container.add() method. One takes a single parameter (the component to add), and the other takes a string and the component to add. The second form calls LayoutManager.addLayoutComponent(), whereas the first form skips this step, under the questionable assumption that programmers using this form are using simple stateless layout managers. This is a potential source of Java programming bugs. You should therefore always use the Component.add(String, Component) method rather than the Component.add(Component) method, even if you have to make up a string value or simply give it a null string.

The method SpaceLayout.setConstraints() allows you to define constraints for a component. For example, setConstraints(Constraints.LEFT, Constraints.TOP, false, 10, 5, buttonA); specifies that buttonA will be left- and top-aligned within the space allocated for it; it will be ten pixels from the next component's space (which may have its own spacing requirement), and five pixels from the components above it. You can now set constraints for the buttonB button: setConstraints(Constraints.LEFT, Constraints .TOP, true, 10, 5, buttonB);. The Boolean value of true indicates that you want this to be the last component in its row, and that the next component after it should be placed on the next row.

There is a Catch-22 in that to place the components successfully, you first have to know the size of each; the placement of one component may affect the others. Yet, once you discover the component sizes, they may turn out to be too big to fit into the space available, so the components themselves may have to be resized, and a new layout calculated. This could potentially be a long recursive process. Some layout managers compromise and merely reshuffle the components, while others actually resize the components as needed.

The minimumLayoutSize() method returns the smallest size allowed for the layout, and the preferredLayoutSize() returns the ideal size. In my implementation, you simply always return the preferred size, which is sufficient in most cases. The implementation of minimumLayoutSize() is left to the reader.

The real workhorse of a layout-manager class is the layoutContainer() method. In SpaceLayout, layoutContainer() delegates its work to a method called layoutSize(), which calculates the size required by the container and all of its components, and optionally moves the components into this new layout. This is because the steps required to do the calculation and those required to create the layout are very similar, and so it makes sense to combine them.

To actually identify the components contained within the specified Container object, layoutSize() first determines the number of components in the container, using the Container.countComponents() method. It then iterates through these in ascending order, using Container.getComponent() to identify each component by its ordinal position. The ordering of components retrieved in this way is identical to the order in which they were originally added to the container; this order is significant, and is the order used in laying out the components. Listing One is a more-complete example of SpaceLayout's use, while Figure 1 is the resulting layout.

Summary

The layout managers provided with the Java AWT provide sufficient functionality for basic layout requirements. Nevertheless, many applications require layout policies different from those in the standard layout managers. The LayoutManager interface provides a way to create new layout managers with customized policies for any application.

Creating your own layout manager also gives you additional control over the sizes of the components themselves. As mentioned before, some of the standard layout managers actually resize components to fit into the allocated space. Therefore, a call to resize() for a component within your program may have no effect, because the same component may subsequently be resized by the container's layout manager according to its policies. For example, FlowLayout always resizes components to their preferred size, regardless of the space available. Using your own layout manager allows you to control more precisely when component resizing is done.

The complete source code for SpaceLayout is available electronically and at the Digital Focus Web site (http://www.digitalfocus.com/ddj/code/). In addition, a more-complete version of SpaceLayout can be found at the Digital Focus site. In this version, the minimumLayoutSize() method is fully implemented, and the spacing constraints are treated as minimum values.

Example 1: Creating a new panel.

Panel p = new Panel();
bl = new BorderLayout();
p.setLayout(bl);
 ...
Button b = new Button();
p.add("Center", b);


Example 2: (a) Calling setLayout(); all layout managers must define these methods.

(a)     SpaceLayout s = new SpaceLayout();
setLayout(s);
 ...

(b)     addLayoutComponent(String name, Component comp)
removeLayoutComponent(Component comp)
minimumLayoutSize(Container target)
preferredLayoutSize(Container target)
layoutContainer(Container target)

Figure 1: The layout that results from Listing One.

Listing One


import java.applet.*;
import java.awt.*;

{
    public void init()
    {
        setBackground(Color.white);
        SpaceLayout lm = new SpaceLayout();
        setLayout(lm);
        Button a = new Button("Start");
        Label la = new Label("Start Engines");
        Button b = new Button("Impulse");
        Button c = new Button("Warp");
        Button e = new Button("Scotty");
        Label le = new Label("Call Scotty Now!");
        add("A", a);    // Note: always give components a name!
        add(null, la);
        add("B", b);
        add("C", c);
        add("E", e);
        add(null, le);
        lm.setHGap(2);
        lm.setVGap(5);
        lm.setConstraints(Constraints.RIGHT,Constraints.CENTER,false,5,5,a);
        lm.setConstraints(Constraints.LEFT,Constraints.CENTER,false,5,5,la);

        lm.setConstraints(Constraints.RIGHT,Constraints.CENTER,false,80,5,b);
        lm.setConstraints(Constraints.LEFT,Constraints.CENTER,true,20,5,c);

        lm.setConstraints(Constraints.RIGHT,Constraints.CENTER,false,5,5,e);
        lm.setConstraints(Constraints.LEFT,Constraints.CENTER,false,5,5,le);
    }
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.