Anda di halaman 1dari 93

Lesson: Laying Out Components

Within a Container
This lesson tells you how to use the layout managers provided by the Java platform. It also tells you
how to use absolute positioning (no layout manager) and gives an example of writing a custom
layout manager. For each layout manager (or lack thereof), this lesson points to an example that you
can run using JavaTM Web Start. By resizing an example's window, you can see how size changes
affect the layout.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

A Visual Guide to Layout Managers

This section shows examples of the standard layout managers and points to the how-to section for
each one.

Using Layout Managers

This section gives general rules on using the standard layout managers. It includes how to set the
layout manager, add components to a container, provide size and alignment hints, put space between
components, and set the orientation of the container's layout so that it is appropriate for the locale in
which the program is running. It also has some tips for choosing the right layout manager.

How Layout Management Works

This section goes through a typical layout sequence and then describes what happens when a
component's size changes.

How to Use ...

This series of sections tells you how to use each of the general-purpose layout managers that the Java
platform provides.

Creating a Custom Layout Manager

Instead of using one of the Java platform's layout managers, you can write your own. Layout
managers must implement the LayoutManager interface, which specifies the five methods every
layout manager must define. Optionally, layout managers can implement LayoutManager2, which is
a subinterface of LayoutManager.

840
Doing Without a Layout Manager (Absolute Positioning)

If necessary, you can position components without using a layout manager. Generally, this solution
is used to specify absolute sizes and positions for components.

Solving Common Layout Problems

Some of the most common layout problems involve components that are displayed too small — or
not at all. This section tells you how to fix these and other common layout problems.

Questions and Exercises

Try these questions and exercises to test what you have learned in this lesson.

A Visual Guide to Layout Managers


Several AWT and Swing classes provide layout managers for general use:

 BorderLayout
 BoxLayout
 CardLayout
 FlowLayout
 GridBagLayout
 GridLayout
 GroupLayout
 SpringLayout

This section shows example GUIs that use these layout managers, and tells you where to find the
how-to page for each layout manager. You can find links for running the examples in the how-to
pages and in the example index.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

BorderLayout

841
Every content pane is initialized to use a BorderLayout. (As Using Top-Level Containers explains,
the content pane is the main container in all frames, applets, and dialogs.) A BorderLayout places
components in up to five areas: top, bottom, left, right, and center. All extra space is placed in the
center area. Tool bars that are created using JToolBar must be created within a BorderLayout
container, if you want to be able to drag and drop the bars away from their starting positions. For
further details, see How to Use BorderLayout.

BoxLayout

The BoxLayout class puts components in a single row or column. It respects the components'
requested maximum sizes and also lets you align components. For further details, see How to Use
BoxLayout.

CardLayout

The CardLayout class lets you implement an area that contains different components at different
times. A CardLayout is often controlled by a combo box, with the state of the combo box
determining which panel (group of components) the CardLayout displays. An alternative to using
CardLayout is using a tabbed pane, which provides similar functionality but with a pre-defined GUI.
For further details, see How to Use CardLayout.

FlowLayout

FlowLayout is the default layout manager for every JPanel. It simply lays out components in a
single row, starting a new row if its container is not sufficiently wide. Both panels in
CardLayoutDemo, shown previously, use FlowLayout. For further details, see How to Use
FlowLayout.

GridBagLayout

842
GridBagLayout is a sophisticated, flexible layout manager. It aligns components by placing them
within a grid of cells, allowing components to span more than one cell. The rows in the grid can have
different heights, and grid columns can have different widths. For further details, see How to Use
GridBagLayout.

GridLayout

GridLayout simply makes a bunch of components equal in size and displays them in the requested
number of rows and columns. For further details, see How to Use GridLayout.

GroupLayout

GroupLayout is a layout manager that was developed for use by GUI builder tools, but it can also be
used manually. GroupLayout works with the horizontal and vertical layouts separately. The layout is
defined for each dimension independently. Consequently, however, each component needs to be
defined twice in the layout. The Find window shown above is an example of a GroupLayout. For
further details, see How to Use GroupLayout.

SpringLayout

843
SpringLayout is a flexible layout manager designed for use by GUI builders. It lets you specify
precise relationships between the edges of components under its control. For example, you might
define that the left edge of one component is a certain distance (which can be dynamically
calculated) from the right edge of a second component. SpringLayout lays out the children of its
associated container according to a set of constraints, as shall be seen in How to Use SpringLayout.

Using Layout Managers


A layout manager is an object that implements the LayoutManager interface* and determines the
size and position of the components within a container. Although components can provide size and
alignment hints, a container's layout manager has the final say on the size and position of the
components within the container.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

This section discusses some of the common tasks related to using layout managers:

 Setting the Layout Manager


 Adding Components to a Container
 Providing Size and Alignment Hints
 Putting Space Between Components
 Setting the Container's Orientation
 Tips on Choosing a Layout Manager
 Third-Party Layout Managers

Setting the Layout Manager

As a rule, the only containers whose layout managers you need to worry about are JPanels and
content panes. Each JPanel object is initialized to use a FlowLayout, unless you specify differently
when creating the JPanel. Content panes use BorderLayout by default. If you do not like the default
layout manager that a panel or content pane uses, you are free to change it to a different one.
However, unless you are using JToolBar, the FlowLayout and BorderLayout managers are only
useful for prototyping. Any real application will need to reset the layout manager. Again, you should
use an appropriate tool to do this, rather than coding the manager by hand.

844
You can set a panel's layout manager using the JPanel constructor. For example:

JPanel panel = new JPanel(new BorderLayout());


After a container has been created, you can set its layout manager using the setLayout method. For
example:
Container contentPane = frame.getContentPane();
contentPane.setLayout(new FlowLayout());

Although we strongly recommend that you use layout managers, you can perform layout without
them. By setting a container's layout property to null, you make the container use no layout manager.
With this strategy, called absolute positioning, you must specify the size and position of every
component within that container. One drawback of absolute positioning is that it does not adjust well
when the top-level container is resized. It also does not adjust well to differences between users and
systems, such as different font sizes and locales.

Adding Components to a Container

When you add components to a panel or content pane, the arguments you specify to the add method
depend on the layout manager that the panel or content pane is using. In fact, some layout managers
do not even require you to add the component explicitly; for example, GroupLayout. For example,
BorderLayout requires that you specify the area to which the component should be added (using one
of the constants defined in BorderLayout) using code like this:
pane.add(aComponent, BorderLayout.PAGE_START);

The how-to section for each layout manager has details on what, if any, arguments you need to
specify to the add method. Some layout managers, such as GridBagLayout and SpringLayout,
require elaborate setup procedures. Many layout managers, however, simply place components based
on the order they were added to their container.

Swing containers other than JPanel and content panes generally provide API that you should use
instead of the add method. For example, instead of adding a component directly to a scroll pane (or,
actually, to its viewport), you either specify the component in the JScrollPane constructor or use
setViewportView. Because of specialized API like this, you do not need to know which layout
manager (if any) many Swing containers use. (For the curious: scroll panes happen to use a layout
manager named ScrollPaneLayout.)

For information about how to add components to a specific container, see the how-to page for the
container. You can find the component how-to pages using How to Use Various Components.

Providing Size and Alignment Hints

Sometimes you need to customize the size hints that a component provides to its container's layout
manager, so that the component will be laid out well. You can do this by specifying one or more of
the minimum, preferred, and maximum sizes of the component. You can invoke the component's
methods for setting size hints — setMinimumSize, setPreferredSize, and setMaximumSize. Or
you can create a subclass of the component that overrides the appropriate getter methods —
getMinimumSize, getPreferredSize, and getMaximumSize. Here is an example of making a
component's maximum size unlimited:
component.setMaximumSize(new Dimension(Integer.MAX_VALUE,
Integer.MAX_VALUE));

845
Many layout managers do not pay attention to a component's requested maximum size. However,
BoxLayout and SpringLayout do. Furthermore, GroupLayout provides the ability to set the
minimum, preferred or maximum size explicitly, without touching the component.

Besides providing size hints, you can also provide alignment hints. For example, you can specify that
the top edges of two components should be aligned. You set alignment hints either by invoking the
component's setAlignmentX and setAlignmentY methods, or by overriding the component's
getAlignmentX and getAlignmentY methods. Although most layout managers ignore alignment
hints, BoxLayout honors them. You can find examples of setting the alignment in How to Use
BoxLayout.

Putting Space Between Components

Three factors influence the amount of space between visible components in a container:
The layout manager
Some layout managers automatically put space between components; others do not.
Some let you specify the amount of space between components. See the how-to page
for each layout manager for information about spacing support.
Invisible components
You can create lightweight components that perform no painting, but that can take up
space in the GUI. Often, you use invisible components in containers controlled by
BoxLayout. See How to Use BoxLayout for examples of using invisible components.
Empty borders
No matter what the layout manager, you can affect the apparent amount of space
between components by adding empty borders to components. The best candidates for
empty borders are components that typically have no default border, such as panels
and labels. Some other components might not work well with borders in some look-
and-feel implementations, because of the way their painting code is implemented. For
information about borders, see How to Use Borders.

Setting the Container's Orientation

This website is written in English, with text that runs from left to right, and then top to bottom.
However, many other languages have different orientations. The componentOrientation property
provides a way of indicating that a particular component should use something different from the
default left-to-right, top-to-bottom orientation. In a component such as a radio button, the orientation
might be used as a hint that the look and feel should switch the locations of the icon and text in the
button. In a container, the orientation is used as a hint to the layout manager.

To set a container's orientation, you can use either the Component-defined method
setComponentOrientation or, to set the orientation on the container's children as well,
applyComponentOrientation. The argument to either method can be a constant such as
ComponentOrientation.RIGHT_TO_LEFT, or it can be a call to the ComponentOrientation method
getOrientation(Locale). For example, the following code causes all JComponents to be
initialized with an Arabic-language locale, and then sets the orientation of the content pane and all
components inside it accordingly:

JComponent.setDefaultLocale(new Locale("ar"));
JFrame frame = new JFrame();
...
Container contentPane = frame.getContentPane();
contentPane.applyComponentOrientation(
ComponentOrientation.getOrientation(

846
contentPane.getLocale()));
Here are two pictures showing how FlowLayout lays out components in containers that are exactly
the same, except for their orientation.

Default orientation (left-to-right)

Right-to-left orientation

The standard layout managers that support component orientation are FlowLayout, BorderLayout,
BoxLayout, GridBagLayout, and GridLayout.

Note: Care must be taken that the component orientation is applied to renderers, editors and any
other components unreachable through normal traversal of the containment hierarchy.

Tips on Choosing a Layout Manager

Layout managers have different strengths and weaknesses. This section discusses some common
layout scenarios and which layout managers might work for each scenario. However, once again, it is
strongly recommended that you use a builder tool to create your layout managers, such as the
NetBeans IDE 5.5 Matisse GUI builder, rather than coding managers by hand. The scenarios listed
below are given for information purposes, in case you are curious about which type of manager is
used in different situations, or in case you absolutely must code your manager manually.

If none of the layout managers we discuss is right for your situation and you cannot use a builder
tool, feel free to use other layout managers that you may write or find. Also keep in mind that
flexible layout managers such as GridBagLayout and SpringLayout can fulfill many layout needs.

Scenario: You need to display a component in as much space as it can get.


If it is the only component in its container, use GridLayout or BorderLayout.
Otherwise, BorderLayout or GridBagLayout might be a good match.

If you use BorderLayout, you will need to put the space-hungry component in the
center. With GridBagLayout, you will need to set the constraints for the component
so that fill=GridBagConstraints.BOTH. Another possibility is to use BoxLayout,
making the space-hungry component specify very large preferred and maximum sizes.

Scenario: You need to display a few components in a compact row at their natural size.
Consider using a JPanel to group the components and using either the JPanel's
default FlowLayout manager or the BoxLayout manager. SpringLayout is also good
for this.
Scenario: You need to display a few components of the same size in rows and columns.
GridLayout is perfect for this.

847
Scenario: You need to display a few components in a row or column, possibly with varying amounts
of space between them, custom alignment, or custom component sizes.
BoxLayout is perfect for this.
Scenario: You need to display aligned columns, as in a form-like interface where a column of labels
is used to describe text fields in an adjacent column.
SpringLayout is a natural choice for this. The SpringUtilities class used by
several Tutorial examples defines a makeCompactGrid method that lets you easily
align multiple rows and columns of components.
Scenario: You have a complex layout with many components.
Consider either using a very flexible layout manager such as GridBagLayout or
SpringLayout, or grouping the components into one or more JPanels to simplify
layout. If you take the latter approach, each JPanel might use a different layout
manager.

Third Party Layout Managers

Other third party layout managers have been created by the Swing community, to complement those
provided by the Java platform. The following list is by no means definitive, but the layout managers
listed below are the most popular:

 TableLayout
 MiGLayout
 Karsten Lentzsch's FormLayout

*Way back in JDK 1.1 a second interface, LayoutManager2, was introduced. LayoutManager2 extends
LayoutManager, providing support for maximum size and alignment. LayoutManager2 also adds the methods
addLayoutComponent, that takes an Object, and invalidateLayout. Layout managers also need the
notifications provided by LayoutManager2, so any modern layout manager will need to implement it.

How Layout Management Works


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

Here is an example of a layout management sequence for a container using LayoutManager2.

1. Layout managers basically do two things:


o Calculate the minimum/preferred/maximum sizes for a container.
o Lay out the container's children.

Layout managers do this based on the provided constraints, the container's properties (such as
insets) and on the children's minimum/preferred/maximum sizes. If a child is itself a
container then its own layout manger is used to get its minimum/preferred/maximum sizes
and to lay it out.

848
2. A container can be valid (namely, isValid() returns true) or invalid. For a container to be
valid, all the container's children must be laid out already and must all be valid also. The
Container.validate method can be used to validate an invalid container. This method
triggers the layout for the container and all the child containers down the component
hierarchy and marks this container as valid.
3. After a component is created it is in the invalid state by default. The Window.pack method
validates the window and lays out the window's component hierarchy for the first time.

The end result is that to determine the best size for the container, the system determines the sizes of
the containers at the bottom of the containment hierarchy. These sizes then percolate up the
containment hierarchy, eventually determining the container's total size.

If the size of a component changes, for example following a change of font, the component must be
resized and repainted by calling the revalidate and repaint methods on that component. Both
revalidate and repaint are thread-safe — you need not invoke them from the event-dispatching
thread.

When you invoke revalidate on a component, a request is passed up the containment hierarchy
until it encounters a container, such as a scroll pane or top-level container, that should not be affected
by the component's resizing. (This is determined by calling the container's isValidateRoot
method.) The container is then laid out, which has the effect of adjusting the revalidated component's
size and the size of all affected components.

How to Use Various Layout Managers


Each of the following pages describes how to use a particular kind of layout manager. Another way
to get to these pages is through A Visual Guide to Layout Managers.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

 How to Use BorderLayout


 How to Use BoxLayout
 How to Use CardLayout
 How to Use FlowLayout
 How to Use GridBagLayout
 How to Use GridLayout
 How to Use GroupLayout
 How to Use SpringLayout

How to Use BorderLayout


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

849
The following figure represents a snapshot of an application that uses the BorderLayout class.

Click the Launch button to run BorderLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

The complete code of this demo is in the BorderLayoutDemo.java file.

As the preceding picture shows, a BorderLayout object has five areas. These areas are specified by
the BorderLayout constants:

 PAGE_START
 PAGE_END
 LINE_START
 LINE_END
 CENTER

Version note: Before JDK release 1.4, the preferred names for the various areas were different,
ranging from points of the compass (for example, BorderLayout.NORTH for the top area) to wordier
versions of the constants we use in our examples. The constants our examples use are preferred
because they are standard and enable programs to adjust to languages that have different orientations.

If the window is enlarged, the center area gets as much of the available space as possible. The other
areas expand only as much as necessary to fill all available space. Often a container uses only one or
two of the areas of the BorderLayout object — just the center, or the center and the bottom.

The following code adds components to a frame's content pane. Because content panes use the
BorderLayout class by default, the code does not need to set the layout manager. The complete
program is in the BorderLayoutDemo.java file.

...//Container pane = aFrame.getContentPane()...


JButton button = new JButton("Button 1 (PAGE_START)");
pane.add(button, BorderLayout.PAGE_START);

//Make the center component big, since that's the


//typical usage of BorderLayout.
button = new JButton("Button 2 (CENTER)");
button.setPreferredSize(new Dimension(200, 100));
pane.add(button, BorderLayout.CENTER);

button = new JButton("Button 3 (LINE_START)");


pane.add(button, BorderLayout.LINE_START);

850
button = new JButton("Long-Named Button 4 (PAGE_END)");
pane.add(button, BorderLayout.PAGE_END);

button = new JButton("5 (LINE_END)");


pane.add(button, BorderLayout.LINE_END);

Specify the component's location (for example, BorderLayout.LINE_END) as one of the arguments
to the add method. If this component is missing from a container controlled by a BorderLayout
object, make sure that the component's location was specified and no another component was placed
in the same location.

All tutorial examples that use the BorderLayout class specify the component as the first argument to
the add method. For example:

add(component, BorderLayout.CENTER) //preferred


However, the code in other programs specifies the component as the second argument. For example,
here are alternate ways of writing the preceding code:
add(BorderLayout.CENTER, component) //valid but old fashioned
or
add("Center", component) //valid but error prone

The BorderLayout API

The following table lists constructors and methods to specify gaps (in pixels).
Specifying gaps
Constructor or Method Purpose
BorderLayout(int horizontalGap, int Defines a border layout with specified gaps
verticalGap) between components.
setHgap(int) Sets the horizontal gap between components.
setVgap(int) Sets the vertical gap between components.

Examples that Use BorderLayout

The following table lists code examples that use the BorderLayout class and provides links to
related sections.
Where
Example Notes
Described
BorderLayoutDemo This page Puts a component in each of the five possible locations.
One of many examples that puts a single component in the
How to Use
TabbedPaneDemo center of a content pane, so that the component is as large as
Tabbed Panes
possible.
Creates a JPanel object that uses the BorderLayout class.
How to Use
CheckBoxDemo Puts components into the left (actually, LINE_START) and
Check Boxes
center locations.

