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,
- Correctly positioned components that are independent of fonts, screen resolutions, and platform differences.
- Intelligent component placement for containers that are dynamically resized at runtime.
- Ease of translation with different sized strings. If a string increases in size, the components stay properly aligned.
This chapter contains the following topics:
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.
- All panels (including applets) use FlowLayout.
- All windows (including frames and dialogs) use BorderLayout.
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. 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: You can not edit the layout properties for a <default> layout. If you want to modify the properties for a container's layout manager, you must specify an explicit layout manager, then 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".
See also:
Laying out Components within a container in the Java Language Tutorial at
http://java.sun.com/docs/books/tutorial/ui/layout/.
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 Inspector when the layout manager is selected in the Component 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 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
Below are some examples of layout properties and layout constraints:
- BorderLayout has properties called hgap (horizontal gap) and vgap (vertical gap) that determine the distance between components, while each component in the BorderLayout container has a constraint value, called constraints in the Inspector, with a possible value of NORTH, SOUTH, EAST, WEST, or CENTER.
- FlowLayout and GridLayout have properties you can use to modify the alignment of components, or the vertical and horizontal gap between them.
- GridLayout has properties for specifying the number of rows and columns.
- GridBagLayout has no properties itself. However, each component placed into a GridBagLayout container has a constraints object associated with it that has many properties which control the component's location and appearance, such as:
- The component's height and width
- Where the component is anchored in its cell
- How a component fills up its cell
- How much padding surrounds the component inside its cell
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,
- Select the container in the Component Tree.
- Click the Properties tab in the Inspector and select the layout property.
- 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:
- Substitutes the new layout manager in the Component Tree.
- Changes the source code to add the new layout manager, and updates the container's call to setLayout.
- Changes the layout of components in the UI Designer.
- Updates the layout constraints for the container's components in the Inspector and in the source code.
Modifying layout properties
To modify the properties of a layout from the Inspector,
- 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.
- 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,
- Select the component in the UI Designer or the Tree.
- Select the constraints property in the Inspector.
- 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 containers. 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.
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 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 using the Application Wizard with the defaults, change the nested panel to BorderLayout or FlowLayout without putting anything in it, and 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 various screens 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 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() called 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?
- If you will be constructing the UI from various places within your application, and you always want it to come up in
the same size and place, you may want to consider putting such calls into the constructor of your UI container class (after
the call to jbInit()).
- If your application only instantiates the UI from one place, as in the Application Wizard generated application,
it is perfectly reasonable to put the sizing and positioning code in the place where the UI is created, in this case the Application
class.
Adding custom layout managers
JBuilder supports the integration of other layout managers with its UI Designer. To get a custom layout manager to appear in the Inspector's layout property list, make sure that the layout manager is on the IDEClasspath as defined in JBuilder\bin\JBuilder.ini, and add the following line to JBuilder\lib\jbuilder.properties:
jbuilder.uiassistant.<full LayoutManager classname>=borland.jbuilder.uidesigner.BasicLayoutAssistant
If the custom Layout Manager uses a constraint class, additional integration can be performed by
supplying a class implementing java.beans.PropertyEditor for editing the constraint.
This property editor would also need to be on the IDEClasspath and would be specified in
jbuilder.properties as follows:
jbuilder.propertyeditor.<your constraint class>=<your constraint editor class>
You would then need to extend BasicLayoutAssistant and override two
methods:
public java.beans.PropertyEditor getPropertyEditor() {
//return an instance of the constraints property editor
return new com.mycompany.MyLayoutConstrainteditor();
}
public String getContstraintsType () {
// return the fully qualified constraint class name as a string, for example
return "com.mycompany.myLayoutConstraintEditor";
}
BasicLayoutAssistant is a bare bones implementation of the interface
jbuilder.uidesigner.LayoutAssistant.
Each LayoutManager must be associated with a class that implements this
interface for the LayoutManager to be designable in JBuilder (the association is made in the jbuilder.properties file as described above). There are many other methods in the interface, but it is unfortunately beyond the scope of this document to describe how to use the other methods.
See also:
Component Writer's Guide: Creating properties
Component Writer's Guide: Component properties
Component Writer's Guide: Controlling access to property fields
Component Writer's Guide: Writing property editors
Layouts provided by JBuilder
Borland JBuilder provides the following layout managers from Java AWT and Swing:
JBuilder also provides three custom layouts:
- XYLayout, which keeps components you put in a container at their original size and location (x,y coordinates)
- PaneLayout, used by the SplitPanel control
- VerticalFlowLayout, which is very similar to FlowLayout except that it arranges the components vertically instead of horizontally
Each of JBuilder's layout managers is explained in detail later in this section
You can create custom layouts of your own, or experiment with other layouts such as the ones in the sun.awt classes or third-party layout managers. Many of these 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.
Most 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.