Using layout managers

A program written in Java may be deployed on more than one platform. If you were to use standard UI design techniques of specifying absolute positions and sizes for your UI components, your UI might not look good on all platforms. What looks fine on your development system might be unusable on another platform. To solve this problem, Java provides a system of portable layout managers. You use these layout managers to specify rules and constraints for the layout of your UI in a way that will be portable.

Layout managers give you the following advantages,

About layout managers

A Java UI container (java.awt.Container) uses a special object called a layout manager to control how components are located and sized in the container each time it is displayed. A layout manager automatically arranges the components in a container according to a particular set of rules specific to that layout manager.

The layout manager sets the sizes and locations of the components based on various factors such as

In the Java AWT, certain types of containers use specific layout managers by default.

When you create a container in a Java program, you can accept the default layout manager for that container type, or you can override the default by specifying a different type of layout manager.

Normally, when coding your UI manually, you override the default layout manager before adding components to the container. When using the UI designer, you can change the layout whenever you like. JBuilder will adjust the code as needed.

JBuilder's UI Designer uses a default layout manager for each container, usually the layout of the AWT parent container. But sometimes it overrides the AWT default with a different layout. If you want to use a different layout manager than the default one, you can do so by explicitly adding a layout manager to the source code for the container, or by selecting a layout from the container's layout property list in the Inspector.

Important: If you want to change the properties for a layout manager using JBuilder's visual design tools, you must explicitly specify a layout for a container so its properties will be accessible in the Inspector.

You choose a layout manager based on the overall design you want for the container. Some layouts can be difficult to work with in the UI Designer because they immediately take over placement and resizing of a component as soon as you drop it onto the container. To alleviate this problem during initial layout prototyping, JBuilder provides a custom layout called XYLayout, which leaves the components exactly where you place them and at the size you specify. (See XYLayout below.) Starting with an XYLayout makes prototyping easier in your container. Later, after adding components to the container, you can switch to an appropriate portable layout for your design.

When you start a UI project with the Application or Applet Wizard, JBuilder overrides the default layout managers in the generated source code. It uses BorderLayout for the Main UI frame (DecoratedFrame), and XYLayout for the initial panel (BevelPanel).

In some designs, you might use nested panels to group components in the main Frame, using various different layouts for the Frame and each of its panels.

Note: If you really want to design a panel without a layout manager, you can set the layout manager in the source code to null. However, we don't recommend leaving it this way for deployment.

Experiment with different layouts to see their effect on the container's components. If you find the layout manager you've chosen doesn't give you the results you want, try a different one, or try nesting multiple panels with different layouts to get the desired effect. See Using nested panels and layouts.

For a more detailed discussion of each layout, see the individual topics for each layout in "Layouts provided with JBuilder". You can also study the Java Language Tutorial at http://www.javasoft.com/nav/read/Tutorial/.

Understanding layout properties

Each container normally has some kind of layout manager attached to its layout property. The layout manager has properties that can affect the sizing and location of all components added to the container. These properties can be viewed and edited in the Component Inspector when the layout manager is selected in the Tree. The layout manager displays as an item in the Tree just below the container to which it is attached.

Understanding layout constraints

For each component you drop into a container, JBuilder may instantiate a constraints object, or produce a constraint value, which provides additional information about how the layout manager should size and locate this specific component. The type of constraint object or value created depends upon the type of layout manager being used. The Component Inspector displays the constraints of each component as if they were properties of the component itself, and it allows you to edit them.

Examples of layout properties and constraints

Note: When you initially add a new panel of any type to the UI Designer, you'll notice that the layout property in the Inspector says <default layout>. This means the UI Designer will automatically use the default layout for that container. However, you cannot edit the layout properties for a <default layout>. If you want to change layout properties, such as horizontal or vertical gap, you must explicitly select a layout in the container's layout property in the Inspector, or add a layout manager manually in the source code. When you specify a layout for a container in the Inspector, it becomes visible in the Component Tree and its properties can be modified in the Inspector. For more information on each layout's properties and constraints, see the topic for each layout later in this User's Guide.

Selecting a new layout for a container

JBuilder provides a layout property in the Inspector for containers . With a click of the mouse you can choose a new layout for any container in the UI Designer.

To select a new layout,

  1. Select the container in the Component Tree.
  2. Click the Properties tab in the Inspector and select the layout property.
  3. Click the down arrow at the end of the layout property's value field and choose a layout from the drop-down list.
JBuilder does the following,