851
How to Use BoxLayout
Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

The Swing packages include a general purpose layout manager named BoxLayout. BoxLayout either
stacks its components on top of each other or places them in a row — your choice. You might think
of it as a version of FlowLayout, but with greater functionality. Here is a picture of an application
that demonstrates using BoxLayout to display a centered column of components:

Click the Launch button to run BoxLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

You can see the code in BoxLayoutDemo.java.

The following figure shows a GUI that uses two instances of BoxLayout. In the top part of the GUI,
a top-to-bottom box layout places a label above a scroll pane. In the bottom part of the GUI, a left-to-
right box layout places two buttons next to each other. A BorderLayout combines the two parts of
the GUI and ensures that any excess space is given to the scroll pane.

You can find links for running ListDialog and for its source files in the example index for Using
Swing Components.

The following code, taken from ListDialog.java, lays out the GUI. This code is in the constructor
for the dialog, which is implemented as a JDialog subclass. The bold lines of code set up the box
layouts and add components to them.

JScrollPane listScroller = new JScrollPane(list);


listScroller.setPreferredSize(new Dimension(250, 80));

852
listScroller.setAlignmentX(LEFT_ALIGNMENT);
...
//Lay out the label and scroll pane from top to bottom.
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
JLabel label = new JLabel(labelText);
...
listPane.add(label);
listPane.add(Box.createRigidArea(new Dimension(0,5)));
listPane.add(listScroller);
listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));

//Lay out the buttons from left to right.


JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
buttonPane.add(Box.createHorizontalGlue());
buttonPane.add(cancelButton);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
buttonPane.add(setButton);

//Put everything together, using the content pane's BorderLayout.


Container contentPane = getContentPane();
contentPane.add(listPane, BorderLayout.CENTER);
contentPane.add(buttonPane, BorderLayout.PAGE_END);

The first bold line creates a top-to-bottom box layout and sets it up as the layout manager for
listPane. The two arguments to the BoxLayout constructor are the container that it manages and the
axis along which the components will be laid out. The PAGE_AXIS constant specifies that components
should be laid out in the direction that lines flow across a page as determined by the target container's
ComponentOrientation property. The LINE_AXIS constant specifies that components should be laid
out in the direction of a line of text as determined by the target container's ComponentOrientation
property. These constants allow for internationalization, by laying out components in their container
with the correct left-to-right, right-to-left or top-to-bottom orientation for the language being used.

The next three bold lines add the label and scroll pane to the container, separating them with a rigid
area — an invisible component used to add space between components. In this case, the rigid area
has no width and puts exactly 5 pixels between the label and scroll pane. Rigid areas are discussed
later, in Using Invisible Components as Filler.

The next chunk of bold code creates a left-to-right box layout and sets it up for the buttonPane
container. Then the code adds two buttons to the container, using a rigid area to put 10 pixels
between the buttons. To place the buttons at the right side of their container, the first component
added to the container is glue. This glue is an invisible component that grows as necessary to absorb
any extra space in its container. Glue is discussed in Using Invisible Components as Filler.

As an alternative to using invisible components, you can sometimes use empty borders to create
space around components, most particularly panels. For example, the preceding code snippet uses
empty borders to put 10 pixels between all sides of the dialog and its contents, and between the two
parts of the contents. Borders are completely independent of layout managers. They are simply how
Swing components draw their edges and provide padding between the content of the component and
the edge. See How to Use Borders for more information.

The following sections discuss BoxLayout in more detail:

 Box layout features

853
 Using invisible components as filler
 Fixing alignment problems
 Specifying component sizes
 The box layout API
 Examples that use box layouts

Do not let the length of the BoxLayout discussion scare you! You can probably use BoxLayout with
the information you already have. If you run into trouble or you want to take advantage of
BoxLayout's power, read on.

Box Layout Features

As said before, BoxLayout arranges components either on top of each other or in a row. As the box
layout arranges components, it takes the components' alignments and minimum, preferred, and
maximum sizes into account. In this section, we will talk about top-to-bottom layout. The same
concepts apply to left-to-right or right-to-left layout. You simply substitute X for Y, height for width,
and so on.

Version note: Before JDK version 1.4, no constants existed for specifying the box layout's axis in a
localizable way. Instead, you specified X_AXIS (left to right) or Y_AXIS (top to bottom) when
creating the BoxLayout. Our examples now use the constants LINE_AXIS and PAGE_AXIS, which are
preferred because they enable programs to adjust to languages that have different orientations. In the
default, left-to-right orientation, LINE_AXIS specifies left-to-right layout and PAGE_AXIS specifies
top-to-bottom layout.

When a BoxLayout lays out components from top to bottom, it tries to size each component at the
component's preferred height. If the vertical space of the layout does not match the sum of the
preferred heights, then BoxLayout tries to resize the components to fill the space. The components
either grow or shrink to fill the space, with BoxLayout honoring the minimum and maximum sizes of
each of the components. Any extra space appears at the bottom of the container.

For a top-to-bottom box layout, the preferred width of the container is that of the maximum preferred
width of the children. If the container is forced to be wider than that, BoxLayout attempts to size the
width of each component to that of the container's width (minus insets). If the maximum size of a
component is smaller than the width of the container, then X alignment comes into play.

The X alignments affect not only the components' positions relative to each other, but also the
location of the components (as a group) within their container. The following figures illustrate
alignment of components that have restricted maximum widths.

854
In the first figure, all three components have an X alignment of 0.0 (Component.LEFT_ALIGNMENT).
This means that the components' left sides should be aligned. Furthermore, it means that all three
components are positioned as far left in their container as possible.

In the second figure, all three components have an X alignment of 0.5


(Component.CENTER_ALIGNMENT). This means that the components' centers should be aligned, and
that the components should be positioned in the horizontal center of their container.

In the third figure, the components have an X alignment of 1.0 (Component.RIGHT_ALIGNMENT).


You can guess what that means for the components' alignment and position relative to their
container.

You might be wondering what happens when the components have both restricted maximum sizes
and different X alignments. The next figure shows an example of this:

As you can see, the left side of the component with an X alignment of 0.0
(Component.LEFT_ALIGNMENT) is aligned with the center of the component that has the 0.5 X
alignment (Component.CENTER_ALIGNMENT), which is aligned with the right side of the component
that has an X alignment of 1.0 (Component.RIGHT_ALIGNMENT). Mixed alignments like this are
further discussed in Fixing Alignment Problems.

What if none of the components has a maximum width? In this case, if all the components have
identical X alignment, then all components are made as wide as their container. If the X alignments
are different, then any component with an X alignment of 0.0 (left) or 1.0 (right) will be smaller. All
components with an intermediate X alignment (such as center) will be as wide as their container.
Here are two examples:

To get to know BoxLayout better, you can run your own experiments with BoxLayoutDemo2.

Try this:

855
1. Click the Launch button to run BoxLayoutDemo2 using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.

You can see the code in BoxLayoutDemo2.java.


You will see a window like the one above that contains three rectangles. Each
rectangle is an instance of BLDComponent, which is a JComponent subclass.

2. Click inside one of the rectangles.


This is how you change the rectangle's X alignment.
3. Click the check box at the bottom of the window.
This turns off restricted sizing for all the rectangles.
4. Make the window taller.
This makes the rectangles' container larger than the sum of the rectangles' preferred
sizes. The container is a JPanel that has a red outline, so that you can tell where the
container's edges are.

Using Invisible Components as Filler

Each component controlled by a box layout butts up against its neighboring components. If you want
to have space between components, you can either add an empty border to one or both components,
or insert invisible components to provide the space. You can create invisible components with the
help of the Box class.

The Box class defines a nested class, Box.Filler, that is a transparent component that paints
nothing, and is used to provide space between other components. However, Filler is not actually
invisible, because setVisible(false) is not invoked. The Box class provides convenience methods
to help you create common kinds of filler. The following table gives details about creating invisible
components with Box and Box.Filler.

Type Size Constraints How to Create

rigid area Box.createRigidArea(size)

horizontal Box.createHorizontalGlue()

glue
vertical Box.createVerticalGlue()

new Box.Filler(minSize, prefSize,


custom Box.Filler (as specified) maxSize)

Here is how you generally use each type of filler:

Rigid area
Use this when you want a fixed-size space between two components. For example, to
put 5 pixels between two components in a left-to-right box, you can use this code:
container.add(firstComponent);

856
container.add(Box.createRigidArea(new Dimension(5,0)));
container.add(secondComponent);

Note: The Box class provides another kind of filler for putting fixed space between
components: a vertical or horizontal strut. Unfortunately, struts have unlimited
maximum heights or widths (for horizontal and vertical struts, respectively). This
means that if you use a horizontal box within a vertical box, for example, the
horizontal box can sometimes become too tall. For this reason, we recommend that
you use rigid areas instead of struts.

Glue
Use this to specify where excess space in a layout should go. Think of it as a kind of
elastic glue — stretchy and expandable, yet taking up no space unless you pull apart
the components that it is sticking to. For example, by putting horizontal glue between
two components in a left-to-right box, you make any extra space go between those
components, instead of to the right of all the components. Here is an example of
making the space in a left-to-right box go between two components, instead of to the
right of the components:
container.add(firstComponent);
container.add(Box.createHorizontalGlue());
container.add(secondComponent);

Custom Box.Filler
Use this to specify a component with whatever minimum, preferred, and maximum
sizes you want. For example, to create some filler in a left-to-right layout that puts at
least 5 pixels between two components and ensures that the container has a minimum
height of 100 pixels, you could use this code:
container.add(firstComponent);
Dimension minSize = new Dimension(5, 100);
Dimension prefSize = new Dimension(5, 100);
Dimension maxSize = new Dimension(Short.MAX_VALUE, 100);
container.add(new Box.Filler(minSize, prefSize, maxSize));
container.add(secondComponent);

Fixing Alignment Problems

857
Two types of alignment problems sometimes occur with BoxLayout:

 A group of components all have the same alignment, but you want to change their
alignment to make them look better. For example, instead of having the centers of a
group of left-to-right buttons all in a line, you might want the bottoms of the buttons
to be aligned. Here is an example:

 Two or more components controlled by a BoxLayout have different default


alignments, which causes them to be mis-aligned. For example, as the following
shows, if a label and a panel are in a top-to-bottom box layout, the label's left edge is,
by default, aligned with the center of the panel.

In general, all the components controlled by a top-to-bottom BoxLayout object should have the same
X alignment. Similarly, all the components controlled by a left-to-right Boxlayout should generally
have the same Y alignment. You can set a JComponent's X alignment by invoking its
setAlignmentX method. An alternative available to all components is to override the
getAlignmentX method in a custom subclass of the component class. Similarly, you set the Y
alignment of a component by invoking the setAlignmentY method or by overriding
getAlignmentY.

Here is an example, taken from an application called BoxAlignmentDemo, of changing the Y


alignments of two buttons so that the bottoms of the buttons are aligned:

button1.setAlignmentY(Component.BOTTOM_ALIGNMENT);
button2.setAlignmentY(Component.BOTTOM_ALIGNMENT);

858
Click the Launch button to run BoxAlignmentDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

By default, most components have center X and Y alignment. However, buttons, combo boxes,
labels, and menu items have a different default X alignment value: LEFT_ALIGNMENT. The previous
picture shows what happens if you put a left-aligned component such as a label together with a
center-aligned component in a container controlled by a top-to-bottom BoxLayout.

The BoxAlignmentDemo program gives examples of fixing mismatched alignment problems.


Usually, it is as simple as making an offending button or label be center aligned. For example:

label.setAlignmentX(Component.CENTER_ALIGNMENT);

Specifying Component Sizes

As mentioned before, BoxLayout pays attention to a component's requested minimum, preferred, and
maximum sizes. While you are fine-tuning the layout, you might need to adjust these sizes.

Sometimes the need to adjust the size is obvious. For example, a button's maximum size is generally
the same as its preferred size. If you want the button to be drawn wider when additional space is
available, then you need to change its maximum size.

Sometimes, however, the need to adjust size is not so obvious. You might be getting unexpected
results with a box layout, and you might not know why. In this case, it is usually best to treat the
problem as an alignment problem first. If adjusting the alignments does not help, then you might
have a size problem. We'll discuss this further a bit later.

Note: Although BoxLayout pays attention to a component's maximum size, many layout managers
do not. For example, if you put a button in the bottom part of a BorderLayout, the button will
probably be wider than its preferred width, no matter what the button's maximum size is. BoxLayout,
on the other hand, never makes a button wider than its maximum size.

You can change the minimum, preferred, and maximum sizes in two ways:

 By invoking the appropriate setXxxSize method (which is defined by the


JComponent class). For example:
 comp.setMinimumSize(new Dimension(50, 25));
 comp.setPreferredSize(new Dimension(50, 25));
 comp.setMaximumSize(new Dimension(Short.MAX_VALUE,
 Short.MAX_VALUE));
 By overriding the appropriate getXxxSize method. For example:
 ...//in a subclass of a component class:
 public Dimension getMaximumSize() {
 size = getPreferredSize();
 size.width = Short.MAX_VALUE;
 return size;
 }

859
If you are running into trouble with a box layout and you have ruled out alignment problems, then
the trouble might well be size-related. For example, if the container controlled by the box layout is
taking up too much space, then one or more of the components in the container probably needs to
have its maximum size restricted.

You can use two techniques to track down size trouble in a box layout:

 Add a garish line border to the outside of the Swing components in question. This lets
you see what size they really are. For example:
 comp.setBorder(BorderFactory.createCompoundBorder(
 BorderFactory.createLineBorder(Color.red),
 comp.getBorder()));
 Use System.out.println to print the components' minimum, preferred, and
maximum sizes, and perhaps their bounds.

The Box Layout API

The following tables list the commonly used BoxLayout and Box constructors and methods. The API
for using box layouts falls into these categories:

 Creating BoxLayout objects


 Creating space fillers
 Other useful methods

Creating BoxLayout Objects


Constructor or Method Purpose
Creates a BoxLayout instance that controls the specified Container.
The integer argument specifies the axis along which the container's
BoxLayout(Container, components should be laid out. When the container has the default
int) component orientation, BoxLayout.LINE_AXIS specifies that the
components be laid out from left to right, and BoxLayout.PAGE_AXIS
specifies that the components be laid out from top to bottom.
Creates a Box — a container that uses a BoxLayout with the specified
Box(int) axis. As of release 1.3, Box extends JComponent. Before that, it was
implemented as a subclass of Container.
static Box
createHorizontalBox() Creates a Box that lays out its components from left to right.
(in Box)
static Box
createVerticalBox() Creates a Box that lays out its components from top to bottom.
(in Box)
Creating Space Fillers
These methods are defined in the Box class.
Constructor or Method Purpose
Component
createRigidArea(Dimension) Create a rigid component.
Component
createHorizontalGlue() Create a glue component. Horizontal glue and vertical glue
Component createVerticalGlue() can be very useful.
Component createGlue()

860
Component
createHorizontalStrut() Create a "strut" component. We recommend using rigid areas
Component instead of struts.
createVerticalStrut()
Creates a component with the specified minimum, preferred,
Box.Filler(Dimension, and maximum sizes (with the arguments specified in that
Dimension, Dimension) order). See the custom Box.Filler discussion, earlier in this
section, for details.
Other Useful Methods
Method Purpose
Change the minimum, preferred, and maximum sizes
void changeShape(Dimension,
Dimension, Dimension) (in Box.Filler) of the recipient Box.Filler object. The layout
changes accordingly.

Examples that Use Box Layouts

The following table lists some of the many examples that use box layouts.
Example Where Described Notes
BoxLayoutDemo2 This page Uses a box layout to create a centered column of
components.
BoxAlignmentDemo This page Demonstrates how to fix common alignment
problems.
BoxLayoutDemo This page Lets you play with alignments and maximum
sizes.
ListDialog This page A simple yet realistic example of using both a top-
to-bottom box layout and a left-to-right one. Uses
horizontal glue, rigid areas, and empty borders.
Also sets the X alignment of a component.
InternalFrameEventDemo How to Write an Uses a top-to-bottom layout to center buttons and
Internal Frame a scroll pane in an internal frame.
Listener
MenuGlueDemo Customizing Shows how to right-align a menu in the menu bar,
Menu Layout using a glue component.
MenuLayoutDemo Customizing Shows how to customize menu layout by changing
Menu Layout the menu bar to use a top-to-bottom box layout,
and the popup menu to use a left-to-right box
layout.
ConversionPanel.java in How to Use Aligns two components in different box-layout-
the Converter demo Panels controlled containers by setting the components'
widths to be the same, and their containers' widths
to be the same.

How to Use CardLayout


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
861
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

The following figure represents a snapshot of an application that uses the CardLayout class to switch
between two panels.

Click the Launch button to run CardLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

The complete code of this demo is in the CardLayoutDemo.java file.

The CardLayout class manages two or more components (usually JPanel instances) that share the
same display space. When using the CardLayout class, let the user choose between the components
by using a combo box. The CardLayoutDemo application is an example to illustrate this feature.

Another way to accomplish the same task is to use a tabbed pane. The following picture shows a
tabbed pane version of the preceding example:

Because a tabbed pane provides its own GUI, using a tabbed pane is simpler than using the
CardLayout class. For example, implementing the preceding example using a tabbed pane results in
a program with fewer lines of code.

Click the Launch button to run TabDemo using Java™ Web Start (download JDK 6). Alternatively,
to compile and run the example yourself, consult the example index.

The complete code of this demo is in the TabDemo.java file.

Conceptually, each component that a CardLayout manages is like a playing card or trading card in a
stack, where only the top card is visible at any time. You can choose the card that is showing in any
of the following ways:

 By asking for either the first or last card, in the order it was added to the container
 By flipping through the deck backwards or forwards
 By specifying a card with a specific name

862
The CardLayoutDemo class uses the last scheme.

The following code snippet from the CardLayoutDemo.java application creates the CardLayout
object and the components it manages.

//Where instance variables are declared:


JPanel cards;
final static String BUTTONPANEL = "Card with JButtons";
final static String TEXTPANEL = "Card with JTextField";

