A
container is a kind of component that holds and manages other
components. JComponent
objects can be containers,
because the JComponent
class descends from the
Container
class.
Three of the most useful container types are
JFrame
, JPanel
, and
JApplet
. A JFrame
is a
top-level window on your display.
JFrame
is derived from JWindow
,
which is pretty much the same but lacks a border. A
JPanel
is a generic container element used to
group components inside of JFrame
s and other
JPanel
s. The JApplet
class is a
kind of container that provides the foundation for
applets that run inside web browsers.
Like every other JComponent
, a
JApplet
has the ability to contain other user
interface components. You can also use the
JComponent
class directly, like a
JPanel
, to hold components inside of another
container. With the exception of JFrame
and
JWindow
, all the components and containers in
Swing are lightweight.
A container maintains the list of
“child” components that it manages, and has
methods for dealing with those components. Note that this child
relationship refers to a visual hierarchy, not a subclass/superclass
hierarchy. By themselves, most components aren’t very useful
until they are added to a container and displayed. The add( )
method of the Container
class adds a
component to the container. Thereafter, this component can be
displayed in the container’s display area and positioned by its
layout manager. You can remove a component from a container with the
remove( )
method.
A layout manager
is an object that controls the placement and sizing of
components
within the display area of a container. A layout manager is like a
window manager in a display system; it controls where the components
go and how big they are. Every container has a default layout
manager, but you can install a new one by calling the
container’s setLayout( )
method.
Swing comes with a few layout
managers that implement common layout schemes. The default layout
manager for a JPanel
is a
FlowLayout
, which tries to place objects at their
preferred size from left to right and top to bottom in the container.
The default for a JFrame
is a
BorderLayout
, which places a limited number of
objects at named locations within the window, such as
NORTH
, SOUTH
, and
CENTER
. Another layout manager,
GridLayout
, arranges components in a
rectangular grid. The most general (and difficult to use) layout
manager is GridBagLayout
, which lets you do the
kinds of things you can do with HTML tables. (We’ll get into
the details of all of these layout managers in Chapter 16.)
When
you add a component to a container, you’ll often use the
version of add( )
that takes a single
Component
as an argument. However, if you’re
using a layout manager that uses “constraints,” like
BorderLayout
or GridBagLayout
,
you must specify additional information about where to put the new
component. For that you can use the version that takes a constraint
object. For example, here’s how to place a component at the top
edge of a container that uses a BorderLayout
manager:
myContainer.add(myComponent, BorderLayout.NORTH);
In this case, the constraint object is the static member variable
NORTH
. GridBagLayout
uses a
much more complex constraint object to specify positioning.
Insets specify a
container’s
margins; the space specified by the container’s insets
won’t be used by a layout manager. Insets are described by an
Insets
object, which has four public
int
fields: top
,
bottom
, left
, and
right
. You normally don’t need to worry
about the insets; the container will set them automatically, taking
into account extras like the menu bar that may appear at the top of a
frame. To find out the insets, call the component’s
getInsets( )
method, which returns an Insets
object.
With the standard layout
managers, components are not allowed to overlap. However, if you use
custom-built layout managers or absolute positioning, components
within a container may overlap. If they do, the order in which
components were added to a container matters. When components overlap
they are “stacked” in the order in which they were added:
the first component added to the container is on top; the last is on
the bottom. To give you more control over stacking, two additional
forms of the add( )
method take an additional
integer argument that lets you specify the component’s exact
position in the container’s stacking order.
A layout manager arranges the components in a container only when asked to. Several things can mess up a container after it’s initially laid out:
Changing its size
Resizing or moving one of its child components
Adding, showing, removing, or hiding a child component
Any of these actions cause the container or its components to be
marked invalid
. This means it needs
to have its child components readjusted by its layout manager. In
most cases, Swing will re-layout container automatically. There are a
few cases where you may need to tell Swing to fix things. One example
is when you change the preferred size of a component. To fix up the
layout, call the revalidate( )
method. revalidate( )
marks a component (or
container) invalid and calls Container
’s
doLayout( )
method, which asks the layout manager
to do its job. In addition, revalidate( )
also
notes that the Container
has been fixed (i.e.,
it’s valid again) and looks at each child component of the
container, recursively validating any containers or components that
are also messed up.
So if you have a small JPanel
—say a keypad
holding some buttons—and you change the preferred size of the
JPanel
by calling its setPreferredSize( )
method, you should also call
revalidate( )
on the panel or its container. The
layout manager of the panel’s container may then reposition or
resize the keypad. It also automatically calls revalidate( )
for the keypad itself, so that it can rearrange its
buttons to fit inside its new area.
All components, not just containers, maintain a notion of when they
are valid or invalid. If the size, location, or internal layout of a
component changes, its revalidate( )
will
automatically be called.
Child containers are validated only if they are invalid. That means
that if you have an invalid component nested inside a valid component
and you validate a container above both, the invalid component may
never be reached. To help avoid this situation, the
invalidate( )
method that marks a container as
dirty automatically marks parent containers as well, all the way up
the container hierarchy.
There are a few additional tools of the Container
class that we should mention:
-
Component[]
getComponents
( )
Returns the container’s components in an array.
-
void
list
(PrintWriter
out
, int
indent
)
Generates a list of the components in this container and writes them to the specified
PrintWriter
.-
Component
getComponentAt
(int
x
, int
y
)
Tells you what component is at the specified coordinates in the container’s coordinate system.
You
can use the
ContainerListener
interface to automate the setting
up of a container’s new components. A container that implements
this interface can receive an event whenever it gains or loses a
component. This facility makes it easy for a container to
micro-manage its components.
Windows and frames are the top-level
containers for Java components. A
JWindow
is simply a
plain, graphical screen that displays in your windowing system.
Windows have no frills; they are mainly suitable for making
“splash” screens and dialogs.
JFrame
, on the other hand, is a subclass of
JWindow
that has a border and can hold a menu bar.
You can drag a frame around on the screen and resize it, using the
ordinary controls for your windowing environment. Figure 13.2 shows a JFrame
on the
left and a JWindow
on the right.
All other Swing components and containers must be held, at some
level, inside of a JWindow
or
JFrame
.
Applets are a kind of
Container
. Even applets must be housed in a frame
or window, though normally you don’t see an applet’s
parent frame because it is part of (or simply is) the browser or
appletviewer displaying the applet.
JFrame
s and JWindow
s are the
only components that can be displayed without being added or attached
to another Container
. After creating a
JFrame
or JWindow
, you can call
the setVisible( )
method to display it. The following short
application creates a JFrame
and a
JWindow
and displays them side by side, just like
in Figure 13.2.
//file: TopLevelWindows.java import javax.swing.*; public class TopLevelWindows { public static void main(String[] args) { JFrame f = new JFrame("The Frame"); f.setSize(300, 300); f.setLocation(100, 100); JWindow w = new JWindow( ); w.setSize(300, 300); w.setLocation(500, 100); f.setVisible(true); w.setVisible(true); } }
The JFrame
constructor can take a
String
argument that supplies a title, displayed
in the JFrame
’s title bar. (Another approach
would be to create the JFrame
with no title and
call setTitle( )
to supply the title later.) The JFrame
’s
size and location on your desktop is
determined by the calls to setSize( )
and
setLocation( )
. After creating the
JFrame
, we create a JWindow
in
almost exactly the same way. The JWindow
doesn’t have a title bar, so there are no arguments to the
JWindow
constructor.
Once the JFrame
and JWindow
are
set up, we call setVisible(true)
to get them on
the screen. The setVisible( )
method returns
immediately, without blocking. Fortunately, our application does not
exit, even though we’ve reached the end of the main( )
method, because the windows are still visible. You can
close the JFrame
by clicking on the close button
in the title bar. JFrame
’s default behavior
is to hide itself when you click on the box by calling
setVisible(false)
. You can alter this behavior by
calling the setDefaultCloseOperation( )
method, or by adding an event
listener, which we’ll cover later.
There’s no way to close the JWindow
inside
our application. You will have to hit Ctrl-C or whatever keystroke
kills a process on your machine to stop execution of the
TopLevelWindows
application.
The setLocation( )
method of the
Component
class can be used on a
JFrame
or JWindow
to set
its position on the screen. The x
and
y
coordinates are relative to the screen’s
origin (the top left corner).
You can use the toFront( )
and toBack( )
methods to place a JFrame
or
JWindow
in front of, or behind, other windows. By
default, a user is allowed to resize a JFrame
, but
you can prevent resizing by calling
setResizable(false)
before showing the
JFrame
.
On most systems, frames can be
"iconified”; that is, they
can be represented by a little icon image. You can get and set a
frame’s icon image by calling getIconImage( )
and
setIconImage( )
. As you can with all components,
you set the
cursor by calling the setCursor( )
method.
Windows and
frames don’t
behave exactly like regular containers.
With other containers, you can add child components with the
add( )
method. JFrame
and
JWindow
have some extra stuff in them (mostly to
support Swing’s peerless components), so you can’t just
add( )
components directly. Instead, you need to
add the components to the associated content
pane. The content pane is just a
Container
that covers the visible area of the
JFrame
or JWindow
. Whenever you create a new
JFrame
or JWindow
, a content
pane is automatically created for you. You can retrieve it with
getContentPane( )
.
Here’s another example that creates a JFrame
and adds some components to its content pane:
//file: MangoMango1.java import java.awt.*; import javax.swing.*; public class MangoMango1 { public static void main(String[] args) { JFrame f = new JFrame("The Frame"); f.setLocation(100, 100); Container content = f.getContentPane( ); content.setLayout(new FlowLayout( )); content.add(new JLabel("Mango")); content.add(new JButton("Mango")); f.pack( ); f.setVisible(true); } }
The call to JFrame
’s pack( )
method
tells the frame window to resize itself so it exactly fits its
components. Instead of having to determine the size of the
JFrame
, we just tell it to be “just big
enough.” If you do ever
want to set the absolute size of the JFrame
yourself, call setSize( )
instead.
If you create your own Container
, you can make it
the content pane of a JFrame
or
JWindow
by passing it to setContentPane( )
.
Using this strategy, you could rewrite the previous example as
follows:
//file: MangoMango2.java import java.awt.*; import javax.swing.*; public class MangoMango2 { public static void main(String[] args) { JFrame f = new JFrame("The Frame"); f.setLocation(100, 100); Container content = new JPanel( ); content.add(new JLabel("Mango")); content.add(new JButton("Mango")); f.setContentPane(content); f.pack( ); f.setVisible(true); } }
We’ll cover labels and buttons in Chapter 14
and layouts in Chapter 16. The important thing to
remember is that you can’t add components directly to a
JFrame
or JWindow
. Instead, add
them to the automatically created content pane, or create an
entirely
new content pane.
Get Learning Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.