Modifying layout properties

To modify the properties of a layout from the Component Inspector,
  1. In the Component Tree, select the layout you want to modify. JBuilder displays the container's layout directly under each container in the Tree. For example, in the picture below, gridLayout1 for groupBox1 is selected.

  2. In the Inspector, select the Properties page and edit the layout's property values. For example, in a gridLayout, you can change the number of columns or rows in the grid, and the horizontal and vertical gap between them.

The Designer displays the changes immediately, and JBuilder modifies the source code's jbInit() method.

Modifying component layout constraints

When you drop a component into a container, JBuilder will create an appropriate constraint object or value for that container's layout manager. JBuilder automatically inserts this constraint value or object into the constraint property of that component in the Inspector. It also adds it to the source code as a parameter of the add() method call in the jbInit() method.

To edit a component's layout constraints,

  1. Select the component in the UI Designer or the Tree.
  2. Select the constraints property in the Inspector.
  3. Use the pull-down list or property editor to modify the constraints.

Understanding sizing properties

Layout managers use various pieces of information to determine how to position and size components in their container. AWT components provide a set of methods that allow layout managers to be intelligent when laying out components. All of these methods are provided so that a component can communicate its desired sizing to whomever is responsible for sizing it (usually a layout manager).

The methods for this look like property getters and represent the following.

getPreferredSize()
The size a component would choose to be, that is, the ideal size for the component to look best. Depending on the rules of the particular layout manager, the preferredSize may or may not be considered in laying out the container.

getMinimumSize()
How small the component can be and still be usable. The minimumSize of a component may be limited, for example, by the size of a label. For most of the AWT controls, minimumSize is the same as preferredSize. Layout managers generally respect minimumSize more than they do preferredSize.

getMaximumSize()
The largest, useful size for this component. This is so the layout manager won't waste space giving it to a component that can't use it effectively, and instead, giving it to another component that has only its minimumSize. For instance, BorderLayout could limit the center component's size to its maximum size, and then either give the space to the edge components, or limit the size of the outer window when resized.

getAlignmentX()
How the component would like to be aligned along the x axis, relative to other components.

getAlignmentY()
How the component would like to be aligned along the y axis, relative to other components.

Most layout managers that existed in JDK 1.0 only consider minimumSize and preferredSize. The other three are new in JDK 1.1.

To understand how each layout manager uses these pieces of information, study the individual layouts listed in "Layouts provided with JBuilder".


Determining the size and location of your UI window at runtime

If your UI class is a descendant of java.awt.Window (such as a Frame or Dialog), you can control its size and location at runtime. The size and location is determined by a combination of what the code does when the UI window is created and what the user does to resize or reposition it.

When the UI window is created, and various components are added to it, each component added affects the preferredSize of the overall window, typically making the preferredSize of the window container larger as additional components are added. The exact effect this has on preferredSize depends on the layout manager of the outer container, as well as any nested container layouts. For more details about the way that preferredLayoutSize is calculated for various layouts, see the sections in this document on each type of layout.

The size of the UI window, as set by your program (before any additional resizing that may be done by the user), is determined by which of the following container methods is called last in the code:

The location of your UI at runtime will be at 0,0 unless you override this by setting the location property of the container (for example by calling setLocation() before making it visible).

Sizing a window automatically with pack()

When you call the pack() method on a window, you are asking it to compute its preferredSize, based upon the components it contains, then size itself to that size. This generally has the effect of making it the smallest it can be while still respecting the preferredSize of the components placed within it.

You can call the pack() method to automatically set the window to a size that is as small as possible and still have all of the controls and subcontainers on it look good. Note that the Application.java file created by the Application Wizard calls pack() on the frame it creates. This causes the frame to be packed to its preferredSize before being made visible.

How the preferredSize is calculated for a container

preferredSize is calculated differently for containers with different layouts.

Portable Layouts

Portable layouts, such as FlowLayout and BorderLayout, calculate their preferredSize based on a combination of the layout rules and the preferredSize of each component that was added to the container. If any of the components were themselves containers (such as a Panel), then the preferredSize of that Panel is calculated according to its layout and components, the calculation recursing into as many layers of nested containers as necessary. For more information about preferredSize calculation for particular layouts, see the individual layout descriptions.

XYLayout

For XYLayout containers, the preferredSize of the container is defined by the values specified in the width and height properties of the XYLayout. For example, if you have the following lines of code in your container initialization,

     xYLayoutN.setWidth(400);
     xYLayoutN.setHeight(300);