//Where the components controlled by the CardLayout are initialized:


//Create the "cards".
JPanel card1 = new JPanel();
...
JPanel card2 = new JPanel();
...

//Create the panel that contains the "cards".


cards = new JPanel(new CardLayout());
cards.add(card1, BUTTONPANEL);
cards.add(card2, TEXTPANEL);
To add a component to a container that a CardLayout object manages, specify a string that identifies
the component being added. For example, in this demo, the first panel has the string "Card with
JButtons", and the second panel has the string "Card with JTextField". In this demo those
strings are also used in the combo box.

To choose which component a CardLayout object shows, put additional code in your code example:

//Where the GUI is assembled:


//Put the JComboBox in a JPanel to get a nicer look.
JPanel comboBoxPane = new JPanel(); //use FlowLayout
String comboBoxItems[] = { BUTTONPANEL, TEXTPANEL };
JComboBox cb = new JComboBox(comboBoxItems);
cb.setEditable(false);
cb.addItemListener(this);
comboBoxPane.add(cb);
...
pane.add(comboBoxPane, BorderLayout.PAGE_START);
pane.add(cards, BorderLayout.CENTER);
...

//Method came from the ItemListener class implementation,


//contains functionality to process the combo box item selecting
public void itemStateChanged(ItemEvent evt) {
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, (String)evt.getItem());
}
This example shows that to use the show method of the CardLayout class, you must set the currently
visible component. The first argument in the show method is the container the CardLayout controls
— that is, the container of the components the CardLayout manages. The second argument is the
string that identifies the component to show. This string is the same string that was used when adding
the component to the container.

The CardLayout API

The following table lists the CardLayout class methods that are used to choose a component. For
each method, the first argument is the container for which the CardLayout is the layout manager (the
container of the cards the CardLayout controls).

863
Method Purpose
first (Container
parent) Flips to the first card of the container.
next (Container Flips to the next card of the container. If the currently visible card is the
parent) last one, this method flips to the first card in the layout.
previous (Container Flips to the previous card of the container. If the currently visible card is
parent) the first one, this method flips to the last card in the layout.
last (Container
parent) Flips to the last card of the container.
show (Container Flips to the component that was added to this layout with the specified
parent, String name) name, using the addLayoutComponent method.

Examples that Use CardLayout

Only one example in this trail uses CardLayout, and this is the CardLayoutDemo. Generally, our
examples use tabbed panes instead of CardLayout, since a tabbed pane provides its own GUI.

How to Use FlowLayout


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

The FlowLayout class provides a very simple layout manager that is used, by default, by the JPanel
objects. The following figure represents a snapshot of an application that uses the flow layout:

Click the Launch button to run FlowLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

The complete code of this demo is in the FlowLayoutDemo.java file.

The FlowLayout class puts components in a row, sized at their preferred size. If the horizontal space
in the container is too small to put all the components in one row, the FlowLayout class uses
multiple rows. If the container is wider than necessary for a row of components, the row is, by
default, centered horizontally within the container. To specify that the row is to aligned either to the
left or right, use a FlowLayout constructor that takes an alignment argument. Another constructor of
the FlowLayout class specifies how much vertical or horizontal padding is put around the
components.

The code snippet below creates a FlowLayout object and the components it manages.

864
FlowLayout experimentLayout = new FlowLayout();

...

compsToExperiment.setLayout(experimentLayout);

compsToExperiment.add(new JButton("Button 1"));


compsToExperiment.add(new JButton("Button 2"));
compsToExperiment.add(new JButton("Button 3"));
compsToExperiment.add(new JButton("Long-Named Button 4"));
compsToExperiment.add(new JButton("5"));

Select either the Left to Right or Right to Left option and click the Apply orientation button to set up
the component's orientation. The following code snippet applies the Left to Right components
orientation to the experimentLayout.

compsToExperiment.setComponentOrientation(
ComponentOrientation.LEFT_TO_RIGHT);

The FlowLayout API

The following table lists constructors of the FlowLayout class.

Constructor Purpose
FlowLayout()
Constructs a new FlowLayout object with a centered alignment and
horizontal and vertical gaps with the default size of 5 pixels.
Creates a new flow layout manager with the indicated alignment and
horizontal and vertical gaps with the default size of 5 pixels. The alignment
argument can be FlowLayout.LEADING, FlowLayout.CENTER, or
FlowLayout(int
align) FlowLayout.TRAILING. When the FlowLayout object controls a container
with a left-to right component orientation (the default), the LEADING value
specifies the components to be left-aligned and the TRAILING value specifies
the components to be right-aligned.
FlowLayout (int Creates a new flow layout manager with the indicated alignment and the
align, int hgap, indicated horizontal and vertical gaps. The hgap and vgap arguments specify
int vgap) the number of pixels to put between components.

Examples that Use FlowLayout

The following table lists code examples that use the FlowLayout class and provides links to related
sections.
Example Where Described Notes
FlowLayoutDemo This page Sets up a content pane to use FlowLayout. If you set the
RIGHT_TO_LEFT constant to true and recompile, you can
see how FlowLayout handles a container that has a right-to-
left component orientation.
CardLayoutDemo How to Use Centers a component nicely in the top part of a
CardLayout BorderLayout, and puts the component in a JPanel that
uses a FlowLayout.
ButtonDemo How to Use Buttons, Uses the default FlowLayout of a JPanel.
Check Boxes, and

865
Radio Buttons
TextInputDemo How to Use Uses a panel with a right-aligned FlowLayout presenting
Formatted Text two buttons.
Fields

How to Use GridBagLayout


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

Here is a picture of an example that uses GridBagLayout.

Click the Launch button to run GridBagLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

The code for GridBagDemo is in GridBagLayoutDemo.java.

GridBagLayout is one of the most flexible — and complex — layout managers the Java platform
provides. A GridBagLayout places components in a grid of rows and columns, allowing specified
components to span multiple rows or columns. Not all rows necessarily have the same height.
Similarly, not all columns necessarily have the same width. Essentially, GridBagLayout places
components in rectangles (cells) in a grid, and then uses the components' preferred sizes to determine
how big the cells should be.

The following figure shows the grid for the preceding applet. As you can see, the grid has three rows
and three columns. The button in the second row spans all the columns; the button in the third row
spans the two right columns.

866
If you enlarge the window as shown in the following figure, you will notice that the bottom row,
which contains Button 5, gets all the new vertical space. The new horizontal space is split evenly
among all the columns. This resizing behavior is based on weights the program assigns to individual
components in the GridBagLayout. You will also notice that each component takes up all the
available horizontal space — but not (as you can see with button 5) all the available vertical space.
This behavior is also specified by the program.

The way the program specifies the size and position characteristics of its components is by
specifying constraints for each component. The preferred approach to set constraints on a component
is to use the Container.add variant, passing it a GridBagConstraints object, as demonstrated in
the next sections.

The following sections explain the constraints you can set and provide examples.

Specifying Constraints

The following code is typical of what goes in a container that uses a GridBagLayout. You will see a
more detailed example in the next section.
JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();

//For each component to be added to this container:


//...Create the component...
//...Set instance variables in the GridBagConstraints instance...
pane.add(theComponent, c);
As you might have guessed from the above example, it is possible to reuse the same
GridBagConstraints instance for multiple components, even if the components have different
constraints. However, it is recommended that you do not reuse GridBagConstraints, as this can
very easily lead to you introducing subtle bugs if you forget to reset the fields for each new instance.

Note: The following discussion assumes that the GridBagLayout controls a container that has a left-
to-right component orientation.

867
You can set the following GridBagConstraints instance variables:

gridx, gridy
Specify the row and column at the upper left of the component. The leftmost column
has address gridx=0 and the top row has address gridy=0. Use
GridBagConstraints.RELATIVE (the default value) to specify that the component be
placed just to the right of (for gridx) or just below (for gridy) the component that
was added to the container just before this component was added. We recommend
specifying the gridx and gridy values for each component rather than just using
GridBagConstraints.RELATIVE; this tends to result in more predictable layouts.
gridwidth, gridheight
Specify the number of columns (for gridwidth) or rows (for gridheight) in the
component's display area. These constraints specify the number of cells the
component uses, not the number of pixels it uses. The default value is 1. Use
GridBagConstraints.REMAINDER to specify that the component be the last one in its
row (for gridwidth) or column (for gridheight). Use
GridBagConstraints.RELATIVE to specify that the component be the next to last one
in its row (for gridwidth) or column (for gridheight). We recommend specifying
the gridwidth and gridheight values for each component rather than just using
GridBagConstraints.RELATIVE and GridBagConstraints.REMAINDER; this tends
to result in more predictable layouts.

Note: GridBagLayout does not allow components to span multiple rows unless the
component is in the leftmost column or you have specified positive gridx and gridy
values for the component.

fill
Used when the component's display area is larger than the component's requested size
to determine whether and how to resize the component. Valid values (defined as
GridBagConstraints constants) include NONE (the default), HORIZONTAL (make the
component wide enough to fill its display area horizontally, but do not change its
height), VERTICAL (make the component tall enough to fill its display area vertically,
but do not change its width), and BOTH (make the component fill its display area
entirely).
ipadx, ipady
Specifies the internal padding: how much to add to the size of the component. The
default value is zero. The width of the component will be at least its minimum width
plus ipadx*2 pixels, since the padding applies to both sides of the component.
Similarly, the height of the component will be at least its minimum height plus
ipady*2 pixels.
insets
Specifies the external padding of the component -- the minimum amount of space
between the component and the edges of its display area. The value is specified as an
Insets object. By default, each component has no external padding.
anchor
Used when the component is smaller than its display area to determine where (within
the area) to place the component. Valid values (defined as GridBagConstraints
constants) are CENTER (the default), PAGE_START, PAGE_END, LINE_START, LINE_END,
FIRST_LINE_START, FIRST_LINE_END, LAST_LINE_END, and LAST_LINE_START.

868
Here is a picture of how these values are interpreted in a container that has the default,
left-to-right component orientation.

-------------------------------------------------
|FIRST_LINE_START PAGE_START FIRST_LINE_END|
| |
| |
|LINE_START CENTER LINE_END|
| |
| |
|LAST_LINE_START PAGE_END LAST_LINE_END|
-------------------------------------------------

Version note: The PAGE_* and *LINE_* constants were introduced in 1.4.
Previous releases require values named after points of the compass. For
example, NORTHEAST indicates the top-right part of the display area. We
recommend that you use the new constants, instead, since they enable easier
localization.

weightx, weighty
Specifying weights is an art that can have a significant impact on the appearance of
the components a GridBagLayout controls. Weights are used to determine how to
distribute space among columns (weightx) and among rows (weighty); this is
important for specifying resizing behavior.

Unless you specify at least one non-zero value for weightx or weighty, all the
components clump together in the center of their container. This is because when the
weight is 0.0 (the default), the GridBagLayout puts any extra space between its grid
of cells and the edges of the container.

Generally weights are specified with 0.0 and 1.0 as the extremes: the numbers in
between are used as necessary. Larger numbers indicate that the component's row or
column should get more space. For each column, the weight is related to the highest
weightx specified for a component within that column, with each multicolumn
component's weight being split somehow between the columns the component is in.
Similarly, each row's weight is related to the highest weighty specified for a
component within that row. Extra space tends to go toward the rightmost column and
bottom row.

The next section discusses constraints in depth, in the context of explaining how the example
program works.

The Example Explained

Here, again, is a picture of the GridBagLayoutDemo application.

869
Click the Launch button to run GridBagLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

The following code creates the GridBagLayout and the components it manages. You can find the
entire source file in GridBagLayoutDemo.java.

JButton button;
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if (shouldFill) {
//natural height, maximum width
c.fill = GridBagConstraints.HORIZONTAL;
}

button = new JButton("Button 1");


if (shouldWeightX) {
c.weightx = 0.5;
}
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
pane.add(button, c);

button = new JButton("Button 2");


c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 1;
c.gridy = 0;
pane.add(button, c);

button = new JButton("Button 3");


c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 2;
c.gridy = 0;
pane.add(button, c);

button = new JButton("Long-Named Button 4");


c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 40; //make this component tall
c.weightx = 0.0;
c.gridwidth = 3;
c.gridx = 0;
c.gridy = 1;
pane.add(button, c);

button = new JButton("5");


c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 0; //reset to default
c.weighty = 1.0; //request any extra vertical space

870
c.anchor = GridBagConstraints.PAGE_END; //bottom of space
c.insets = new Insets(10,0,0,0); //top padding
c.gridx = 1; //aligned with button 2
c.gridwidth = 2; //2 columns wide
c.gridy = 2; //third row
pane.add(button, c);

This example uses one GridBagConstraints instance for all the components the GridBagLayout
manages, however in real-life situations it is recommended that you do not reuse
GridBagConstraints, as this can very easily lead to you introducing subtle bugs if you forget to
reset the fields for each new instance. Just before each component is added to the container, the code
sets (or resets to default values) the appropriate instance variables in the GridBagConstraints
object. It then adds the component to its container, specifying the GridBagConstraints object as the
second argument to the add method.

For example, to make button 4 be extra tall, the example has this code:

c.ipady = 40;
And before setting the constraints of the next component, the code resets the value of ipady to the
default:
c.ipady = 0;

If a component's display area is larger than the component itself, then you can specify whereabouts
in the display area the component will be displayed by using the GridBagConstraints.anchor
constraint. The anchor constraint's values can be absolute (north, south, east, west, and so on), or
orientation-relative (at start of page, at end of line, at the start of the first line, and so on), or relative
to the component's baseline. For a full list of the possible values of the anchor constraint, including
baseline-relative values,see the API documentation for GridBagConstraints.anchor. You can see
in the code extract above that Button 5 specifies that it should be displayed at the end of the display
area by setting an anchor at GridBagConstraints.PAGE_END.

Note: The Tutorial's examples used to specify the constraints object a different way, which you
might see in other programs as well. Rather than specifying the constraints with the add method, our
examples used to invoke the setConstraints method on the GridBagLayout object. For example:
GridBagLayout gridbag = new GridBagLayout();
pane.setLayout(gridbag);
...
gridbag.setConstraints(button, c);
pane.add(button);
However, we recommend you use the Container.add method since it makes for cleaner code than if
you were to use setConstraints.

Here is a table that shows all the constraints for each component in GridBagLayoutDemo's content
pane. Values that are not the default are marked in boldface. Values that are different from those in
the previous table entry are marked in italics.
Component Constraints
All components ipadx = 0
fill = GridBagConstraints.HORIZONTAL

Button 1 ipady = 0
weightx = 0.5
weighty = 0.0
gridwidth = 1
anchor = GridBagConstraints.CENTER

871
insets = new Insets(0,0,0,0)
gridx = 0
gridy = 0

Button 2 weightx = 0.5


gridx = 1
gridy = 0
Button 3 weightx = 0.5
gridx = 2
gridy = 0
Button 4 ipady = 40
weightx = 0.0
gridwidth = 3
gridx = 0
gridy = 1
Button 5 ipady = 0
weightx = 0.0
weighty = 1.0
anchor = GridBagConstraints.PAGE_END
insets = new Insets(10,0,0,0)
gridwidth = 2
gridx = 1
gridy = 2

GridBagLayoutDemo has two components that span multiple columns (buttons 4 and 5). To make
button 4 tall, we added internal padding (ipady) to it. To put space between buttons 4 and 5, we used
insets to add a minimum of 10 pixels above button 5, and we made button 5 hug the bottom edge of
its cell.

All the components in the pane container are as wide as possible, given the cells that they occupy.
The program accomplishes this by setting the GridBagConstraints fill instance variable to
GridBagConstraints.HORIZONTAL, leaving it at that setting for all the components. If the program
did not specify the fill, the buttons would be at their natural width, like this:

When you enlarge GridBagLayoutDemo's window, the columns grow proportionately. This is
because each component in the first row, where each component is one column wide, has weightx =
1.0. The actual value of these components' weightx is unimportant. What matters is that all the
components, and consequently, all the columns, have an equal weight that is greater than 0. If no
component managed by the GridBagLayout had weightx set, then when the components' container
was made wider, the components would stay clumped together in the center of the container, like
this:

872
If the container is given a size that is smaller or bigger than the prefered size, then any space is
distributed according to the GridBagContainer weights.

Note that if you enlarge the window, the last row is the only one that gets taller. This is because only
button 5 has weighty greater than zero.

The GridBagLayout API

The GridBagLayout and GridBagConstraints classes each have only one constructor, with no
arguments. Instead of invoking methods on a GridBagConstraints object, you manipulate its
instance variables, as described in Specifying Constraints. Generally, the only method you invoke on
a GridBagLayout object is setConstraints, as demonstrated in The Example Explained.

Examples that Use GridBagLayout

You can find examples of using GridBagLayout throughout this tutorial. The following table lists a
few.
Example Where Described Notes
GridBagLayoutDemo This section Uses many features — weights, insets, internal padding,
horizontal fill, exact cell positioning, multi-column cells,
and anchoring (component positioning within a cell).
TextSamplerDemo Using Text Aligns two pairs of labels and text fields, plus adds a label
Components across the full width of the container.
ContainerEventDemo How to Write a Positions five components within a container, using
Container Listener weights, fill, and relative positioning.

873
How to Use GridLayout
Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

The following figure represents a snapshot of an application that uses the GridLayout class.

Click the Launch button to run GridLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

The complete code of this demo is in the GridLayoutDemo.java file.

A GridLayout object places components in a grid of cells. Each component takes all the available
space within its cell, and each cell is exactly the same size. If the GridLayoutDemo window is
resized, the GridLayout object changes the cell size so that the cells are as large as possible, given
the space available to the container.

The code snippet below creates the GridLayout object and the components it manages.

GridLayout experimentLayout = new GridLayout(0,2);

...

compsToExperiment.setLayout(experimentLayout);

compsToExperiment.add(new JButton("Button 1"));


compsToExperiment.add(new JButton("Button 2"));
compsToExperiment.add(new JButton("Button 3"));
compsToExperiment.add(new JButton("Long-Named Button 4"));
compsToExperiment.add(new JButton("5"));
The constructor of the GridLayout class creates an instance that has two columns and as many rows
as necessary.

