Understanding
Containers
-- And Content Panes, JRootPanes,
et al
If
you've started the big move from the Abstract Windowing Toolkit
(AWT) to Swing, you've probably noticed by now that containers don't
work the same way in Swing that they did back during the days of
AWT.
Because of the intricacies involved in making lightweight
and heavyweight components work together in Swing, you can't just
add() anything
you like to a JFrame, JApplet, or JDialog object; first, you must
get something called a "content pane," and then you
can add Swing components to that.
If you want to find out why that's true -- and if you'd like
to learn more about adding components to containers in Swing --
this article is what you've been waiting for. It will tell you
everything you need to know about content panes and JRootPanes,
and will show you how to add components to Swing containers.
To help you understand the points covered in the article,
we've provided some source-code snippets
that you can download, study, and incorporate into your own programs.
By Eric Armstrong
To understanding Swing's window,
frame, and dialog containers, it's essential to have an understanding
of a new Swing class named JRootPane. That's because every
component container used in Swing contains an object called a content
pane, which is the actual repository for components that are
added to the container. This content pane is an object of the JRootPane
class, which is the focus of this article.
The major topics covered in this article are:
We've
provided some downloadable source-code snippets that illustrate
the most important points covered in this article. To view or
download the code, just follow this
link.
The code samples we've supplied will show you how to create:
- A panel with a titled border.
- A tabbed pane (with three tabs on the left).
- A split pane (horizontally split with two panels).
- Internal frames containing messages.
- A ScrollPane that scrolls a panel.
Because a container's contents are actually stored in its content
pane, you never add() a
component to a Swing container directly. Instead, you add the component
to the container's content pane. You do that by calling a method
named getContentPane(),
using a statement similar to this:
aContainer.getContentPane().add(aComponent)
There five Swing
container classes that delegate their contents to a JRootPane instance:
(For more information about lightweight and heavyweight components,
see the article titled "Mixing
Heavy and Light Components" in The Swing Connection
archive.)
The following diagram shows the relationships that these five kinds
of containers have with each other, and with four heavyweight parent
container classes that reside in the java.awt package:
Window, Applet, Frame, and Dialog.
All the heavyweight components shown in the diagram appear inside
the blue panel at the top of the picture, which is labeled java.awt.
The four heavyweight components in the Swing package (JFrame,
JDialog, JWindow, and JApplet) have pink interiors and appear
in the center section of the diagram. They extend the four
heavyweight AWT classes -- Window, Applet, Frame, and Dialog
-- that appear in the java.awt panel.
Also shown in the diagram is one lightweight Swing component --
JInternalPane -- which doesn't extend any AWT class.
All five of the Swing containers shown in the drawing implement
the Swing RootPaneContainerapi
interface, which defines the JRootPaneapi
class. Notice that all five Swing containers delegate their
operations to the JRootPane class, with is shown at the bottom
of the diagram with a little handle on top.
To obtain the JRootPane object that contains a particular component,
call the JComponent method
getRootPane()
This section shows
how the JRootPane and JLayeredPaneapi
classes work together to provide the architectural underpinnings
of the Swing container hierarchy.
JRootPane
A JRootPane object
is made up of a glassPane, a contentPane, and an optional
menu bar. The contentPane and the menu bar, if there is one, are
managed by a JLayeredPane object. The following diagram shows this
arrangement.
Although a menu bar is optional in a JRootPane object, the layeredPane,
contentPane, and glassPane shown in this diagram always exist.
Attempting to set them to null generates an exception.
JRootPane's contentPane
It´s very important to remember that a JRootPane object
cannot have children. So you cannot add a component directly
to a JRootPane. That means you can´t do this:
rootPane.add(child);
// NO!!
Instead,
add your component to the contentPane
of the JRootPane by calling a JRootPane
method named getContentPane(),
like this:
rootPane.getContentPane().add(child);
The same principle
holds true for setting layout managers, removing components, listing
children, and so on. All these methods are invoked on a contentPane
instead of on a JRootPane object.
The
default layout manager for a contentPane is the BorderLayout manager.
But JRootPane uses a custom LayoutManager. So, when you want to
change the layout manager for components you have added to a JRootPane,
be sure to use code like this:
rootPane.getContentPane().setLayout
(new BoxLayout());
How getContentPane() methods are delegated
All the major frame and window containers (JFrame, JWindow,
and so on) delegate their getContentPane()
methods to the JRootPane object they contain. These
JRootPane objects, in turn, delegate their getContentPane()
methods to their JLayeredPane instances.
However, operations such as add(child)cannot
be delegated down to the contentPane, because the contentPane could
be an arbitrary component. For example, if the contentPane were
a JScrollPaneapi,
then add(child)
would not be a valid method -- you would instead use setViewportView().
JRootPane's menuBar
If a JMenuBar component is set on the JRootPane, it is
positioned along the upper edge of the frame. The contentPane is
adjusted in location and size to fill the remaining area.
JRootPane's layeredPane
The layeredPane is the parent of all children in the JRootPane.
It is an instance of JLayeredPane, which provides the ability to
add components at several layers. This capability is very useful
when working with popup menus, dialog boxes, and dragging -- situations
in which you need to place a component on top of all other components
in the pane.
JRootPane's glassPane
The glassPane sits on top of all other components in the
JRootPane. This positioning makes it possible to intercept mouse
events, which is useful for dragging one component across another.
This positioning is also useful for drawing.
The glassPane can be set up for drawing because the glassPane,
like the contentPane, can be an arbitrary component. Lines and images
on the glassPane can then range over the frames underneath without
being limited by their boundaries.
Developers can use setVisible()
on the glassPane to control when the glassPane displays over the
other children. By default, the glassPane is not visible. The default
glassPane is an instance of JPanel.
The custom LayoutManager used by JRootPane ensures that:
- The glassPane, if present, fills the entire viewable area of
the JRootPane (bounds - insets).
- The layeredPane also fills the entire viewable area of the JRootPane
(bounds - insets).
- The menuBar is positioned at the upper edge of the layeredPane.
- The contentPane fills the entire viewable area, minus the MenuBar,
if there is one.
If you replace the LayoutManager of the JRootPane, you are responsible
for managing all these views. So, ordinarily, you will want to be
sure that you change the layout manager for the contentPane rather
than for the JRootPane itself!
JLayeredPane
JLayeredPane
adds depth to a JFC/Swing container, allowing components to overlap
each other when needed. An Integer object specifies each component's
depth in the container, where higher-numbered components sit "on
top" of other components.
For convenience, JLayeredPane divides the depth range into several
different layers. Putting a component into one of those layers makes
it easy to ensure that components overlap properly, without having
to worry about specifying numbers for specific depths.
The depth-range layers used by JLayeredPane are:
- DEFAULT_LAYER: The standard layer, where
most components go. This the bottommost layer.
- PALETTE_LAYER: The palette layer sits
over the default layer. Useful for floating toolbars and palettes,
so they can be positioned above other components.
- MODAL_LAYER: The layer used for
modal dialogs. They will appear on top of any toolbars, palettes,
or standard components in the container.
- POPUP_LAYER: The popup layer displays
above dialogs. That way, the popup windows associated with combo
boxes, tooltips, and other help text will appear above the component,
palette, or dialog that generated them.
- DRAG_LAYER: When the user is dragging
a component, reassigning it to the drag layer ensures that it
is positioned over every other component in the container. When
finished dragging, it can be reassigned to its normal layer.
The JLayeredPane methods moveToFront(Component),
moveToBack(Component) and
setPosition can be used
to reposition a component within its layer. The setLayer()
method can also be used to change the component's current layer.
Swing containers typically used in GUIs are JPanel, JSplitPane,
JTabbedPane, JDesktopPane, JInternalFrame, JScrollPane, JViewport,
JTextPane, and JEditorPane. You can add other containers and components
to these containers, combining components and their containers in
various ways to get the interface you want.
JPanel
A JPanel object is
a simple container with no fancy additions. It's a "JComponent
with real estate." (JComponent is itself an extension of Container.)
JPanels are typically used with a layout manager to divide an area
into discrete sections, each of which can then get its own layout
manager.
The default layout for a JPanel is flow layout. The default glassPane
for a JRootPane is a JPanel.
The Box container
The Box container
uses BoxLayoutapi,
a layout manager that lets you line up components added to the Box
either horizontally or vertically. Nesting multiple Box components
gives you an easy way to create sophisticated arrangements without
the complexity of AWT's GridBagLayout manager.
JSplitPane
A JSplitPaneapi
container manages two panes that are separated horizontally or vertically
by a divider that can be repositioned by the user. You can choose
which pane to add a component to. You can specify the components
with setLeftComponent() and
setRightComponent(),
or setTopComponent() and
setBottomComponent(). In these methods, "Left"
is equivalent to "Top" and "Right" is equivalent
to "Bottom" -- so if you change the arrangement, your
existing code still works. Subsequent adds to the same pane replace
its contents with the new object.
JTabbedPane
A
JTabbedPaneapi
object manages multiple panes that completely overlap each other.
The user can select a pane to view by clicking on a "tab"
attached to the pane (like the tab on a file folder). The tabs can
be positioned to the top, bottom, left side, or right side of the
container.
JDesktopPane and JInternalFrame
JDesktopPane is basically
a container for one or more JInternalFrames used to create a multiple-document
interface or virtual desktop.
JDesktopPane extends JLayeredPane to manage the potentially overlapping
internal frames. It also maintains a reference to an instance of
DesktopManager that is set by the UI class for the current Look
and Feel (L&F). The internal frames delegate L&F-specific
operations to the DesktopManager (for example, how an internal frame
is iconized).
JScrollPane and JViewport
A JScrollPane consists
of JScrollBars, a JViewport, and the wiring between them, as shown
in the following diagram. The JViewPort provides a window,
or "viewport" onto a data source -- for example, a text
file. That data source is the "scrollable client" (or
data model) displayed by the JViewport view. The scrollable client
can be any component, but rather than "adding" it to the
JScrollPane container, you specify it as part of the constructor
or specify it using the setViewportView()
method. (Subsequent calls to that method replace the original view.)
Along with its scroll bars and viewport, a JScrollPane can have
a column header and a row header (see diagram). Each
of these is a JViewport object that you specify with setRowHeaderView()
and setColumnHeaderView().
The column header viewport automatically scrolls left and
right, tracking the left-right scrolling of the main viewport. (It
never scrolls vertically, however.) The row header acts in
a similar fashion.
By default, the corners are empty. You can put a component
into a corner using setCorner(),
in case you there is some function or decoration you would like
to add to the scroll pane. The size of corner components is entirely
determined by the size of the headers and scroll bars that surround
them.
To add a border around the main viewport, you can use setViewportBorder().
(Of course, you can also add a border around the whole scroll pane
using setBorder().)
JTextPane and JEditorPane
JTextPane and JEditorPane
sound like general containers (because they are named "pane"),
but in reality these are highly specialized containers that can
display text and provide basic editing capabilities.
JTextPane, as its name suggests, displays text. Unlike JTextArea,
JTextPane can handle multiple fonts in the same display.
JEditorPane provides basic editing capabilities for mixed-font
text. The html and rtf packages provide additional
capabilities for text formatted using the HyperText Markup Language
(HTML) and the Rich Text Format (RTF).
For much more information on Swing's text components, see the article
titled "Using the
Swing Text Package."
In Swing, as in pre-Swing versions of AWT, the recommended
way to place components inside containers is to use a layout
manager.
BoxLayout
BoxLayout
is a layout manager that allows multiple components to be laid out
either vertically or horizontally. The components in a box layout
do not wrap -- so, for example, a vertical arrangement of components
stays vertically arranged when the frame is resized.
Nesting multiple panels with different combinations of horizontal
and vertical gives an effect similar to the familiar AWT GridBagLayout,
without the complexity. The prededing diagram
shows two panels arranged horizontally, each of which contains three
components arranged vertically.
The Box container uses BoxLayout (unlike JPanel, which defaults
to flow layout). You can nest multiple boxes and add components
to them to get the arrangement you want.
For more information about Swing´s box layout, see the Swing layout-manager
chapter in The
Java Tutorial.
OverlayLayout
OverlayLayout
is a small-footprint layout manager that centers an object in its
area. Used by JButton and its subclasses. Unlike BorderLayout's
centering, OverlayLayout does not expand the component to fill the
available space, but instead leaves it at is preferred size.
ScrollPaneLayout and ViewportLayout
ScrollPaneLayout/ViewportLayout
are the layout managers used by JScrollPaneapi
and JViewportapi.
To understand the behavior of Swing containers, it helps to have
an understanding of the differences between lightweight and heavyweight
components.
Almost all Swing components are said to be lightweight because
they don't rely on user-interface code that's native to whatever
operating system they're running on. Consequently, they can usually
be implemented using only about half the classes that earlier heavyweight
components require.
Heavyweight components are used in programs that were written before
Swing was introduced, the creators of Swing have made it possible
to mix heavyweight and lightweight components in the same program.
To learn how to do that, see the article titled "Mixing
Heavy and Light Components" in The Swing Connection
archive.
The only heavyweight components used in Swing are:
- swing.JFrame
- swing.JDialog
- swing.JWindow
- swing.JApplet
- All AWT components (awt.*), except those noted
below.
All other Swing components are lightweight. Swing's lightweight
components are:
- awt.Component
- awt.Container
- swing.JComponent
- All other Swing classes (swing.*) and all packages
under it, except for those noted above.
__________
Eric Armstrong is an author and magazine contributor who has
recently been specializing in Swing and the JavaTM
programming language. He is the author of a new book, "Java
JBuilder Bible," which teaches object-oriented programming
in the Java programming language using the Borland JBuilder IDE.
The book is scheduled for summer publication by IDG.
|