and if xYLayoutN is the layout manager for the container, then its preferredSize will be 400 x 300 pixels.

If one of the nested panels in your UI has XYLayout, then that panel's preferredSize will be determined by the layout's setWidth() and setHeight() calls, and that will be the value used for the panel in computing the preferredSize of the next outer container.

For example, in the default Application Wizard application, the nested panel occupying the center of the frame's BorderLayout is itself initially in XYLayout, and is set to size 400 x 300. This has a significant effect on the overall size of the frame when it is packed, because the nested panel will report its preferredSize to be 400x300. The overall frame will be that plus the sizes necessary to satisfy the other components around it in the BorderLayout of the frame.

As an interesting experiment, create a new application with the Application Wizard with the defaults, and change the nested panel to BorderLayout or FlowLayout without putting anything in it, then run the application. The UI will be small, because nothing is holding that central panel open.

Explicitly setting the size of a window using setSize()

If you call setSize() on the container (rather than pack() or subsequent to calling pack()), then the size of the container will be set to a specific size, in pixels. This basically has the same effect as if the user manually sized the container: it overrides the effect of pack() and preferredSize for the container, and sets it to some new arbitrary size.

Important: Although you can certainly set the size of your container to some specific width and height, doing so will make your UI less portable, because different screens have different pixel sizes. If you set size explicitly using setSize(), you must call validate() to get the children laid out properly. (Note that pack() calls validate().

Making the size of your UI portable to various platforms

Generally, if you want the UI to be portable, you should either use pack() and not explicitly setSize(), or you should give careful thought to the pixel sizes of the various screens your application may be deployed on and do some reasonable calculation of the size to set.

For example you may decide that, rather than calling pack(), you want to always have the UI show up at 75% of the width and height of the screen. To do this, you could add the following lines of code to your application class, instead of the call to pack():

   Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
   frame.setSize(screenSize.width * 3 / 4, screenSize.height * 3 / 4);
Note: Also, to ensure portability, change all XYLayout containers to a portable layout after protoyping.

Positioning a window on the screen

If you don't explicitly position your UI on the screen, it will appear in the upper left corner of the screen.

Often it is nicer to center the UI on the screen. This can be done by obtaining the width and height of the screen, subtracting the width and height of your UI, dividing the difference by two (in order to create equal margins on opposite sides of the UI), and using these half difference figures for the location of the upper left corner of your UI.

An example of this is the code mentioned above that is generated by the Center frame on screen option of the Application Wizard. This option causes it to create additional code in the Application class which, after creating the frame, positions it in the center of the screen. Take a look at the code generated by this option to see a good example of how to center your UI.

    //Center the window
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension frameSize = frame.getSize();
    if (frameSize.height > screenSize.height)
       frameSize.height = screenSize.height;
    if (frameSize.width > screenSize.width)
       frameSize.width = screenSize.width;
       frame.setLocation((screenSize.width- frameSize.width) / 2, (screenSize.height - frameSize.height) /2);

Placing the sizing and positioning method calls in your code

The calls to pack(), validate(), setSize(), or setLocation() can be made from inside the UI container class, for example, this.pack(). They can also be called from the class that creates the container, for example, frame.pack(), after invoking the constructor, before the setVisible(). The latter is what the Application Wizard-generated code does: the calls to pack()or validate(), and setLocation() are placed in the Application class, after the frame is constructed and the jbInit() is therefore finished.

How should you decide where to put your calls for sizing and positioning your UI?


Layouts provided by JBuilder

Borland JBuilder provides the following standard layout managers from the Java AWT: BorderLayout, FlowLayout, GridLayout, CardLayout, and GridBagLayout.

JBuilder also provides two custom layouts called XYLayout that keeps components you put in a container at their original size and location (x,y coordinates), and PaneLayout that is used by the SplitPanel control.

You can create custom layouts of your own, or experiment with other layouts like the ones in the sun.awt classes, or third-party layout managers, many of which are public domain on the Web. If you want to use a custom layout in the UI Designer, you may have to provide a Java helper class file to help the UI Designer use the layout.

See the Component Writers Guide for information on creating custom layout managers.

See also:
Creating Properties
Component properties
Controlling access to property fields
Writing property editors

Each of JBuilder's layout managers is explained below and includes an example.

Most of your UI designs will use a combination of layouts by nesting different layout panels within each other. To see how this is done, see Using nested panels and layouts and the tutorial Creating a UI with nested layouts.