Use combo boxes to set up how much vertical or horizontal padding is put around the components.
Then click the Apply gaps button. The following code snippet shows how your selection is processed
by using the setVgap and setHgap methods of the GridLayout class:

applyButton.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){

874
//Get the horizontal gap value
String horGap = (String)horGapComboBox.getSelectedItem();
//Get the vertical gap value
String verGap = (String)verGapComboBox.getSelectedItem();
//Set up the horizontal gap value
experimentLayout.setHgap(Integer.parseInt(horGap));
//Set up the vertical gap value
experimentLayout.setVgap(Integer.parseInt(verGap));
//Set up the layout of the buttons
experimentLayout.layoutContainer(compsToExperiment);
}
});

The GridLayout API

The following table lists constructors of the GridLayout class that specify the number of rows and
columns.

The GridLayout class constructors


Constructor Purpose
Creates a grid layout with the specified number of rows and columns.
GridLayout(int rows, All components in the layout are given equal size. One, but not both,
int cols) of rows and cols can be zero, which means that any number of
objects can be placed in a row or in a column.
Creates a grid layout with the specified number of rows and columns.
GridLayout(int rows,
int cols, int hgap, int
In addition, the horizontal and vertical gaps are set to the specified
vgap) values. Horizontal gaps are places between each of columns. Vertical
gaps are placed between each of the rows.

The GridLayout class has two constructors:

Examples that Use GridLayout

The following table lists code examples that use the GridLayout class and provides links to related
sections.
Example Where Described Notes
GridLayoutDemo This page Uses a 2-column grid.
ComboBoxDemo2 How to Use Combo One of many examples that use a 1x1 grid to make a
Boxes component as large as possible.
LabelDemo How to Use Labels Uses a 3-row grid.
DragPictureDemo Introduction to DnD Uses a 4-row grid to present 12 components that display
photographs.

875
How to Use GroupLayout
roupLayout is a layout manager that was developed for GUI builders such as Matisse, the GUI
builder provided with the NetBeans IDE. Although the layout manager was originally designed to
suit the GUI builder needs, it also works well for manual coding. This discussion will teach you how
GroupLayout works and show you how you can use GroupLayout to build GUIs, whether you
choose to use a GUI builder like Matisse or write your own code.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

Design Principle: Independent Dimensions

GroupLayout works with the horizontal and vertical layouts separately. The layout is defined for
each dimension independently. You do not need to worry about the vertical dimension when
defining the horizontal layout, and vice versa, as the layout along each axis is totally independent of
the layout along the other axis.

When focusing on just one dimension, you only have to solve half the problem at one time. This is
easier than handling both dimensions at once. This means, of course, that each component needs to
be defined twice in the layout. If you forget to do this, GroupLayout will generate an exception.

Layout Organization: Hierarchical Groups

GroupLayout uses two types of arrangements -- sequential and parallel, combined with hierarchical
composition.

1. With sequential arrangement, the components are simply placed one after another, just like
BoxLayout or FlowLayout would do along one axis. The position of each component is
defined as being relative to the preceding component.
2. The second way places the components in parallel—on top of each other in the same space.
They can be baseline-, top-, or bottom-aligned along the vertical axis. Along the horizontal
axis, they can be left-, right-, or center-aligned if the components are not all the same size.

Usually, components placed in parallel in one dimension are in a sequence in the other, so that they
do not overlap.

What makes these two arrangements powerful is that they can be nested hierarchically. For this
purpose GroupLayout defines layout groups. A group is either sequential or parallel and may
contain components, other groups, and gaps (discussed below).

The size of a sequential group is the sum of the sizes of the contained elements, and the size of a
parallel group corresponds to the size of the largest element (although, depending on the elements
and where the baseline lands, the size of a baseline-aligned group may be a bit larger than the largest
element).

876
Defining a layout means defining how the components should be grouped by combining the
sequential and parallel arrangements.

Let us use a simple example to see how it works in practice.

An Example

Let us start with something simple, just three components in a row:

We will express this layout using groups. Starting with the horizontal axis it is easy to see there is a
sequential group of 3 components arranged from left to right. Along the vertical axis there is a
parallel group of the same 3 components with the same location, size, and baseline:

In pseudo code, the layout specification might look like this (the real code is in the Writing Code
section below):

horizontal layout = sequential group { c1, c2, c3 }


vertical layout = parallel group (BASELINE) { c1, c2, c3 }

This illustrates a principle mentioned earlier: components grouped sequentially in one dimension
usually form a parallel group in the other dimension.

Now let us add one more component, C4, left-aligned with C3:

Along the horizontal axis the new component occupies the same horizontal space as C3 so that it
forms a parallel group with C3. Along the vertical axis C4 forms a sequential group with the original
parallel group of the three components.

In pseudo code, the layout specification now looks like this:

horizontal layout = sequential group { c1, c2, parallel group (LEFT) { c3, c4 } }
vertical layout = sequential group { parallel group (BASELINE) { c1, c2, c3 }, c4
}

877
Now you understand the most important aspects of designing layouts with GroupLayout. There are
just a few more details to explain: how to add gaps, how to define size and resize behavior, how to
define justified layout, and how to write real code.

Gaps

A gap can be thought of as an invisible component of a certain size. Gaps of arbitrary size can be
added to groups just like components or other groups. Using gaps you can precisely control the
distance between components or from the container border.

GroupLayout also defines automatic gaps that correspond to preferred distances between
neighboring components (or between a component and container border). The size of such a gap is
computed dynamically based on the look and feel the application is using (the LayoutStyle class is
used for this). There are two advantages to using automatic (preferred) gaps: you do not have to
specify the pixel sizes of the gaps, and they automatically adjust to the look and feel the UI runs
with, reflecting the actual look and feel guidelines.

GroupLayout distinguishes between (a) the preferred gap between two components and (b) the
preferred gap between a component and the container border. There are corresponding methods in
the GroupLayout API for adding these gaps (addPreferredGap and addContainerGap). There are
three types of component gaps: related, unrelated and indented. The
LayoutStyle.ComponentPlacement enum defines corresponding constants to be used as parameters
of the addPreferredGap method: RELATED, UNRELATED and INDENT. The difference between related
and unrelated gaps is just in size—the distance between unrelated components is a bit bigger.
Indented represents a preferred horizontal distance of two components when one of them is
positioned underneath the second with an indent.

As mentioned above, GroupLayout can insert gaps automatically—if you do not add your own gaps
explicitly, it adds the related preferred gaps for you. This is not the default behavior, however. You
have to turn this feature on by invoking setAutoCreateGaps(true) and
setAutoCreateContainerGaps(true) on the GroupLayout. Then you will get correct spacing
automatically.

Writing Code

Now, let us take a look at the actual code to create the layout described above.

Let us assume we have a container named panel and the same four components already presented
(c1, c2, c3, and c4). First, we create a new GroupLayout object and associate it with the panel:

GroupLayout layout = new GroupLayout(panel);


panel.setLayout(layout);

We specify automatic gap insertion:

layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);

878
Then, we define the groups and add the components. We establish a root group for each dimension
using the setHorizontalGroup and setVerticalGroup methods. Groups are created via
createSequentialGroup and createParallelGroup methods. Components are added to groups by
using the addComponent method.

layout.setHorizontalGroup(
layout.createSequentialGroup()
.addComponent(c1)
.addComponent(c2)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(c3)
.addComponent(c4))
);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(c1)
.addComponent(c2)
.addComponent(c3))
.addComponent(c4)
);

You can specify the alignment for parallel groups. It can be one of the following constants defined in
the GroupLayout.Alignment enum: LEADING, TRAILING, CENTER, and BASELINE. These constants
are used for both dimensions and depend on whether the component orientation is left-to-right or
right-to-left (top-to-bottom or bottom-to-top). For example, if the horizontal (vertical) component
orientation is left-to-right (top-to-bottom) LEADING means left (top) while TRAILING means right
(bottom). CENTER means "centered" in both dimensions. If you do not specify the alignment,
LEADING will be used. The BASELINE alignment is valid only in the vertical dimension.

Note: Alignment in the layout of a group only has meaning for components of different sizes.
Components of the same size will be automatically aligned for each of the GroupLayout.Alignment
constants.

Some comments about the code:

 You do not need to add the component directly to the container—that is done for you
implicitly when using one of the addComponent methods.
 Note the chained calls of the addComponent methods used to fill the groups. The
addComponent method always returns the group on which it is called. Thanks to this you do
not need to use local variables to hold the groups.
 It is a good idea to indent the code so it is easy to see the hierarchical structure of the groups.
Give each component a new line, add one level of indent for each new group in the hierarchy.
A good source editor will help you with pairing the parenthesis to close the createXXXGroup
methods. By following these simple rules, it is easier to add a new component or remove an
existing one.

Component Size and Resizability

There is no limit on the number of resizable components in a layout.

879
The size of each component in a GroupLayout is constrained by three values; minimum size,
preferred size and maximum size. These sizes control how the component resizes within the layout.
The GroupLayout.addComponent(...) method allows the size constraints to be specified.

If not specified explicitly, the layout asks the component for its default sizes (by using the
component's getMinimumSize(), getPreferredSize() and getMaximumSize() methods). You do
not need to specify anything for most of the components, like making JTextField resizable or
JButton fixed, because the components themselves have the desired resizing behavior as default. On
the other hand you can override the default behavior. For example you can make a JTextField fixed
or JButton resizable.

GroupLayout defines constants that provide precise control over resize behavior. They can be used
as parameters in the addComponent(Component comp, int min, int pref, int max) method.
Here are two examples:

1. To force a component to be resizable (allow shrinking and growing):


2. group.addComponent(component, 0, GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE) ...

This allows the component to resize between zero size (minimum) to any size
(Short.MAX_VALUE as maximum size means "infinite"). If we wanted the component not to
shrink below its default minimum size, we would use GroupLayout.DEFAULT_SIZE instead
of 0 in the second parameter.

3. To make a component fixed size (suppress resizing):


4. group.addComponent(component, GroupLayout.PREFERRED_SIZE,
GroupLayout.DEFAULT_SIZE,
5. GroupLayout.PREFERRED_SIZE) ...

In these examples the initial size of the component is not altered, its default size is the component's
preferred size. If we wanted a specific size for the component, we would specify it in the second
parameter instead of using GroupLayout.DEFAULT_SIZE.

Resizable gaps

Specifying size and resizability applies to gaps as well, including the preferred ones. For example,
you can specify a preferred gap between two components that acts like a spring pushing the
components away from each other (to the opposite sides of the container). The preferred distance of
the two components is only used as the minimum size of the gap. See the following snippet:

layout.createSequentialGroup()
.addComponent(c1)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED,
GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(c2);

Sizing in Parallel Groups

Resizable elements placed in a parallel group are stretched to fill the space of the group determined
by the largest element in the group, so they end up aligned with the same size. GroupLayout also
provides control over whether the enclosing parallel group itself should resize. If group resizing is
suppressed, it prevents the contained elements from growing over the preferred size of the group.

880
This way you can make a block of components align on both sides, or constrain individual
components to have the same size.

Let us try to achieve the same size for two components from our example (c3 and c4 in the
horizontal dimension):

layout.createParallelGroup(GroupLayout.Alignment.LEADING, false)
.addComponent(c3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
.addComponent(c4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE);

The underlying mechanism works as follows:

1. The size of the parallel group is set to the preferred size of the largest element; so to the
preferred size of c4 in our example.
2. Resizable elements are stretched to the size of the group. In our example, only c3 is
effectively stretched, the size of c4 already corresponds to the size of the group.

As a result, c3 and c4 would have the same width. The components would not resize further because
the parallel group itself is not resizable (the second parameter of the createParallelGroup method,
above, is false).

Question for attentive readers: Why do we define both components in the parallel group as resizable
in this example? It seems enough to have just c3 resizable since c4 is not stretched anyway...

The answer is: because of platform and localization independence. Otherwise we would have to rely
on that c4 component always being bigger than c3. But this may change when the application runs
on different platform or is translated to another language. By having both components resizeable
they adjust to each other, no matter which one is bigger at a given moment.

Making Components the Same Size

The previous case is special because the components are in the same parallel group. But what if we
wanted unrelated components to have the same size? Clearly, the same size cannot always be
ensured by grouping. The OK and Cancel buttons in a row at the bottom of a dialog are a good
example. For this purpose GroupLayout provides a linkSize method. This method allows the size
of arbitrary components to be linked regardless of where they are placed. The resulting size of the
linked components is set according to the largest component. For example:

layout.linkSize(SwingConstants.HORIZONTAL, c3, c4);

In this example, the size is linked selectively for the horizontal dimension.

881
Runtime Changes to Your GUI

There are two important methods that you can use to make changes to your GUI at runtime,
replace() and setHonorsVisibility(). Using these two methods, you can exchange components
or change the visibility of components at runtime and have the GUI rearrange itself accordingly.

replace(Component existingComponent, Component newComponent) replaces an existing


component with a new one. One of the common operations needed for dynamic layouts is the ability
to replace components like this. For example, perhaps a check box toggles between a component
displaying a graph or a tree. GroupLayout makes this scenario simple with the replace() method.
You can swap components without recreating all the groups.

Another common operation in user interfaces is to dynamically change the visibility of components.
Perhaps components are shown only as a user completes earlier portions of a form. To avoid
components shuffling around in such a scenario, space should be taken up regardless of the visibility
of the components. GroupLayout offers two ways to configure how invisible components are treated.
The setHonorsVisibility(boolean) method globally sets how invisible components are handled.
A value of true, the default, indicates invisible components are treated as if they are not there. On the
other hand, a value of false provides space for invisible components, treating them as though they
were visible. The setHonorsVisibility(Component,Boolean) method can be used to configure
the behavior at the level of a specific component. To determine how visibility is handled,
GroupLayout first checks if a value has been specified for the Component, if not, it checks the
setting of the global property.

Some history: GroupLayout in the Java Standard Edition 6 consists of three distinct bodies of work:
the ability to get the baseline for a component, the ability to get the preferred gap between
components (LayoutStyle), and GroupLayout. This work was originally done as an open source
project at http://swing-layout.dev.java.net

NetBeans 5.0 supports GroupLayout by way of the swing-layout project. Because of the success of
this work, all three portions have been rolled into GroupLayout in Java Standard Edition version 6.
The main difference between the GroupLayout in Java SE 6 and swing-layout is in the package
name and method names. NetBeans 5.5 provides the ability to target either the GroupLayout in Java
SE 6, or the GroupLayout in swing-layout. Which version NetBeans targets is determined by the
version of the Java platform your project targets. A project targeting Java SE 6 uses the
GroupLayout in Java SE, otherwise GroupLayout in swing-layout is used.

A GroupLayout Example
Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

As an example of GUI creation with GroupLayout, let us create a layout for this "Find" dialog box:

882
Horizontal layout

Examining the horizontal dimension from left to right, we can see there are 3 groups in a sequence.
The first one is actually not a group, just a component -- the label. The second one is a group
containing the text field and the check boxes (we will decompose it later). And the third is a group of
the two buttons. As illustrated here:

Let us sketch out the sequential group in code. Note that GroupLayout.Alignment.LEADING
corresponds to left alignment in the horizontal dimension. Also note we do not specify gaps,
assuming the gap auto-insertion feature is turned on.

layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(label)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING))
);

Now let us decompose the group in the middle. This is the hardest one. There is a text field in
parallel with a sequence of two parallel groups each containing two check boxes. See the following
illustration:

Let us add the corresponding code:

layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(label)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(textField)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(caseCheckBox)
.addComponent(wholeCheckBox))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(wrapCheckBox)

883
.addComponent(backCheckBox))))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING))
);

We want the text field to be resizable, but that happens automatically since JTextField returns the
right maximum size by default.

The remaining group on the right is trivial: it contains just two buttons. Here is the code:

layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(label)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(textField)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(caseCheckBox)
.addComponent(wholeCheckBox))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(wrapCheckBox)
.addComponent(backCheckBox))))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(findButton)
.addComponent(cancelButton))
);

And finally, we would like the buttons to be always the same size, so let us link them:

layout.linkSize(SwingConstants.HORIZONTAL, findButton, cancelButton);

Now we are done with the horizontal dimension. Let us switch to the vertical dimension. From now,
we will only need to think about the y axis.

Vertical layout

In the vertical dimension, we examine the layout from top to bottom. We definitely want all the
components on the first line aligned on the baseline. So along the vertical axis there is a sequence of
the baseline group, followed by a group of the remaining components. See the following picture.

Let us sketch out the code. First, we need to define two parallel groups. Note that
GroupLayout.Alignment.LEADING corresponds to the top alignment in the vertical dimension.

layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING))
);

We can fill the baseline group right away:

layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)

884
.addComponent(label)
.addComponent(textField)
.addComponent(findButton))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING))
);

Now let us look at the bottom group. Note the Cancel button is not on a shared baseline with the
check boxes; it is aligned at the top. So the second parallel group comprises the button and a
sequential group of two baseline groups with check boxes:

The corresponding code looks as follows:

layout.setVerticalGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(label)
.addComponent(textField)
.addComponent(findButton))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(caseCheckBox)
.addComponent(wrapCheckBox))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(wholeCheckBox)
.addComponent(backCheckBox)))
.addComponent(cancelButton))
);

So, we have created a complete layout, including resize behavior, without specifying a single number
in pixels—a true cross platform layout. Note that we do not need to specify gaps between
components, we get correct spacing automatically and according to the look and feel guidelines.
Here is the complete code for the Find dialog's layout:

GroupLayout layout = new GroupLayout(getContentPane());


getContentPane().setLayout(layout);
layout.setAutocreateGaps(true);
layout.setAutocreateContainerGaps(true);

layout.setHorizontalGroup(layout.createSequentialGroup()
.addComponent(label)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(textField)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(caseCheckBox)
.addComponent(wholeCheckBox))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(wrapCheckBox)
.addComponent(backCheckBox))))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(findButton)
.addComponent(cancelButton))
);
layout.linkSize(SwingConstants.HORIZONTAL, findButton, cancelButton);

layout.setVerticalGroup(layout.createSequentialGroup()

885
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(label)
.addComponent(textField)
.addComponent(findButton))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(caseCheckBox)
.addComponent(wrapCheckBox))
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(wholeCheckBox)
.addComponent(backCheckBox)))
.addComponent(cancelButton))
);

Here is the complete Find.java file. You can compile and run it. Try resizing the dialog
horizontally to see how the layout automatically adjusts to the new size.

How to Use SpringLayout


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

The SpringLayout class was added in JDK version 1.4 to support layout in GUI builders.
SpringLayout is a very flexible layout manager that can emulate many of the features of other
layout managers. SpringLayout is, however, very low-level and as such you really should only use
it with a GUI builder, rather than attempting to code a spring layout manager by hand.

This section begins with a simple example showing all the things you need to remember to create
your first spring layout — and what happens when you forget them! Later it presents utility methods
that let you lay out components in a couple of different types of grids.

Here are pictures of some of the layouts we will cover:

886
How Spring Layouts Work

Spring layouts do their job by defining directional relationships, or constraints, between the edges of
components. For example, you might define that the left edge of one component is a fixed distance
(5 pixels, say) from the right edge of another component.

In a SpringLayout, the position of each edge is dependent on the position of just one other edge. If a
constraint is subsequently added to create a new binding for an edge, the previous binding is
discarded and the edge remains dependent on a single edge.

Unlike many layout managers, SpringLayout does not automatically set the location of the
components it manages. If you hand-code a GUI that uses SpringLayout, remember to initialize
component locations by constraining the west/east and north/south locations. Depending on the
constraints you use, you may also need to set the size of the container explicitly.

Components define edge properties, which are connected by Spring instances. Each spring has four
properties — its minimum, preferred, and maximum values, and its actual (current) value. The
springs associated with each component are collected into a SpringLayout.Constraints object.

An instance of the Spring class holds three properties that characterize its behavior: the minimum,
preferred, and maximum values. Each of these properties may be involved in defining its fourth,
value, property based on a series of rules.

An instance of the Spring class can be visualized as a mechanical spring that provides a corrective
force as the spring is compressed or stretched away from its preferred value. This force is modelled
as linear function of the distance from the preferred value, but with two different constants -- one for
the compressional force and one for the tensional one. Those constants are specified by the minimum
and maximum values of the spring such that a spring at its minimum value produces an equal and
opposite force to that which is created when it is at its maximum value. The difference between the
preferred and minimum values, therefore, represents the ease with which the spring can be
compressed. The difference between its maximum and preferred values indicates the ease with which
the Spring can be extended.

Based on this, a SpringLayout can be visualized as a set of objects that are connected by a set of
springs on their edges.

887
Example: SpringDemo

This section takes you through the typical steps of specifying the constraints for a container that uses
SpringLayout. The first example, SpringDemo1.java, is an extremely simple application that
features a label and a text field in a content pane controlled by a spring layout. Here is the relevant
code:

public class SpringDemo1 {


public static void main(String[] args) {
...
Container contentPane = frame.getContentPane();
SpringLayout layout = new SpringLayout();
contentPane.setLayout(layout);
contentPane.add(new JLabel("Label: "));
contentPane.add(new JTextField("Text field", 15));
...
frame.pack();
frame.setVisible(true);
}
}

Click the Launch button to run SpringDemo1 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

Here is what the GUI looks like when it first comes up:

Here is what it looks like when it is resized to be bigger:

Obviously, we have some problems. Not only does the frame come up way too small, but even when
it is resized the components are all located at (0,0). This happens because we have set no springs
specifying the components' positions and the width of the container. One small consolation is that at
least the components are at their preferred sizes — we get that for free from the default springs
created by SpringLayout for each component.

Our next example, SpringDemo2.java, improves the situation a bit by specifying locations for each
component.Click the Launch button to run SpringDemo2 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

In this example, we will specify that the components should appear in a single row, with 5 pixels
between them. The following code specifies the location of the label:

//Adjust constraints for the label so it's at (5,5).


layout.putConstraint(SpringLayout.WEST, label,

888
5,
SpringLayout.WEST, contentPane);
layout.putConstraint(SpringLayout.NORTH, label,
5,
SpringLayout.NORTH, contentPane);
The first putConstraint call specifies that the label's left (west) edge should be 5 pixels from its
container's left edge. This translates to an x coordinate of 5. The second putConstraint call sets up
a similar relationship between the top (north) edges of the label and its container, resulting in a y
coordinate of 5.

Here is the code that sets up the location of the text field:

//Adjust constraints for the text field so it's at


//(<label's right edge> + 5, 5).
layout.putConstraint(SpringLayout.WEST, textField,
5,
SpringLayout.EAST, label);
layout.putConstraint(SpringLayout.NORTH, textField,
5,
SpringLayout.NORTH, contentPane);

The first putConstraint call makes the text field's left (west) edge be 5 pixels away from the label's
right (east) edge. The second putConstraint call is just like the second call in the first snippet, and
has the same effect of setting the component's y coordinate to 5.

The previous example still has the problem of the container coming up too small. But when we resize
the window, the components are in the right place:

To make the container initially appear at the right size, we need to set the springs that define the right
(east) and bottom (south) edges of the container itself. No constraints for the right and bottom
container edges are set by default. The size of the container is defined by setting these constraints.
SpringDemo3.java shows how to do this. Click the Launch button to run SpringDemo3 using
Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.

Here is the code that sets the container's springs:

layout.putConstraint(SpringLayout.EAST, contentPane,
5,
SpringLayout.EAST, textField);
layout.putConstraint(SpringLayout.SOUTH, contentPane,
5,
SpringLayout.SOUTH, textField);

The first putConstraint call makes the container's right edge be 5 pixels to the right of the text
field's right edge. The second one makes its bottom edge be 5 pixels beyond the bottom edge of the
tallest component (which, for simplicity's sake, we've assumed is the text field).

889
Finally, the window comes up at the right size:

When we make the window larger we can see the spring layout in action, distributing the extra space
between the available components.

In this case the spring layout has chosen to give all the extra space to the text field. Although it
seems like the spring layout treats labels and text fields differently, spring layout has no special
knowledge of any Swing or AWT components. It relies on the values of a component's minimum,
preferred, and maximum size properties. The next section discusses how spring layout uses these
properties, and why they can cause uneven space distribution.

Springs and Component Size

A SpringLayout object automatically installs Springs for the height and width of each component
that the SpringLayout controls. These springs are essentially covers for the componentÂ’s
getMinimumSize, getPreferredSize, and getMaximumSize methods. By "covers" we mean that
not only are the springs initialized with the appropriate values from these methods, but also that the
springs track those values. For example, the Spring object that represents the width of a component
is a special kind of spring that simply delegates its implementation to the relevant size methods of
the component. That way the spring stays in sync with the size methods as the characteristics of the
component change.

When a component's getMaximumSize and getPreferredSize methods return the same value,
SpringLayout interprets this as meaning that the component should not be stretched. JLabel and
JButton are examples of components implemented this way. For this reason, the label in the
SpringDemo3 example does not stretch.

The getMaximumSize method of some components, such as JTextField, returns the value
Integer.MAX_VALUE for the width and height of its maximum size, indicating that the component
can grow to any size. For this reason, when the SpringDemo3 window is enlarged, SpringLayout
distributes all the extra space to the only springs that can grow — those determining the size of the
text field.

More About the SpringLayout API

The SpringDemo examples used the SpringLayout method putConstraint to set the springs
associated with each component. The putConstraint method is a convenience method that lets you
modify a component's constraints without needing to use the full spring layout API. Here, again, is
the code from SpringDemo3 that sets the location of the label:
layout.putConstraint(SpringLayout.WEST, label,

890
5,
SpringLayout.WEST, contentPane);
layout.putConstraint(SpringLayout.NORTH, label,
5,
SpringLayout.NORTH, contentPane);
Here is equivalent code that uses the SpringLayout.Constraints and Spring classes directly:
SpringLayout.Constraints contentPaneCons =
layout.getConstraints(contentPane);
contentPaneCons.setX(Spring.sum(Spring.constant(5),
contentPaneCons
.getConstraint(SpringLayout.WEST)));
contentPaneCons.setY(Spring.sum(Spring.constant(5),
contentPaneCons
.getConstraint(SpringLayout.NORTH)));
To see the entire demo converted to use this API, look at SpringDemo4.java. That file also includes
a more polished (and much longer) version of the code that sets the container's size. Click the Launch
button to run SpringDemo4 using Java™ Web Start (download JDK 6). Alternatively, to compile
and run the example yourself, consult the example index.

As the preceding snippets imply, SpringLayout and SpringLayout.Constraints tend to use


different conventions for describing springs. The SpringLayout API uses edges to define its
constraints. Springs connect edges to establish linear relations between them. Edges are defined by
components, using the following constants:

 SpringLayout.NORTH specifies the top edge of a component's bounding rectangle.


 SpringLayout.SOUTH specifies the bottom edge of a component's bounding
rectangle.
 SpringLayout.EAST specifies the right edge of a component's bounding rectangle.
 SpringLayout.WEST specifies the left edge of a component's bounding rectangle.
 SpringLayout.BASELINE specifies the baseline of a component.
 SpringLayout.HORIZONTAL_CENTER specifies the horizontal center of a component's
bounding rectangle.
 SpringLayout.VERTICAL_CENTER specifies the vertical center of a component's
bounding rectangle.

Edges differ from Spring objects The SpringLayout.Constraints class knows about edges, but
only has Spring objects for the following properties:

 x
 y
 width
 height

Each Constraints object maintains the following relationships between its springs and the edges
they represent:
west = x
north = y
east = x + width
south = y + height

If you are confused, do not worry. The next section presents utility methods you can use to
accomplish some common layout tasks without knowing anything about the spring layout API.

891
Utility Methods for Grids

Because the SpringLayout class was created for GUI builders, setting up individual springs for a
layout can be cumbersome to code by hand. This section presents a couple of methods you can use to
install all the springs needed to lay out a group of components in a grid. These methods emulate
some of the features of the GridLayout, GridBagLayout, and BoxLayout classes.

The two methods, called makeGrid and makeCompactGrid, are defined in SpringUtilities.java.
Both methods work by grouping the components together into rows and columns and using the
Spring.max method to make a width or height spring that makes a row or column big enough for all
the components in it. In the makeCompactGrid method the same width or height spring is used for all
components in a particular column or row, respectively. In the makeGrid method, by contrast, the
width and height springs are shared by every component in the container, forcing them all to be the
same size. Furthermore, factory methods are provided by Spring for creating different kinds of
springs, including springs that depend on other springs.

Let us see these methods in action. Our first example, implemented in the source file
SpringGrid.java, displays a bunch of numbers in text fields. The center text field is much wider
than the others. Just as with GridLayout, having one large cell forces all the cells to be equally large.
Click the Launch button to run SpringGrid using Java™ Web Start (download JDK 6). Alternatively,
to compile and run the example yourself, consult the example index.

Here is the code that creates and lays out the text fields in SpringGrid:

JPanel panel = new JPanel(new SpringLayout());


for (int i = 0; i < 9; i++) {
JTextField textField = new JTextField(Integer.toString(i));
...//when i==4, put long text in the text field...
panel.add(textField);
}
...
SpringUtilities.makeGrid(panel,
3, 3, //rows, cols
5, 5, //initialX, initialY
5, 5);//xPad, yPad

Now let us look at an example, in the source file SpringCompactGrid.java, that uses the
makeCompactGrid method instead of makeGrid. This example displays lots of numbers to show off
spring layout's ability to minimize the space required. Click the Launch button to run
SpringCompactGrid using Java™ Web Start (download JDK 6). Alternatively, to compile and run
the example yourself, consult the example index.

Here is what the SpringCompactGrid GUI looks like:

892
Here is the code that creates and lays out the text fields in SpringCompactGrid:

JPanel panel = new JPanel(new SpringLayout());

int rows = 10;


int cols = 10;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
int anInt = (int) Math.pow(r, c);
JTextField textField =
new JTextField(Integer.toString(anInt));
panel.add(textField);
}
}

//Lay out the panel.


SpringUtilities.makeCompactGrid(panel, //parent
rows, cols,
3, 3, //initX, initY
3, 3); //xPad, yPad

One of the handiest uses for the makeCompactGrid method is associating labels with components,
where the labels are in one column and the components in another. The file SpringForm.java uses
makeCompactGrid in this way, as the following figure demonstrates.

Click the Launch button to run SpringForm using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

Here is the code that creates and lays out the label-text field pairs in SpringForm:
893
String[] labels = {"Name: ", "Fax: ", "Email: ", "Address: "};
int numPairs = labels.length;

//Create and populate the panel.


JPanel p = new JPanel(new SpringLayout());
for (int i = 0; i < numPairs; i++) {
JLabel l = new JLabel(labels[i], JLabel.TRAILING);
p.add(l);
JTextField textField = new JTextField(10);
l.setLabelFor(textField);
p.add(textField);
}

//Lay out the panel.


SpringUtilities.makeCompactGrid(p,
numPairs, 2, //rows, cols
6, 6, //initX, initY
6, 6); //xPad, yPad

Because we are using a real layout manager instead of absolute positioning, the layout manager
responds dynamically to changes in components involved. For example, if the names of the labels
are localized, the spring layout produces a configuration that gives the first column more or less
room, as needed. And as the following figure shows, when the window is resized, the flexibly sized
components — the text fields — take all the excess space, while the labels stick to what they need.

Our last example of the makeCompactGrid method, in SpringBox.java, shows some buttons
configured to be laid out in a single row. Click the Launch button to run SpringBox using Java™
Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the
example index.

Note that the behavior is almost identical to that of BoxLayout in the case of a single row. Not only
are the components laid out as BoxLayout would arrange them but the minimum, preferred, and
maximum sizes of the container that uses the SpringLayout return the same results that BoxLayout
would. Here is the call to makeCompactGrid that produces this layout:

//Lay out the buttons in one row and as many columns


//as necessary, with 6 pixels of padding all around.
SpringUtilities.makeCompactGrid(contentPane, 1,
contentPane.getComponentCount(),

894
6, 6, 6, 6);

Let us look at what happens when we resize this window. This is an odd special case that is worth
taking note of as you may run into it by accident in your first layouts.

Nothing moved! That is because none of the components (buttons) or the spacing between them was
defined to be stretchable. In this case the spring layout calculates a maximum size for the parent
container that is equal to its preferred size, meaning the parent container itself is not stretchable. It
would perhaps be less confusing if the AWT refused to resize a window that was not stretchable, but
it does not. The layout manager cannot do anything sensible here as none of the components will
take up the required space. Instead of crashing, it just does nothing, leaving all the components as
they were.

The SpringLayout API

The API for using SpringLayout is spread across three classes:

 SpringLayout
 SpringLayout.Constraints
 Spring

SpringLayout
Constructor or Method Purpose
SpringLayout() Create a SpringLayout instance.
SpringLayout.Constraints Get the constraints (set of springs) associated with the specified
getConstraints(Component) component.
Get the spring for an edge of a component. The first argument
Spring getConstraint(String,
specifies the edge and must be one of the following
Component)
SpringLayout constants: NORTH, SOUTH, EAST, or WEST.
Convenience methods for defining relationships between the
void putConstraint(String, edges of two components. The first two arguments specify the
Component, int, String, first component and its affected edge. The last two arguments
Component) specify the second component and its affected edge. The third
void putConstraint(String, argument specifies the spring that determines the distance
Component, Spring, String, between the two. When the third argument is an integer, a
Component) constant spring is created to provide a fixed distance between the
component edges.
SpringLayout.Constraints
Constructor or Method Purpose
SpringLayout.Constraints() Create a SpringLayout.Constraints instance. The first two
SpringLayout.Constraints(Spring, arguments, if present, specify the X and Y springs,
Spring) respectively. The second two arguments, if present, specify the

895
SpringLayout.Constraints(Spring, height and width springs, respectively. Omitting an argument
Spring, Spring, Spring) causes the corresponding spring to be null, which
SpringLayout generally replaces with suitable defaults.
Spring getConstraint(String)
Spring getHeight()
Spring getWidth()
Spring getX() Get or set the specified spring. The string argument to the
Spring getY() getConstraint and setConstraint methods specifies an
void setConstraint(String, Spring) edge name, and must be one of the SpringLayout constants
void setHeight(Spring) NORTH, SOUTH, EAST, or WEST.
void setWidth(Spring)
void setX(Spring)
void setY(Spring)
Spring
Method Purpose
Create a spring that does not track a component's sizes. The three-argument
version creates a spring with its minimum, preferred, and maximum values
set to the specified values, in that order. The one-argument version creates a
spring with its minimum, preferred, and maximum values all set to the
static Spring
specified integer.
constant(int)
static Spring
Despite the name, springs returned by constant are mutable. To make a
constant(int, int, int)
layout work out, SpringLayout might be forced to adjust a "constant"
spring. For this reason, you should avoid reusing constant springs unless (1)
you truly want the springs to always be precisely alike and (2) other springs
provide some flexibility in the layout.
static Spring Create a spring that is the result of some mathematical manipulation. The
sum(Spring, Spring) sum method adds two springs. The max method returns a spring whose value
static Spring is always greater than or equal to the values of the two arguments. The
max(Spring, Spring) minus method returns a spring running in the opposite direction of the
static Spring argument. The minus method can be used to create an argument for the sum
minus(Spring) method, allowing you to get the difference between two springs.
int
getMinimumValue() Get the corresponding value from the spring. For a SpringLayout-created
int getPreferredValue() spring that automatically tracks a component, these methods result in calls
int to the component's corresponding getXxxSize method.
getMaximumValue()
int getValue()
Get or set the spring's current value.
setValue(int)

Examples that Use SpringLayout

The following table lists some examples that use spring layout.
Where
Example Notes
Described
SpringDemo3 This page Uses SpringLayout to create a row of evenly spaced, natural-
size components.
SpringDemo4 This page Reimplements SpringDemo3 to use

896
SpringLayout.Constraints and Spring directly.
SpringGrid This page Uses SpringLayout and the makeGrid utility method to create
a layout where all the components are the same size.
SpringCompactGrid This page Uses SpringLayout and the makeCompactGrid utility method
to create a layout where all the components in a row have the
same height, and all components in a column have the same
width.
SpringForm This page Uses SpringLayout and makeCompactGrid to align label-text
field pairs.
SpringBox This page Uses SpringLayout and makeCompactGrid to demonstrate
laying out a single row of components, and what happens
when no springs can grow.
SpinnerDemo How to Use Uses SpringLayout and makeCompactGrid to lay out rows of
Spinners label-spinner pairs.
TextInputDemo How to Use Uses SpringLayout and makeCompactGrid to lay out rows of
Formatted Text labeled components. The components are a mix of text fields,
Fields formatted text fields, and spinners.

Creating a Custom Layout Manager


Before you start creating a custom layout manager, make sure that no existing layout manager will
meet your requirements. In particular, layout managers such as GridBagLayout, SpringLayout, and
BoxLayout are flexible enough to work in many cases. You can also find layout managers from other
sources, such as from the Internet. Finally, you can simplify layout by grouping components into
containers such as panels.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

To create a custom layout manager, you must create a class that implements the LayoutManager
interface. You can either implement it directly, or implement its subinterface, LayoutManager2.

Every layout manager must implement at least the following five methods, which are required by the
LayoutManager interface:

void addLayoutComponent(String, Component)


Called by the Container class's add methods. Layout managers that do not associate strings
with their components generally do nothing in this method.
void removeLayoutComponent(Component)
Called by the Container methods remove and removeAll. Layout managers override this
method to clear an internal state they may have associated with the Component.
Dimension preferredLayoutSize(Container)
Called by the Container class's getPreferredSize method, which is itself called under a
variety of circumstances. This method should calculate and return the ideal size of the
container, assuming that the components it contains will be at or above their preferred sizes.

897
This method must take into account the container's internal borders, which are returned by the
getInsets method.
Dimension minimumLayoutSize(Container)
Called by the Container getMinimumSize method, which is itself called under a variety of
circumstances. This method should calculate and return the minimum size of the container,
assuming that the components it contains will be at or above their minimum sizes. This
method must take into account the container's internal borders, which are returned by the
getInsets method.
void layoutContainer(Container)
Called to position and size each of the components in the container. A layout manager's
layoutContainer method does not actually draw components. It simply invokes one or
more of each component's setSize, setLocation, and setBounds methods to set the
component's size and position.

This method must take into account the container's internal borders, which are returned by the
getInsets method. If appropriate, it should also take the container's orientation (returned by
the getComponentOrientation method) into account. You cannot assume that the
preferredLayoutSize or minimumLayoutSize methods will be called before
layoutContainer is called.

Besides implementing the preceding five methods, layout managers generally implement at least one
public constructor and the toString method.

If you wish to support component constraints, maximum sizes, or alignment, then your layout
manager should implement the LayoutManager2 interface. In fact, for these reasons among many
others, nearly all modern layout managers will need to implement LayoutManager2. That interface
adds five methods to those required by LayoutManager:

 addLayoutComponent(Component, Object)
 getLayoutAlignmentX(Container)
 getLayoutAlignmentY(Container)
 invalidateLayout(Container)
 maximumLayoutSize(Container)

Of these methods, the most important are addLayoutComponent(Component, Object) and


invalidateLayout(Container). The addLayoutComponent method is used to add components to
the layout, using the specified constraint object. The invalidateLayout method is used to
invalidate the layout, so that if the layout manager has cached information, this should be discarded.
For more information about LayoutManager2, see the LayoutManager2 API documentation.

Finally, whenever you create custom layout managers, you should be careful of keeping references to
Component instances that are no longer children of the Container. Namely, layout managers should
override removeLayoutComponent to clear any cached state related to the Component.

Example of a Custom Layout

The example CustomLayoutDemo uses a custom layout manager called DiagonalLayout. You can
find the layout manager's source code in DiagonalLayout.java. DialogLayout lays out
components diagonally, from left to right, with one component per row. Here is a picture of
CustomLayoutDemo using DialogLayout to lay out five buttons.

898
Click the Launch button to run CustomLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

Doing Without a Layout Manager (Absolute


Positioning)
Although it is possible to do without a layout manager, you should use a layout manager if at all
possible. A layout manager makes it easier to adjust to look-and-feel-dependent component
appearances, to different font sizes, to a container's changing size, and to different locales. Layout
managers also can be reused easily by other containers, as well as other programs.

Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

If a container holds components whose size is not affected by the container's size or by font, look-
and-feel, or language changes, then absolute positioning might make sense. Desktop panes, which
contain internal frames, are in this category. The size and position of internal frames does not depend
directly on the desktop pane's size. The programmer determines the initial size and placement of
internal frames within the desktop pane, and then the user can move or resize the frames. A layout
manager is unnecessary in this situation.

Another situation in which absolute positioning might make sense is that of a custom container that
performs size and position calculations that are particular to the container, and perhaps require
knowledge of the container's specialized state. This is the situation with split panes.

Creating a container without a layout manager involves the following steps.

1. Set the container's layout manager to null by calling setLayout(null).


2. Call the Component class's setbounds method for each of the container's children.
3. Call the Component class's repaint method.

However, creating containers with absolutely positioned containers can cause problems if the
window containing the container is resized.

Here is a snapshot of a frame whose content pane uses absolute positioning.

899
Click the Launch button to run AbsoluteLayoutDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.

Its code is in AbsoluteLayoutDemo.java. The following code snippet shows how the components in
the content pane are created and laid out.

pane.setLayout(null);

JButton b1 = new JButton("one");


JButton b2 = new JButton("two");
JButton b3 = new JButton("three");

pane.add(b1);
pane.add(b2);
pane.add(b3);

Insets insets = pane.getInsets();


Dimension size = b1.getPreferredSize();
b1.setBounds(25 + insets.left, 5 + insets.top,
size.width, size.height);
size = b2.getPreferredSize();
b2.setBounds(55 + insets.left, 40 + insets.top,
size.width, size.height);
size = b3.getPreferredSize();
b3.setBounds(150 + insets.left, 15 + insets.top,
size.width + 50, size.height + 20);

...//In the main method:


Insets insets = frame.getInsets();
frame.setSize(300 + insets.left + insets.right,
125 + insets.top + insets.bottom);

Solving Common Layout Problems


Note: This lesson covers writing layout code by hand, which can be challenging. If you are not
interested in learning all the details of layout management, you might prefer to use the GroupLayout
layout manager combined with a builder tool to lay out your GUI. One such builder tool is the
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then
GridBagLayout is recommended as the next most flexible and powerful layout manager.

Problem: How do I specify a component's exact size?

 A handful of the more modern layout managers provide ways to override the size set by the
component. Check whether the layout manager you are using allows you to specify
component sizes.

900
 Make sure that you really need to set the component's exact size. Each Swing component has
a different preferred size, depending on the font it uses and the look and feel. For this reason,
it often does not make sense to specify a Swing component's exact size.
 If the component is not controlled by a layout manager, you can set its size by invoking the
setSize or setBounds method on it. Otherwise, you need to provide size hints and then
make sure you are using a layout manager that respects the size hints.
 If you extend a Swing component class, you can give size hints by overriding the
component's getMinimumSize, getPreferredSize, and getMaximumSize methods. What is
nice about this approach is that each getXxxxSize method can get the component's default
size hints by invoking super.getXxxxSize(). Then it can adjust the size, if necessary,
before returning it. This is particularly handy for text components, where you might want to
fix the width, but have the height determined from the content. However, sometimes
problems can be encountered with GridBagLayout and text fields, wherein if the size of the
container is smaller than the preferred size, the minimum size gets used, which can cause text
fields to shrink quite substantially.
 Another way to give size hints is to invoke the component's setMinimumSize,
setPreferredSize, and setMaximumSize methods.
 If you specify new size hints for a component that is already visible, you then need to invoke
the revalidate method on it, to make sure that its containment hierarchy is laid out again.
Then invoke the repaint method.

Note: No matter how you specify your component's size, be sure that your component's container
uses a layout manager that respects the requested size of the component. The FlowLayout and
GridBagLayout managers use the component's preferred size (the latter depending on the constraints
that you set), but BorderLayout and GridLayout usually do not. The BoxLayout manager generally
uses a component's preferred size (although components can be larger), and is one of the few layout
managers that respects the component's maximum size.

Problem: My component does not appear after I have added it to the container.

 You need to invoke revalidate and repaint after adding a component before it will show
up in your container.

Problem: My custom component is being sized too small.

 Does the component implement the getPreferredSize and getMinimumSize methods? If


so, do they return the right values?
 Are you using a layout manager that can use as much space as is available? See Tips on
Choosing a Layout Manager for some tips on choosing a layout manager and specifying that
it use the maximum available space for a particular component.

If you do not see your problem in this list, see Solving Common Component Problems.

Questions and Exercises: Laying Out Components within a Container

Questions

In each of the following questions, choose the layout manager(s) most naturally suited for the
described layout. Assume that the container controlled by the layout manager is a JPanel. [Hint:

901
Two sections that might help are A Visual Index to Swing Components and Tips on Choosing a
Layout Manager.]

1. The container has one component that should take up as much space as possible

a. BorderLayout
b. GridLayout
c. GridBagLayout
d. a and b
e. b and c

2. The container has a row of components that should all be displayed at the same size, filling the
container’s entire area.

a. FlowLayout
b. GridLayout
c. BoxLayout
d. a and b

3. The container displays a number of components in a column, with any extra space going between
the first two components.

902
a. FlowLayout
b. BoxLayout
c. GridLayout
d. BorderLayout

4. The container can display three completely different components at different times, depending
perhaps on user input or program state. Even if the components’ sizes differ, switching from one
component to the next shouldn’t change the amount of space devoted to the component.

a. SpringLayout
b. BoxLayout
c. CardLayout
d. GridBagLayout

Exercises

1. Implement the layout described and shown in question 1.

2. Implement the layout described and shown in question 2.

3. Implement the layout described and shown in question 3.

4. Implement the layout described and shown in question 4.

5. By adding a single line of code, make the program you wrote for Exercise 2 display the
components from right-to-left, instead of from left-to-right.

Answers: Laying Out Components within a Container

903
Questions

In each of the following questions, choose the layout manager(s) most naturally suited for the
described layout. Assume that the container controlled by the layout manager is a JPanel. [Hint:
Two sections that might help are A Visual Index to Swing Components and Tips on Choosing a
Layout Manager.]

Question 1. The container has one component that should take up as much space as possible

a. BorderLayout
b. GridLayout
c. GridBagLayout
d. a and b
e. b and c

Answer 1: d. BorderLayout and GridLayout easily deal with this situation. Although you could use
GridBagLayout, it's much more complex than necessary.

Question 2. The container has a row of components that should all be displayed at the same size,
filling the container’s entire area.

a. FlowLayout
b. GridLayout
c. BoxLayout
d. a and b

Answer 2: b. This type of same-size layout — whether in a row, a column, or a grid — is what
GridLayout is best at.

904
Question 3. The container displays a number of components in a column, with any extra space going
between the first two components.

a. FlowLayout
b. BoxLayout
c. GridLayout
d. BorderLayout

Answer 3: b. BoxLayout lays out components in either a column or a row. You can specify extra
space using an invisible component.

Question 4. The container can display three completely different components at different times,
depending perhaps on user input or program state. Even if the components’ sizes differ, switching
from one component to the next shouldn’t change the amount of space devoted to the component.

a. SpringLayout
b. BoxLayout
c. CardLayout
d. GridBagLayout

905
Answer 4: c. CardLayout exists to allow components to share the same space. Although it's simpler
to use a JTabbedPane component to control an area, CardLayout is the solution when you don't
want tabs.

Exercises

Exercise 1. Implement the layout described and shown in question 1.


Answer 1: See Layout1.java . Here's the code that implements the layout:

JPanel p = new JPanel(new BorderLayout());


p.add(createComponent("Component 1"),
BorderLayout.CENTER);
frame.setContentPane(p);

Exercise 2. Implement the layout described and shown in question 2.


Answer 2: See Layout2.java . Here's the code that implements the layout:

JPanel p = new JPanel(new GridLayout(1,0));


p.add(createComponent("Component 1"));
p.add(createComponent("Component 2"));
p.add(createComponent("Component 3"));
p.add(createComponent("Component 4"));
frame.setContentPane(p);

Exercise 3. Implement the layout described and shown in question 3.


Answer 3: See Layout3.java . Here's the code that implements the layout:

JPanel p = new JPanel();


p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
p.add(createComponent("Component 1"));
p.add(Box.createVerticalGlue());
p.add(createComponent("Component 2"));
p.add(createComponent("Component 3"));
p.add(createComponent("Component 4"));
frame.setContentPane(p);

Exercise 4. Implement the layout described and shown in question 4.


Answer 4: See Layout4.java . Here's the code that implements the layout:

...//Where the radio buttons are set up:


for (int i= 0; i < strings.length; i++) {
...
rb[i].setActionCommand(String.valueOf(i));
...
}

...//Where the panel to contain the shared-space components is set up:


cards = new JPanel(new CardLayout());
for (int i = 0; i < strings.length; i++) {
cards.add(createComponent(strings[i]), String.valueOf(i));
}

906
...//In the action listener for the radio buttons:
public void actionPerformed(ActionEvent evt) {
CardLayout cl = (CardLayout)(cards.getLayout());
cl.show(cards, (String)evt.getActionCommand());
}

Exercise 5. By adding a single line of code, make the program you wrote for Exercise 2 display the
components from right-to-left, instead of from left-to-right.

Answer 5: You can change the horizontal orientation using the setComponentOrientation method
defined by the Component class. For example:

p.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

907
Lesson: Modifying the Look and Feel

How to Set the Look and Feel


The architecture of Swing is designed so that you may change the "look and feel" (L&F) of your
application's GUI (see A Swing Architecture Overview). "Look" refers to the appearance of GUI
widgets (more formally, JComponents) and "feel" refers to the way the widgets behave.

Swing's architecture enables multiple L&Fs by separating every JComponent into two distinct
classes: a JComponent subclass and a corresponding ComponentUI subclass. For example, every
JList instance has a concrete implementation of ListUI (ListUI extends ComponentUI). The
ComponentUI subclass is referred to by various names in Swing's documentation—"the UI,"
"component UI," "UI delegate," and "look and feel delegate" are all used to identify the
ComponentUI subclass.

Most developers never need to interact with the UI delegate directly. For the most part, the UI
delegate is used internally by the JComponent subclass for crucial functionality, with cover methods
provided by the JComponent subclass for all access to the UI delegate. For example, all painting in
JComponent subclasses is delegated to the UI delegate. By delegating painting, the 'look' can vary
depending upon the L&F.

It is the responsibility of each L&F to provide a concrete implementation for each of the
ComponentUI subclasses defined by Swing. For example, the Java Look and Feel creates an instance
of MetalTabbedPaneUI to provide the L&F for JTabbedPane. The actual creation of the UI delegate
is handled by Swing for you—for the most part you never need to interact directly with the UI
delegate.

The rest of this section discusses the following subjects:

 Available Look and Feels


 Programatically Setting the Look and Feel
 Specifying the Look and Feel: Command Line
 Specifying the Look and Feel: swing.properties
 How the UI Manager Chooses the Look and Feel
 Changing the Look and Feel After Startup
 An Example
 Themes
 The SwingSet2 Demonstration Program

Available Look and Feels

Sun's JRE provides the following L&Fs:

1. CrossPlatformLookAndFeel—this is the "Java L&F" (also called "Metal") that looks


the same on all platforms. It is part of the Java API (javax.swing.plaf.metal) and
is the default that will be used if you do nothing in your code to set a different L&F.
2. SystemLookAndFeel—here, the application uses the L&F that is native to the system
it is running on. The System L&F is determined at runtime, where the application asks
the system to return the name of the appropriate L&F.

908
3. Synth—the basis for creating your own look and feel with an XML file.
4. Multiplexing— a way to have the UI methods delegate to a number of different look
and feels at the same time.

For Linux and Solaris, the System L&Fs are "GTK+" if GTK+ 2.2 or later is installed, "Motif"
otherwise. For Windows, the System L&F is "Windows," which mimics the L&F of the particular
Windows OS that is running—classic Windows, XP, or Vista. The GTK+, Motif, and Windows
L&Fs are provided by Sun and shipped with the Java SDK and JRE, although they are not part of the
Java API.

Apple provides its own JVM which includes their proprietary L&F.

In summary, when you use the SystemLookAndFeel, this is what you will see:

Platform Look and Feel


Solaris, Linux with GTK+ 2.2 or later GTK+
Other Solaris, Linux Motif
IBM UNIX IBM*
HP UX HP*
Classic Windows Windows
Windows XP Windows XP
Windows Vista Windows Vista
Macintosh Macintosh*

* Supplied by the system vendor.

You don't see the System L&F in the API. The GTK+, Motif, and Windows packages that it requires
are shipped with the Java SDK as:

com.sun.java.swing.plaf.gtk.GTKLookAndFeel
com.sun.java.swing.plaf.motif.MotifLookAndFeel
com.sun.java.swing.plaf.windows.WindowsLookAndFeel
Note that the path includes java, and not javax.

Note: The GTK+ L&F will only run on UNIX or Linux systems with GTK+ 2.2 or later installed,
while the Windows L&F runs only on Windows systems. Like the Java (Metal) L&F, the Motif L&F
will run on any platform.

All of Sun's L&Fs have a great deal of commonality. This commonality is defined in the Basic look
and feel in the API (javax.swing.plaf.basic). The Motif and Windows L&Fs are each built by
extending the UI delegate classes in javax.swing.plaf.basic (a custom L&F can be built by
doing the same thing). The "Basic" L&F is not used without being extended.

In the API you will see four L&F packages:

 javax.swing.plaf.basic—basic UI Delegates to be extended when creating a


custom L&F

909
 javax.swing.plaf.metal—the Java L&F, also known as the CrossPlatform L&F
("Metal" was the Sun project name for this L&F) The current default "theme"
(discussed below) for this L&F is "Ocean, so this is often referred to as the Ocean
L&F.
 javax.swing.plaf.multi—a multiplexing L&F that allows the UI methods to
delegate to a number of look and feels at the same time. It can be used to augment the
behavior of a particular L&F, for example with a L&F that provides audio cues on top
of the Windows L&F. This is a way of creating a handicapped-accessible L&F.
 javax.swing.plaf.synth—an easily configured L&F using XML files (discussed in
the next section of this lesson)

You aren't limited to the L&Fs supplied with the Java platform. You can use any L&F that is in your
program's class path. External L&Fs are usually provided in one or more JAR files that you add to
your program's class path at runtime. For example:

java -classpath .;C:\java\lafdir\customlaf.jar YourSwingApplication

Once an external L&F is in your program's class path, your program can use it just like any of the
L&Fs shipped with the Java platform.

Programatically Setting the Look and Feel

Note: If you are going to set the L&F, you should do it as the very first step in your application.
Otherwise you run the risk of initializing the Java L&F regardless of what L&F you've requested.
This can happen inadvertently when a static field references a Swing class, which causes the L&F to
be loaded. If no L&F has yet been specified, the default L&F for the JRE is loaded. For Sun's JRE
the default is the Java L&F, for Apple's JRE the Apple L&F, and so forth.

The L&F that Swing components use is specified by way of the UIManager class in the
javax.swing package. Whenever a Swing component is created,the component asks the UI manager
for the UI delegate that implements the component's L&F. For example, each JLabel constructor
queries the UI manager for the UI delegate object appropriate for the label. It then uses that UI
delegate object to implement all of its drawing and event handling.

To programatically specify a L&F, use the UIManager.setLookAndFeel() method with the fully
qualified name of the appropriate subclass of LookAndFeel as its argument. For example, the bold
code in the following snippet makes the program use the cross-platform Java L&F:

public static void main(String[] args) {


try {
// Set cross-platform Java L&F (also called "Metal")
UIManager.setLookAndFeel(
UIManager.getCrossPlatformLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
// handle exception
}
catch (ClassNotFoundException e) {
// handle exception
}
catch (InstantiationException e) {
// handle exception
}

910
catch (IllegalAccessException e) {
// handle exception
}

new SwingApplication(); //Create and show the GUI.


}

Alternatively, this code makes the program use the System L&F:

public static void main(String[] args) {


try {
// Set System L&F
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
// handle exception
}
catch (ClassNotFoundException e) {
// handle exception
}
catch (InstantiationException e) {
// handle exception
}
catch (IllegalAccessException e) {
// handle exception
}

new SwingApplication(); //Create and show the GUI.


}

You can also use the actual class name of a Look and Feel as the argument to
UIManager.setLookAndFeel(). For example,

// Set cross-platform Java L&F (also called "Metal")


UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
or
// Set Motif L&F on any platform
UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");

You aren't limited to the preceding arguments. You can specify the name for any L&F that is in your
program's class path.

Specifying the Look and Feel: Command Line

You can specify the L&F at the command line by using the -D flag to set the swing.defaultlaf
property. For example:
java -Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel MyApp

java -Dswing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel MyApp

Specifying the Look and Feel: swing.properties File

Yet another way to specify the current L&F is to use the swing.properties file to set the
swing.defaultlaf property. This file, which you may need to create, is located in the lib directory
of Sun's Java release (other vendors of Java may use a different location). For example, if you're
using the Java interpreter in javaHomeDirectory\bin, then the swing.properties file (if it exists)
is in javaHomeDirectory\lib. Here is an example of the contents of a swing.properties file:

911
# Swing properties
swing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel

How the UI Manager Chooses the Look and Feel

Here are the look-and-feel determination steps that occur when the UI manager needs to set a L&F:

1. If the program sets the L&F before a look and feel is needed, the UI manager tries to
create an instance of the specified look-and-feel class. If successful, all components
use that L&F.
2. If the program hasn't successfully specified a L&F, then the UI manager uses the L&F
specified by the swing.defaultlaf property. If the property is specified in both the
swing.properties file and on the command line, the command-line definition takes
precedence.
3. If none of these steps has resulted in a valid L&F, Sun's JRE uses the Java L&F.
Other vendors, such as Apple, will use their default L&F.

Changing the Look and Feel After Startup

You can change the L&F with setLookAndFeel even after the program's GUI is visible. To make
existing components reflect the new L&F, invoke the SwingUtilities updateComponentTreeUI
method once per top-level container. Then you might wish to resize each top-level container to
reflect the new sizes of its contained components. For example:
UIManager.setLookAndFeel(lnfName);
SwingUtilities.updateComponentTreeUI(frame);
frame.pack();

An Example

In the following example, LookAndFeelDemo.java, you can experiment with different Look and
Feels. The program creates a simple GUI with a button and a label. Every time you click the button,
the label increments.

You can change the L&F by changing the LOOKANDFEEL constant on line 18. The comments on the
preceding lines tell you what values are acceptable:

// Specify the look and feel to use by defining the LOOKANDFEEL constant
// Valid values are: null (use the default), "Metal", "System", "Motif",
// and "GTK"
final static String LOOKANDFEEL = "Motif";
Here the constant is set to "Motif", which is a L&F that will run on any platform (as will the default,
"Metal"). "GTK+" will not run on Windows, and "Windows" will run only on Windows. If you
choose a L&F that will not run, you will get the Java, or Metal, L&F.

In the section of the code where the L&F is actually set, you will see several different ways to set it,
as discussed above:

if (LOOKANDFEEL.equals("Metal")) {
lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
// an alternative way to set the Metal L&F is to replace the
// previous line with:
// lookAndFeel = "javax.swing.plaf.metal.MetalLookAndFeel";

You can verify that both arguments work by commenting/un-commenting the two alternatives.

912
Here is a listing of the LookAndFeelDemo source file:

package lookandfeel;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.metal.*;

public class LookAndFeelDemo implements ActionListener {


private static String labelPrefix = "Number of button clicks: ";
private int numClicks = 0;
final JLabel label = new JLabel(labelPrefix + "0 ");

// Specify the look and feel to use by defining the LOOKANDFEEL constant
// Valid values are: null (use the default), "Metal", "System", "Motif",
// and "GTK"
final static String LOOKANDFEEL = "Metal";

// If you choose the Metal L&F, you can also choose a theme.
// Specify the theme to use by defining the THEME constant
// Valid values are: "DefaultMetal", "Ocean", and "Test"
final static String THEME = "Test";

public Component createComponents() {


JButton button = new JButton("I'm a Swing button!");
button.setMnemonic(KeyEvent.VK_I);
button.addActionListener(this);
label.setLabelFor(button);

JPanel pane = new JPanel(new GridLayout(0, 1));


pane.add(button);
pane.add(label);
pane.setBorder(BorderFactory.createEmptyBorder(
30, //top
30, //left
10, //bottom
30) //right
);

return pane;
}

public void actionPerformed(ActionEvent e) {


numClicks++;
label.setText(labelPrefix + numClicks);
}

private static void initLookAndFeel() {


String lookAndFeel = null;

if (LOOKANDFEEL != null) {
if (LOOKANDFEEL.equals("Metal")) {
lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
// an alternative way to set the Metal L&F is to replace the
// previous line with:
// lookAndFeel = "javax.swing.plaf.metal.MetalLookAndFeel";

else if (LOOKANDFEEL.equals("System")) {

913
lookAndFeel = UIManager.getSystemLookAndFeelClassName();
}

else if (LOOKANDFEEL.equals("Motif")) {
lookAndFeel = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
}

else if (LOOKANDFEEL.equals("GTK")) {
lookAndFeel = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
}

else {
System.err.println("Unexpected value of LOOKANDFEEL specified: "
+ LOOKANDFEEL);
lookAndFeel = UIManager.getCrossPlatformLookAndFeelClassName();
}

try {

UIManager.setLookAndFeel(lookAndFeel);

// If L&F = "Metal", set the theme

if (LOOKANDFEEL.equals("Metal")) {
if (THEME.equals("DefaultMetal"))
MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
else if (THEME.equals("Ocean"))
MetalLookAndFeel.setCurrentTheme(new OceanTheme());
else
MetalLookAndFeel.setCurrentTheme(new TestTheme());

UIManager.setLookAndFeel(new MetalLookAndFeel());
}

catch (ClassNotFoundException e) {
System.err.println("Couldn't find class for specified look and
feel:"
+ lookAndFeel);
System.err.println("Did you include the L&F library in the class
path?");
System.err.println("Using the default look and feel.");
}

catch (UnsupportedLookAndFeelException e) {
System.err.println("Can't use the specified look and feel ("
+ lookAndFeel
+ ") on this platform.");
System.err.println("Using the default look and feel.");
}

catch (Exception e) {
System.err.println("Couldn't get specified look and feel ("
+ lookAndFeel
+ "), for some reason.");
System.err.println("Using the default look and feel.");
e.printStackTrace();
}
}

914
}

private static void createAndShowGUI() {


//Set the look and feel.
initLookAndFeel();

//Make sure we have nice window decorations.


JFrame.setDefaultLookAndFeelDecorated(true);

//Create and set up the window.


JFrame frame = new JFrame("SwingApplication");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

LookAndFeelDemo app = new LookAndFeelDemo();


Component contents = app.createComponents();
frame.getContentPane().add(contents, BorderLayout.CENTER);

//Display the window.


frame.pack();
frame.setVisible(true);
}

public static void main(String[] args) {


//Schedule a job for the event dispatch thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

Themes

Themes were introduced as a way of easily changing the colors and fonts of the cross-platform Java
(Metal) Look and Feel. In the sample program, LookAndfeelDemo.java, listed above, you can
change the theme of the Metal L&F by setting the THEME constant on line 23 to one of three values:

 DefaultMetal
 Ocean
 Test

Ocean, which is a bit softer than the pure Metal look, has been the default theme for the Metal (Java)
L&F since Java SE 5. Despite its name, DefaultMetal is not the default theme for Metal (it was
before Java SE 5, which explains its name). The Test theme is a theme defined in TestTheme.java,
which you must compile with LookAndfeelDemo.java. As it is written, TestTheme.java sets the
three primary colors (with somewhat bizarre results). You can modify TestTheme.java to test any
colors you like.

The section of code where the theme is set is found beginning on line 92 of LookAndfeelDemo.java.
Note that you must be using the Metal L&F to set a theme.

if (LOOKANDFEEL.equals("Metal")) {
if (THEME.equals("DefaultMetal"))
MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
else if (THEME.equals("Ocean"))
MetalLookAndFeel.setCurrentTheme(new OceanTheme());
else

915
MetalLookAndFeel.setCurrentTheme(new TestTheme());

UIManager.setLookAndFeel(new MetalLookAndFeel());
}

The SwingSet2 Demonstration Program

In your JDK 6 installation, there is a demo\jfc folder that contains a demonstration program called
SwingSet2. This program has a graphically rich GUI and allows you to change the Look and Feel
from the menu. Further, if you are using the Java (Metal) Look and Feel, you can choose a variety of
different themes. The files for the various themes (for example, RubyTheme.java) are found in the
SwingSet2\src folder.

This is the "Ocean" theme, which is the default for the cross-platform Java (Metal) Look and Feel:

This is the "Steel" theme, which was the original theme for the cross-platform Java (Metal) Look and
Feel:

916
To run the SwingSet2 demo program on a system that has the JDK installed, use this command:

java -jar SwingSet2.jar


This will give you the default theme of Ocean.

To get the metal L&F, run this:

java -Dswing.metalTheme=steel -jar SwingSet2.jar

It is also possible to run the SwingSet2 demo program with Java Web Start:

http://java.sun.com/products/jfc/jws/SwingSet2.jnlp

The Synth Look and Feel


Creating a custom look and feel, or modifying an existing one, can be a daunting task. The
javax.swing.plaf.synth package can be used to create a custom look and feel with much less

917
effort. You can create a Synth look and feel either programatically or through the use of an external
XML file. The discussion below is devoted to the creation of a Synth look and feel using an external
XML file. Creating a Synth L&F programatically is discussed in the API documentation.

With the Synth look and feel, you provide the "look." Synth itself provides the "feel." Thus, you can
think of the Synth L&F as a "skin."

The Synth Architecture

Recall from the previous topic that it is the responsibility of each L&F to provide a concrete
implementation for each of the many ComponentUI subclasses defined by Swing. The Synth L&F
takes care of this for you. To use Synth, you need not create any ComponentUIs—rather you need
only specify how each component is painted, along with various properties that effect the layout and
size.

Synth operates at a more granular level than a component—this granular level is called a "region."
Each component has one or more regions. Many components have only one region, such as JButton.
Others have multiple regions, such as JScrollBar. Each of the ComponentUIs provided by Synth
associates a SynthStyle with each of the regions defined by the ComponentUI. For example, Synth
defines three regions for JScrollBar: the track, the thumb and the scroll bar itself. The
ScrollBarUI (the ComponentUI subclass defined for JScrollBar) implementation for Synth
associates a SynthStyle with each of these regions.

918
SynthStyle provides style information used by the Synth ComponentUI implementation. For
example, SynthStyle defines the foreground and background color, font information, and so forth.
In addition, each SynthStyle has a SynthPainter that is used to paint the region. For example,
SynthPainter defines the two methods paintScrollBarThumbBackground and
paintScrollBarThumbBorder, which are used to paint the scroll bar thumb regions.

Each of the ComponentUIs in Synth obtain SynthStyles using a SynthStyleFactory. There are
two ways to define a SynthStyleFactory: through a Synth XML file, or programatically. The
following code shows how to load an XML file dictating the look of Synth—beneath the covers this
creates a SynthStyleFactory implementation populated with SynthStyles from the XML file:

SynthLookAndFeel laf = new SynthLookAndFeel();


laf.load(MyClass.class.getResourceAsStream("laf.xml"), MyClass.class);
UIManager.setLookAndFeel(laf);

The programmatic route involves creating an implementation of SynthStyleFactory that returns


SynthStyles. The following code creates a custom SynthStyleFactory that returns distinct
SynthStyles for buttons and trees:

class MyStyleFactory extends SynthStyleFactory {


public SynthStyle getStyle(JComponent c, Region id) {
if (id == Region.BUTTON) {
return buttonStyle;
}
else if (id == Region.TREE) {
return treeStyle;
}
return defaultStyle;
}
}
SynthLookAndFeel laf = new SynthLookAndFeel();
UIManager.setLookAndFeel(laf);
SynthLookAndFeel.setStyleFactory(new MyStyleFactory());

The XML File

An explanation of the DTD for the Synth XML file can be found at
javax.swing.plaf.synth/doc-files/synthFileFormat.html.

When you load a Synth look and feel, only those GUI components (or regions) for which there is a
definition (a "style" bound to the region, as discussed below) are rendered. There is no default
behavior for any components—without style definitions in the Synth XML file, the GUI is a blank
canvas.

To specify the rendering of a component (or region), your XML file must contain a <style> element,
which is then bound to the region using the <bind> element. As an example, let's define a style that
includes the font, foreground color, and background color, and then bind that style to all components.
It is a good idea to include such an element in your Synth XML file while you are developing it—
then, any components you haven't yet defined will at least have colors and a font:

<synth>
<style id="basicStyle">
<font name="Verdana" size="16"/>
<state>
<color value="WHITE" type="BACKGROUND"/>
<color value="BLACK" type="FOREGROUND"/>

919
</state>
</style>
<bind style="basicStyle" type="region" key=".*"/>
</synth>

Let's analyse this style definition:

1. The <style> element is the basic building block of the Synth XML file. It contains all the
information needed to describe a region's rendering. A <style> element can describe more
than one region, as is done here. In general, though, it is best to create a <style> element for
each component or region. Note that the <style> element is given an identifier, the string
"basicStyle." This identifier will be used later in the <bind> element.

2. The <font> element of the <style> element sets the font to Verdana, size 16.

3. The <state> element of the <style> element will be discussed below. The <state> element of
a region can have one, or a mixture, of seven possible values. When the value is not
specified, the definition applies to all states, which is the intention here. Therefore, the
background and foreground colors "for all states" are defined in this element.

4. Finally, the <style> element with the identifier "basicStyle" that has just been defined is
bound to all regions. The <bind> element binds "basicStyle" to "region" types. Which region
type or types the binding applies to is given by the "key" attribute, which is ".*" in this case,
the regular expression for "all."

Let's look at the pieces of the Synth XML file before creating some working examples. We'll start
with the <bind> element, showing how a given <style> is applied to a component or region.

The <bind> Element

Whenever a <style> element is defined, it must be bound to one or more components or regions
before it has an effect. The <bind> element is used for this purpose. It requires three attributes:

1. style is the unique identifier of a previously defined style.

2. type is either "name" or "region." If type is a name, obtain the name with the
component.getName() method. If type is a region, use the appropriate constant defined in
the Region class in the javax.swing.plaf.synth package.

3. key is a regular expression used to determine which components or regions the style is bound
to.

920
A Region is a way of identifying a component or part of a component. Regions are based on the
constants in the Region class, modified by stripping out underscores:

For example, to identify the SPLIT_PANE region you would use SPLITPANE, splitpane, or
SplitPane (case insensitive).

When you bind a style to a region, that style will apply to all of the components with that region.
You can bind a style to more than one region, and you can bind more than one style to a region. For
example,

<style id="styleOne">
<!-- styleOne definition goes here -->
</style>

<style id="styleTwo">
<!-- styleTwo definition goes here -->
</style>

<bind style="styleOne" type="region" key="Button"/>


<bind style="styleOne" type="region" key="RadioButton"/>
<bind style="styleOne" type="region" key="ArrowButton"/>

<bind style="styleTwo" type="region" key="ArrowButton"/>

You can bind to individual, named components, whether or not they are also bound as regions. For
example, suppose you want to have the "OK" and "Cancel" buttons in your GUI treated differently
than all the other buttons. First, you would give the OK and Cancel buttons names, using the
component.setName() method. Then, you would define three styles: one for buttons in general
(region = "Button"), one for the OK button (name = "OK"), and one for the Cancel button (name =
"Cancel"). Finally, you would bind these styles like this:

<bind style="styleButton" type="region" key="Button">


<bind style="styleOK" type="name" key="OK">
<bind style="styleCancel" type="name" key="Cancel">

As a result, the "OK" button is bound to both "styleButton" and "styleOK," while the "Cancel"
button is bound to both "styleButton" and "styleCancel."

When a component or region is bound to more than one style, the styles are merged

Note: Just as a style can be bound to multiple regions or names, multiple styles can be bound to a
region or name. These multiple styles are merged for the region or name. Precedence is given to
styles defined later in the file.

The <state> Element

The <state> element allows you to define a look for a region that depends on its "state." For example,
you will usually want a button that has been PRESSED to look different than the button in its ENABLED
state. There are seven possible values for <state> that are defined in the Synth XML DTD. They are:

1. ENABLED

921
2. MOUSE_OVER
3. PRESSED
4. DISABLED
5. FOCUSED
6. SELECTED
7. DEFAULT

You can also have composite states, separated by 'and'—for example, ENABLED and FOCUSED. If
you do not specify a value, the defined look will apply to all states.

As an example, here is a style that specifies painters per state. All buttons are painted a certain way,
unless the state is "PRESSED," in which case they are painted differently:

<style id="buttonStyle">
<property key="Button.textShiftOffset" type="integer" value="1"/>
<insets top="10" left="10" right="10" bottom="10"/>

<state>
<imagePainter method="buttonBackground" path="images/button.png"
sourceInsets="10 10 10 10"/>
</state>
<state value="PRESSED">
<color value="#9BC3B1" type="BACKGROUND"/>
<imagePainter method="buttonBackground" path="images/button2.png"
sourceInsets="10 10 10 10"/>
</state>
</style>
<bind style="buttonStyle" type="region" key="Button"/>

Ignoring the <property> and <insets> elements for the moment, you can see that a pressed button is
painted differently than an unpressed button.

The <state> value that is used is the defined state that most closely matches the state of the region.
Matching is determined by the number of values that match the state of the region. If none of the
state values match, then the state with no value is used. If there are matches, the state with the most
individual matches will be chosen. For example, the following code defines three states:

<state id="zero">
<color value="RED" type="BACKGROUND"/>
</state>
<state value="SELECTED and PRESSED" id="one">
<color value="RED" type="BACKGROUND"/>
</state>
<state value="SELECTED" id="two">
<color value="BLUE" type="BACKGROUND"/>
</state>

If the state of the region contains at least SELECTED and PRESSED, state one will be chosen. If the
state contains SELECTED, but not does not contain PRESSED, state two will be used. If the state
contains neither SELECTED nor PRESSED, state zero will be used.

When the current state matches the same number of values for two state definitions, the one that is
used is the first one defined in the style. For example, the MOUSE_OVER state is always true of a
PRESSED button (you can't press a button unless the mouse is over it). So, if the MOUSE_OVER state is

922
declared first, it will always be chosen over PRESSED, and any painting defined for PRESSED will not
be done.

<state value="PRESSED">
<imagePainter method="buttonBackground" path="images/button_press.png"
sourceInsets="9 10 9 10" />
<color type="TEXT_FOREGROUND" value="#FFFFFF"/>
</state>

<state value="MOUSE_OVER">
<imagePainter method="buttonBackground" path="images/button_on.png"
sourceInsets="10 10 10 10" />
<color type="TEXT_FOREGROUND" value="#FFFFFF"/>
</state>
The code above will work properly. However, if you reverse the order of the MOUSE_OVER and
PRESSED states in the file, the PRESSED state will never be used. This is because any state that is
PRESSED state is also a MOUSE_OVER state. Since the MOUSE_OVER state was defined first, it is the one
that will be used.

Colors and Fonts

The <color> element requires two attributes:

1. value can be any one of the java.awt.Color constants, such as RED, WHITE, BLACK,
BLUE, etc. It can also be a hex representation of RGB values, such as #FF00FF or #326A3B.

2. type describes where the color applies—it can be BACKGROUND, FOREGROUND,


FOCUS, TEXT_BACKGROUND, OR TEXT_FOREGROUND.

For example:

<style id="basicStyle">
<state>
<color value="WHITE" type="BACKGROUND"/>
<color value="BLACK" type="FOREGROUND"/>
</state>
</style>

The <font> element has three attributes:

1. name—the name of the font. For example, Arial or Verdana.

2. size—the size of the font in pixels.

3. style (optional)—BOLD, ITALIC, OR BOLD ITALIC. If omitted, you get a normal font.

For example:

<style id="basicStyle">

923
<font name="Verdana" size="16"/>
</style>

Each of the <color> element and the <font> element has an alternate usage. Each can have an id
attribute or an idref attribute. Using the id attribute, you can define a color that you can reuse later
by using the idref attribute. For example,

<color id="backColor" value="WHITE" type="BACKGROUND"/>


<font id="textFont" name="Verdana" size="16"/>
...
...
...
<color idref="backColor"/>
<font idref="textFont"/>

Insets

The insets add to the size of a component as it is drawn. For example, without insets, a button with
a caption of Cancel will be just large enough to contain the caption in the chosen font. With an
<insets> element like this

<insets top="15" left="20" right="20" bottom="15"/>,


the button will be made larger by 15 pixels above and below the caption and 20 pixels to the left and
right of the caption.

Painting With Images

Synth's file format allows customizing the painting by way of images. Synth's image painter breaks
an image into nine distinct areas: top, top right, right, bottom right, bottom, bottom left, left, top left,
and center. Each of the these areas is painted into the destination. The top, left, bottom, and right
edges are tiled or stretched, while the corner portions (sourceInsets) remain fixed.

Note: There is no relation between the <insets> element and the sourceInsets attribute. The
<insets> element defines the space taken up by a region, while the sourceInsets attributes define
how to paint an image. The <insets> and sourceInsets will often be similar, but they need not be.

You can specify whether the center area should be painted with the paintCenter attribute. The
following image shows the nine areas:

924
Let's create a button as an example. To do this we can use the following image (shown larger than its
actual size):

The red box at the upper left corner is 10 pixels square (including the box border)—it shows the
corner region that should not be stretched when painting. To achieve this, the top and left
sourceInsets should be set to 10. We'll use the following style and binding:
<style id="buttonStyle">
<insets top="15" left="20" right="20" bottom="15"/>
<state>
<imagePainter method="buttonBackground" path="images/button.png"
sourceInsets="10 10 10 10"/>
</state>
</style>
<bind style="buttonStyle" type="region" key="button"/>

The lines inside the <state> element specify that the background of buttons should be painted using
the image images/button.png. That path is relative to the Class that is passed into
SynthLookAndFeel's load method. The sourceInsets attribute specifies the areas of the image that
are not to be stretched. In this case the top, left, bottom, and right insets are each 10. This will cause
the painter not to stretch a 10 x 10 pixel area at each corner of the image.

The <bind> binds buttonStyle to all buttons.

The <imagePainter> element provides all the information needed to render a portion of a region. It
requires only a few attributes:

 method—this specifies which of the methods in the


javax.swing.plaf.synth.SynthPainter class is to be used for painting. The
SynthPainter class contains about 100 methods that begin with paint. When you determine
which one you need, you remove the paint prefix, change the remaining first letter to
lowercase, and use the result as the method attribute. For example, the SynthPainter method
paintButtonBackground becomes the attribute buttonBackground.

 path—the path to the image to be used, relative to the Class that is passed into
SynthLookAndFeel's load method.

 sourceInsets—the insets in pixels, representing the width and height of the corner areas that
should not be stretched They map to the top, left, bottom, and right, in that order.

925
 paintCenter (optional) : This attribute lets you keep the center of an image or get rid of it (in a
text field, for example, so text can be drawn).

The listing below shows the XML code for loading different images depending on the <state> of the
button

<style id="buttonStyle">
<property key="Button.textShiftOffset" type="integer" value="1"/>
<insets top="15" left="20" right="20" bottom="15"/>
<state>
<imagePainter method="buttonBackground" path="images/button.png"
sourceInsets="10 10 10 10"/>
</state>
<state value="PRESSED">
<imagePainter method="buttonBackground" path="images/button2.png"
sourceInsets="10 10 10 10"/>
</state>
</style>
<bind style="buttonStyle" type="region" key="button"/>
button2.png shows the depressed version of button.png, shifted one pixel to the right. The line
<property key="Button.textShiftOffset" type="integer" value="1"/>
shifts the button text accordingly, as discussed in the next section.

The <property> Element

<property> elements are used to add key value pairs to a <style> element. Many components use the
key value pairs for configuring their visual appearance.

The <property> element has three attributes:

 key—the name of the property.

 type—the data type of the property.

 value—the value of the property.

There is a property table (componentProperties.html) that lists the properties each component
supports: javax/swing/plaf/synth/doc-files/componentProperties.html.

Since the button2.png image shifts the visual button one pixel when it is depressed, we should also
shift the button text. There is a button property that does this:

<property key="Button.textShiftOffset" type="integer" value="1"/>

An Example

Here is an example, using the button style defined above. The button style, plus a "backing style"
with definitions of font and colors that are bound to all regions (similar to the "basicStyle" shown in

926
the section titled "The XML File," above) are combined in buttonSkin.xmlHere is a listing of
buttonSkin.xml:

<!-- Synth skin that includes an image for buttons -->


<synth>
<!-- Style that all regions will use -->
<style id="backingStyle">
<!-- Make all the regions that use this skin opaque-->
<opaque value="TRUE"/>
<font name="Dialog" size="12"/>
<state>
<!-- Provide default colors -->
<color value="#9BC3B1" type="BACKGROUND"/>
<color value="RED" type="FOREGROUND"/>
</state>
</style>
<bind style="backingStyle" type="region" key=".*"/>
<style id="buttonStyle">
<!-- Shift the text one pixel when pressed -->
<property key="Button.textShiftOffset" type="integer" value="1"/>
<insets top="15" left="20" right="20" bottom="15"/>
<state>
<imagePainter method="buttonBackground" path="images/button.png"
sourceInsets="10 10 10 10"/>
</state>
<state value="PRESSED">
<imagePainter method="buttonBackground" path="images/button2.png"
sourceInsets="10 10 10 10"/>
</state>
</style>
<!-- Bind buttonStyle to all JButtons -->
<bind style="buttonStyle" type="region" key="button"/>
</synth>
We can load this XML file to use the Synth look and feel for a simple application called
SynthApplication.java. The GUI for this application includes a button and a label. Every time the
button is clicked, the label increments.

Note: The label is painted, even though buttonSkin.xml does not contain a style for it. This is
because there is a general "backingStyle" that includes a font and colors.

Here is the listing of the SynthApplication.javafile.

Try this: Click the Launch button to run the SynthApplication example using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.

Painting With Icons

Radio buttons and check boxes typically render their state by fixed-size icons. For these, you can
create an icon and bind it to the appropriate property (refer to the properties table,
javax/swing/plaf/synth/doc-files/componentProperties.html). For example, to paint radio
buttons that are selected or unselected, use this code:

927
<style id="radioButton">
<imageIcon id="radio_off" path="images/radio_button_off.png"/>
<imageIcon id="radio_on" path="images/radio_button_on.png"/>
<property key="RadioButton.icon" value="radio_off"/>
<state value="SELECTED">
<property key="RadioButton.icon" value="radio_on"/>
</state>
</style>
<bind style="radioButton" type="region" key="RadioButton"/>

Custom Painters

Synth's file format allows for embedding arbitrary objects by way of the long-term persistence
of JavaBeans components . This ability is particularly useful in providing your own painters
beyond the image-based ones Synth provides. For example, the following XML code specifies that a
gradient should be rendered in the background of text fields:

<synth>
<object id="gradient" class="GradientPainter"/>
<style id="textfield">
<painter method="textFieldBackground" idref="gradient"/>
</style>
<bind style="textfield" type="region" key="textfield"/>
</synth>
Where the GradientPainter class looks like this:
public class GradientPainter extends SynthPainter {
public void paintTextFieldBackground(SynthContext context,
Graphics g, int x, int y,
int w, int h) {
// For simplicity this always recreates the GradientPaint. In a
// real app you should cache this to avoid garbage.
Graphics2D g2 = (Graphics2D)g;
g2.setPaint(new GradientPaint((float)x, (float)y, Color.WHITE,
(float)(x + w), (float)(y + h), Color.RED));
g2.fillRect(x, y, w, h);
g2.setPaint(null);
}
}

Conclusion

In this lesson, we have covered the use of the javax.swing.plaf.synth package to create a custom
look and feel. The emphasis of the lesson has been on using an external XML file to define the look
and feel. The next lesson presents a sample application that creates a search dialog box using the
Synth framework with an XML file.

A Synth Example
In the lesson titled A GroupLayout Example , GroupLayout was used to create a search dialog box
called "Find." The program that created the dialog box, Find.java, used the cross platform
("Metal") look and feel with the "Ocean" theme:

928
This lesson creates the same dialog box with Synth, using an external XML file. Here is the listing of
the SynthDialog.javafile.

SynthDialog.java is exactly the same as Find.java except for the initLookAndFeel() method,
which has been altered to use the Synth look and feel with an external file called synthDemo.xml.
Here is the new initLookAndFeel() method:

private static void initLookAndFeel() {


SynthLookAndFeel lookAndFeel = new SynthLookAndFeel();

// SynthLookAndFeel load() method throws a checked exception


// (java.text.ParseException) so it must be handled
try {
lookAndFeel.load(SynthDialog.class.getResourceAsStream("synthDemo.xml"),

SynthDialog.class);
UIManager.setLookAndFeel(lookAndFeel);
}

catch (ParseException e) {
System.err.println("Couldn't get specified look and feel ("
+ lookAndFeel
+ "), for some reason.");
System.err.println("Using the default look and feel.");
e.printStackTrace();
}
}

The XML File

The XML file, synthDemo.xml, begins with a style bound to all regions. It is good practice to do this
to ensure that regions without a style bound to them will contain something. This style makes all
regions paint their background in an opaque color. It also sets a default font and default colors.

<!-- Style that all regions will use -->


<style id="backingStyle">
<!-- Make all the regions opaque-->
<opaque value="TRUE"/>
<font name="Dialog" size="14"/>
<state>
<color value="#D8D987" type="BACKGROUND"/>
<color value="RED" type="FOREGROUND"/>
</state>
</style>
<bind style="backingStyle" type="region" key=".*"/>

Notes:

1. The color definitions must be inside a <state> element. This permits changing colors depending on
state. The <state> element in backingStyle has no attributes and is therefore applied to all regions,

929
irrespective of their state. If a region has other states, the states are merged with precedence given to
state definitions that appear later in the file.

2. The font definition is not inside a <state> element because the font should not change when there
is a change of state (many components are sized depending on their font, and a change in font could
cause components to change in size unintentionally).

The next <style> element defined is for the text field, which is painted using an image.

<style id="textfield">
<insets top="4" left="6" bottom="4" right="6"/>
<state>
<font name="Verdana" size="14"/>
<color value="#D2DFF2" type="BACKGROUND"/>
<color value="#000000" type="TEXT_FOREGROUND"/>
</state>
<imagePainter method="textFieldBorder" path="images/textfield.png"
sourceInsets="4 6 4 6" paintCenter="false"/>
</style>
<bind style="textfield" type="region" key="TextField"/>

Notes:

1. The font and color definitions override the definitions in backingStyle.

2. The insets and sourceInsets are given the same values, which is just a coincidence because
they are unrelated to each other.

3. The BACKGROUND color, #D2DFF2, is a pale blue—the same color as the background in the
image, textfield.png.

4. paintCenter is false so that you can see the background color.

The next <style> element is for buttons that are painted with different images, depending on the
button state. When the mouse passes over the button, its appearance changes. When it is clicked
(PRESSED) the image changes again.

<style id="button">
<!-- Shift the text one pixel when pressed -->
<property key="Button.textShiftOffset" type="integer" value="1"/>
<!-- set size of buttons -->
<insets top="15" left="20" bottom="15" right="20"/>
<state>
<imagePainter method="buttonBackground" path="images/button.png"
sourceInsets="10 10 10 10" />
<font name="Dialog" size="16"/>
<color type="TEXT_FOREGROUND" value="#FFFFFF"/>
</state>

<state value="PRESSED">
<imagePainter method="buttonBackground"
path="images/button_press.png"
sourceInsets="10 10 10 10" />

930
</state>

<state value="MOUSE_OVER">
<imagePainter method="buttonBackground"
path="images/button_over.png"
sourceInsets="10 10 10 10" />
</state>
</style>
<bind style="button" type="region" key="Button"/>

Notes:

1. The font and color definitions inside the <state> element without attributes apply to all button
states. This is because the definitions of all states that apply (and the <state> element without
attributes is one of these) will merge and there are no other font and color definitions that might take
precedence.

2. The sourceInsets values are large enough that the curved corners of the button image will not be
stretched.

3. The order of the PRESSED and MOUSE_OVER states is important. Since the mouse will always be
over the button when it is pressed, both states will apply to a pressed button and the first state defined
(PRESSED) will apply. When the mouse is over the button but it is not pressed, only the MOUSE_OVER
state applies. If the order of the PRESSED and MOUSE_OVER states is reversed, the PRESSED state image
will never be used.

The next <style> element is for checkboxes that are painted with different icons, depending on the
checkbox state.

<style id="checkbox">
<imageIcon id="check_off" path="images/checkbox_off.png"/>
<imageIcon id="check_on" path="images/checkbox_on.png"/>
<property key="CheckBox.icon" value="check_off"/>
<state value="SELECTED">
<property key="CheckBox.icon" value="check_on"/>
</state>
</style>
<bind style="checkbox" type="region" key="Checkbox"/>

Notes:

1. You must use the <imageIcon> element to define any icons to be used.

2. The <insets> element and the sourceInsets attribute are not used with icons because they are
rendered in their fixed size and are not stretched.

3. The icon used to render the checkbox is the icon named in the CheckBox.icon property. (see
javax/swing/plaf/synth/doc-files/componentProperties.html), which is the icon with
id="check_off" unless the checkbox state is SELECTED.

931
The synthDemo.xml file is constructed of the styles presented above, wrapped in <synth></synth>
tags. You can open the completed file by clicking synthDemo.xml.

Try this: Click the Launch button to run the SynthDialog example using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.

932

Anda mungkin juga menyukai