Vous êtes sur la page 1sur 73

1

About the JFC and Swing

JFC is short for Java Foundation Classes, which encompass a group of features for
building graphical user interfaces (GUIs) and adding rich graphics functionality
and interactivity to Java applications. It is defined as containing the features shown
in the table below.

Features of the Java Foundation Classes


Feature Description
Includes everything from buttons to split panes to tables. Many
Swing GUI
components are capable of sorting, printing, and drag and drop,
Components
to name a few of the supported features.
The look and feel of Swing applications is pluggable, allowing
a choice of look and feel. For example, the same program can
use either the Java or the Windows look and feel. Additionally,
Pluggable Look-
the Java platform supports the GTK+ look and feel, which
and-Feel Support
makes hundreds of existing look and feels available to Swing
programs. Many more look-and-feel packages are available
from various sources.
Enables assistive technologies, such as screen readers and
Accessibility API
Braille displays, to get information from the user interface.
Enables developers to easily incorporate high-quality 2D
graphics, text, and images in applications and applets. Java 2D
Java 2D API
includes extensive APIs for generating and sending high-
quality output to printing devices.
Allows developers to build applications that can interact with
users worldwide in their own languages and cultural
conventions. With the input method framework developers can
Internationalization
build applications that accept text in languages that use
thousands of different characters, such as Japanese, Chinese, or
Korean.

Which Swing Packages Should I Use?

The Swing API is powerful, flexible — and immense. The Swing API has 18
public packages:
javax.accessibility javax.swing.plaf javax.swing.text
2

javax.swing javax.swing.plaf.basic javax.swing.text.html


javax.swing.border javax.swing.plaf.metal javax.swing.text.html.parser
javax.swing.colorchooser javax.swing.plaf.multi javax.swing.text.rtf
javax.swing.event javax.swing.plaf.synth javax.swing.tree
javax.swing.filechooser javax.swing.table javax.swing.undo

Fortunately, most programs use only a small subset of the API. This trail sorts out
the API for you, giving you examples of common code and pointing you to
methods and classes you're likely to need. Most of the code in this trail uses only
one or two Swing packages:

 javax.swing
 javax.swing.event (not always required)

Lesson: Learning Swing with the NetBeans IDE

This lesson provides an introduction to Graphical User Interface (GUI)


programming with Swing and the NetBeans IDE. NetBeans IDE is a free, open-
source, cross-platform integrated development environment with built-in support
for the Java programming language. It offers many advantages over coding with a
text editor; we recommend its use whenever possible. If you have not yet read the
above lesson, please take a moment to do so now. It provides valuable information
about downloading and installing the JDK and NetBeans IDE.
The goal of this lesson is to introduce the Swing API by designing a simple
application that converts temperature from Celsius to Fahrenheit. Its GUI will be
basic, focusing on only a subset of the available Swing components. We will use
the NetBeans IDE GUI builder, which makes user interface creation a simple
matter of drag and drop. Its automatic code generation feature simplifies the GUI
development process, letting you focus on the application logic instead of the
underlying infrastructure.
Because this lesson is a step-by-step checklist of specific actions to take, we
recommend that you run the NetBeans IDE and perform each step as you read
along. This will be the quickest and easiest way to begin programming with Swing.
If you are unable to do so, simply reading along should still be useful, since each
step is illustrated with screenshots.
If you prefer the traditional approach of programming each component manually
(without the assistance of an IDE), think of this lesson as an entry point into the
lower-level discussions already provided elsewhere in the tutorial. Hyperlinks in
3

each discussion will take you to related lessons, should you wish to learn such
lower-level details.
The finished GUI for this application will look as follows:

The CelsiusConverter Application.

From an end-user's perspective, usage is simple: enter a temperature (in Celsius)


into the text box, click the "Convert" button, and watch the converted temperature
(in Fahrenheit) appear on screen. The minimize, maximize, and close buttons will
behave as expected, and the application will also have a title that appears along the
top of the window.
From a programmer's perspective, we will write the application in two main stages.
First, we will populate the GUI with the various Swing components and arrange
them as shown above. Then, we will add the application logic, so that the program
actually performs a conversion when the user presses the "Convert" button.

Setting up the CelsiusConverter Project

If you have worked with the NetBeans IDE in the past, much of this section will
look familiar, since the initial steps are similar for most projects. Still, the
following steps describe settings that are specific to this application, so take care to
follow them closely.

Step 1: Create a New Project

To create a new project, launch the NetBeans IDE and choose New Project from
the File menu:
4

Creating a New Project

Keyboard shortcuts for each command appear on the far right of each menu item.
The look and feel of the NetBeans IDE may vary across platforms, but the
functionality will remain the same.

Step 2: Choose General -> Java Application

Next, select General from the Categories column, and Java Application from the
Projects column:
5

You may notice mention of "J2SE" in the description pane; that is the old name for what is now
known as the "Java SE" platform. Press the button labeled "Next" to proceed.

Step 3: Set a Project Name

Now enter "CelsiusConverterProject" as the project name. You can leave the
Project Location and Project Folder fields set to their default values, or click the
Browse button to choose an alternate location on your system.

Make sure to deselect the "Create Main Class" checkbox; leaving this option
selected generates a new class as the main entry point for the application, but our
main GUI window (created in the next step) will serve that purpose, so checking
this box is not necessary. Click the "Finish" button when you are done.
6

When the IDE finishes loading, you will see a screen similar to the above. All
panes will be empty except for the Projects pane in the upper left hand corner,
which shows the newly created project.

Step 4: Add a JFrame Form

Now right-click the CelsiusConverterProject name and choose New -> JFrame
Form (JFrame is the Swing class responsible for the main frame for your
application.) You will learn how to designate this class as the application's entry
point later in this lesson.
7

Step 5: Name the GUI Class

Next, type CelsiusConverterGUI as the class name, and learn as the package name.
You can actually name this package anything you want, but here we are following
the tutorial convention of naming the package after the lesson in which is resides.

The remainder of the fields should automatically be filled in, as shown above.
Click the Finish button when you are done.
8

When the IDE finishes loading, the right pane will display a design-time, graphical
view of the CelsiusConverterGUI. It is on this screen that you will visually drag,
drop, and manipulate the various Swing components.

NetBeans IDE Basics

It is not necessary to learn every feature of the NetBeans IDE before exploring its
GUI creation capabilities. In fact, the only features that you really need to
understand are the Palette, the Design Area, the Property Editor, and the
Inspector. We will discuss these features below.

The Palette

The Palette contains all of the components offered by the Swing API. You can
probably already guess what many of these components are for, even if this is your
first time using them (JLabel is a text label, JList is a drop-down list, etc.)

From this list, our application will use only JLabel (a basic text label), JTextField
(for the user to enter the temperature), and JButton (to convert the temperature
from Celsius to Fahrenheit.)

The Design Area

The Design Area is where you will visually construct your GUI. It has two views:
source view, and design view. Design view is the default, as shown below. You can
toggle between views at any time by clicking their respective tabs.
9

The figure above shows a single JFrame object, as represented by the large shaded
rectangle with blue border. Commonly expected behavior (such as quitting when
the user clicks the "close" button) is auto-generated by the IDE and appears in the
source view between uneditable blue sections of code known as guarded blocks.

A quick look at the source view reveals that the IDE has created a private method
named initComponents, which initializes the various components of the GUI. It
also tells the application to "exit on close", performs some layout-specific tasks,
then packs the (soon to be added) components together on screen.
10

The Property Editor

The Property Editor does what its name implies: it allows you to edit the properties
of each component. The Property Editor is intuitive to use; in it you will see a
series of rows — one row per property — that you can click and edit without
entering the source code directly. The following figure shows the Property Editor
for the newly added JFrame object:

The screenshot above shows the various properties of this object, such as
background color, foreground color, font, and cursor.

The Inspector

The last component of the NetBeans IDE that we will use in this lesson is the
Inspector:
11

The Inspector

The Inspector provides a graphical representation of your application's


components. We will use the Inspector only once, to change a few variable names
to something other than their defaults.

Creating the CelsiusConverter GUI


This section explains how to use the NetBeans IDE to create the application's GUI.
As you drag each component from the Palette to the Design Area, the IDE auto-
generates the appropriate source code.

Step 1: Set the Title

First, set the title of the application's JFrame to "Celsius Converter", by single-
clicking the JFrame in the Inspector:

Selecting the JFrame

Then, set its title with the Property Editor:


12

Setting the Title

You can set the title by either double-clicking the title property and entering the
new text directly, or by clicking the button and entering the title in the provided
field. Or, as a shortcut, you could single-click the JFrame of the inspector and enter
its new text directly without using the property editor.

Step 2: Add a JTextField

Next, drag a JTextField from the Palette to the upper left corner of the Design
Area. As you approach the upper left corner, the GUI builder provides visual cues
(dashed lines) that suggest the appropriate spacing. Using these cues as a guide,
drop a JTextField into the upper left hand corner of the window as shown below:
13

Adding a JTextField

You may be tempted to erase the default text "JTextField1", but just leave it in
place for now. We will replace it later in this lesson as we make the final
adjustments to each component. For more information about this component, see
How to Use Text Fields.

Step 3: Add a JLabel

Next, drag a JLabel onto the Design Area. Place it to the right of the JTextField,
again watching for visual cues that suggest an appropriate amount of spacing.
Make sure that text base for this component is aligned with that of the JTextField.
The visual cues provided by the IDE should make this easy to determine.
14

Adding a JLabel

For more information about this component, see How to Use Labels.

Step 4: Add a JButton

Next, drag a JButton from the Palette and position it to the left and underneath the
JTextField. Again, the visual cues help guide it into place.
15

Adding a JButton

You may be tempted to manually adjust the width of the JButton and JTextField,
but just leave them as they are for now. You will learn how to correctly adjust
these components later in this lesson. For more information about this component,
see How to Use Buttons.

Step 5: Add a Second JLabel


16

Adding a Second JLabel

Finally, add a second JLabel, repeating the process in step 2. Place this second
label to the right of the JButton, as shown above.

Adjusting the CelsiusConverter GUI

With the GUI components now in place, it is time to make the final adjustments.
There are a few different ways to do this; the order suggested here is just one
possible approach.

Step 1: Set the Component Text

First, double-click the JTextField and JButton to change the default text that was
inserted by the IDE. When you erase the text from the JTextField, it will shrink in
size as shown below. Change the text of the JButton from "JButton1" to "Convert."
Also change the top JLabel text to "Celsius" and the bottom to "Fahrenheit."
17

Setting the Component Text

Step 2: Set the Component Size

Next, shift-click the JTextField and JButton components. This will highlight each
showing that they are selected. Right-click (control-click for mac users) Same Size
-> Same Width. The components will now be the same width, as shown below.
When you perform this step, make sure that JFrame itself is not also selected. If it
is, the Same Size menu will not be active.
18

Setting the JTextField and JButton Sizes

Step 3: Remove Extra Space

Finally, grab the lower right-hand corner of the JFrame and adjust its size to
eliminate any extra whitespace. Note that if you eliminate all of the extra space (as
shown below) the title (which only appears at runtime) may not show completely.
The end-user is free to resize the application as desired, but you may want to leave
some extra space on the right side to make sure that everything fits correctly.
Experiment, and use the screenshot of the finished GUI as a guide.
19

The Completed GUI

The GUI portion of this application is now complete! If the NetBeans IDE has
done its job, you should feel that creating this GUI was a simple, if not trivial, task.
But take a minute to click on the source tab; you might be surprised at the amount
of code that has been generated.
20

To see the code in its entirety, scroll up and down within the IDE as necessary.
You can expand or collapse certain blocks of code (such as method bodies) by
clicking the + or - symbol on the left-hand side of the source editor.

Adding the Application Logic

It is now time to add in the application logic.

Step 1: Change the Default Variable Names

The figure below shows the default variable names as they currently appear within
the Inspector. For each component, the variable name appears first, followed by the
object's type in square brackets. For example, jTextField1 [JTextField] means that
"jTextField1" is the variable name and "JTextField" is its type.

Default Variable Names

The default names are not very relevant in the context of this application, so it
makes sense to change them from their defaults to something that is more
meaningful. Right-click each variable name and choose "Change variable name."
When you are finished, the variable names should appear as follows:
21

New Variable Names

The new variable names are "tempTextField", "celsiusLabel", "convertButton",


and "fahrenheitLabel." Each change that you make in the Inspector will
automatically propagate its way back into the source code. You can rest assured
that compilation will not fail due to typos or mistakes of that nature — mistakes
that are common when editing by hand.

Step 2: Register the Event Listeners

When an end-user interacts with a Swing GUI component (such as clicking the
Convert button), that component will generate a special kind of object — called an
event object — which it will then broadcast to any other objects that have
previously registered themselves as listeners for that event. The NetBeans IDE
makes event listener registration extremely simple:
22

In the Design Area, click on the Convert button to select it. Make sure that only the
Convert button is selected (if the JFrame itself is also selected, this step will not
work.) Right-click the Convert button and choose Events -> Action ->
ActionPerformed. This will generate the required event-handling code, leaving you
with empty method bodies in which to add your own functionality:
23

There are many different event types representing the various kinds of actions that
an end-user can take (clicking the mouse triggers one type of event, typing at the
keyboard triggers another, moving the mouse yet another, and so on.) Our
application is only concerned with the ActionEvent; for more information about
event handling, see Writing Event Listeners.

Step 3: Add the Temperature Conversion Code

The final step is to simply paste the temperature conversion code into the empty
method body. The following code is all that is necessary to convert a temperature
from Celsius to Fahrenheit:

//Parse degrees Celsius as a double and convert to Fahrenheit.


int tempFahr
=(int)((Double.parseDouble(tempTextField.getText()))
* 1.8 + 32);
fahrenheitLabel.setText(tempFahr + " Fahrenheit");

Simply copy this code and paste it into the convertButtonActionPerformed method
as shown below:

With the conversion code in place, the application is now complete.

Step 4: Run the Application

Running the application is simply a matter of choosing Run -> Run Main Project
within the NetBeans IDE. The first time you run this application, you will be
prompted with a dialog asking to set CelsiusConverterGUI as the main class for
24

this project. Click the OK button, and when the program finishes compiling, you
should see the application running in its own window.

Congratulations! You have completed your first Swing application!


25

Using Top-Level Containers

As we mentioned before, Swing provides three generally useful top-level container


classes: JFrame, JDialog, and JApplet. When using these classes, you should keep
these facts in mind:

 To appear onscreen, every GUI component must be part of a


containment hierarchy. A containment hierarchy is a tree of
components that has a top-level container as its root. We'll show you
one in a bit.
 Each GUI component can be contained only once. If a component is
already in a container and you try to add it to another container, the
component will be removed from the first container and then added to
the second.
 Each top-level container has a content pane that, generally speaking,
contains (directly or indirectly) the visible components in that top-
level container's GUI.
 You can optionally add a menu bar to a top-level container. The menu
bar is by convention positioned within the top-level container, but
outside the content pane. Some look and feels, such as the Mac OS
look and feel, give you the option of placing the menu bar in another
place more appropriate for the look and feel, such as at the top of the
screen.

Note: Although JInternalFrame mimics JFrame, internal frames aren't actually


top-level containers.

Here's a picture of a frame created by an application. The frame contains a green


menu bar (with no menus) and, in the frame's content pane, a large blank, yellow
label.
26

You can find the entire source for this example in TopLevelDemo.java. Although
the example uses a JFrame in a standalone application, the same concepts apply to
JApplets and JDialogs.

Here's the containment hierarchy for this example's GUI:

As the ellipses imply, we left some details out of this diagram. We reveal the
missing details a bit later. Here are the topics this section discusses:

 Top-Level Containers and Containment Hierarchies


 Adding Components to the Content Pane
 Adding a Menu Bar
 The Root Pane (a.k.a. The Missing Details)

Top-Level Containers and Containment Hierarchies

Each program that uses Swing components has at least one top-level container.
This top-level container is the root of a containment hierarchy — the hierarchy that
contains all of the Swing components that appear inside the top-level container.
27

As a rule, a standalone application with a Swing-based GUI has at least one


containment hierarchy with a JFrame as its root. For example, if an application has
one main window and two dialogs, then the application has three containment
hierarchies, and thus three top-level containers. One containment hierarchy has a
JFrame as its root, and each of the other two has a JDialog object as its root.

A Swing-based applet has at least one containment hierarchy, exactly one of which
is rooted by a JApplet object. For example, an applet that brings up a dialog has
two containment hierarchies. The components in the browser window are in a
containment hierarchy rooted by a JApplet object. The dialog has a containment
hierarchy rooted by a JDialog object.

Adding Components to the Content Pane

Here's the code that the preceding example uses to get a frame's content pane and
add the yellow label to it:
frame.getContentPane().add(yellowLabel, BorderLayout.CENTER);
As the code shows, you find the content pane of a top-level container by calling the
getContentPane method. The default content pane is a simple intermediate
container that inherits from JComponent, and that uses a BorderLayout as its
layout manager.

It's easy to customize the content pane — setting the layout manager or adding a
border, for example. However, there is one tiny gotcha. The getContentPane
method returns a Container object, not a JComponent object. This means that if
you want to take advantage of the content pane's JComponent features, you need to
either typecast the return value or create your own component to be the content
pane. Our examples generally take the second approach, since it's a little cleaner.
Another approach we sometimes take is to simply add a customized component to
the content pane, covering the content pane completely.

Note that the default layout manager for JPanel is FlowLayout; you'll probably
want to change it.

To make a component the content pane, use the top-level container's


setContentPane method. For example:

//Create a panel and add components to it.


JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(someBorder);
contentPane.add(someComponent, BorderLayout.CENTER);
28

contentPane.add(anotherComponent, BorderLayout.PAGE_END);

topLevelContainer.setContentPane(contentPane);

Note: As a convenience, the add method and its variants, remove and setLayout
have been overridden to forward to the contentPane as necessary. This means you
can write
frame.add(child);
and the child will be added to the contentPane.

Note that only these three methods do this. This means that getLayout() will not
return the layout set with setLayout().

Adding a Menu Bar

In theory, all top-level containers can hold a menu bar. In practice, however, menu
bars usually appear only in frames and applets. To add a menu bar to a top-level
container, create a JMenuBar object, populate it with menus, and then call
setJMenuBar. The TopLevelDemo adds a menu bar to its frame with this code:
frame.setJMenuBar(greenMenuBar);
For more information about implementing menus and menu bars, see How to Use
Menus.

The Root Pane

Each top-level container relies on a reclusive intermediate container called the root
pane. The root pane manages the content pane and the menu bar, along with a
couple of other containers. You generally don't need to know about root panes to
use Swing components. However, if you ever need to intercept mouse clicks or
paint over multiple components, you should get acquainted with root panes.

Here's a list of the components that a root pane provides to a frame (and to every
other top-level container):
29

We've already told you about the content pane and the optional menu bar. The two
other components that a root pane adds are a layered pane and a glass pane. The
layered pane contains the menu bar and content pane, and enables Z-ordering of
other components. The glass pane is often used to intercept input events occuring
over the top-level container, and can also be used to paint over multiple
components.

For more details, see How to Use Root Panes.

Using Text Components

This section provides background information you might need when using Swing
text components. If you intend to use an unstyled text component — a text field,
password field, formatted text field, or text area — go to its how-to page and return
here only if necessary. If you intend to use a styled text component, see How to
Use Editor Panes and Text Panes, and read this section as well. If you do not know
which component you need, read on.

Swing text components display text and optionally allow the user to edit the text.
Programs need text components for tasks ranging from the straightforward (enter a
word and press Enter) to the complex (display and edit styled text with embedded
images in an Asian language).

Swing provides six text components, along with supporting classes and interfaces
that meet even the most complex text requirements. In spite of their different uses
and capabilities, all Swing text components inherit from the same superclass,
JTextComponent, which provides a highly-configurable and powerful foundation
for text manipulation.

The following figure shows the JTextComponent hierarchy.


30

The following picture shows an application called TextSamplerDemo that uses


each Swing text component.

Try this:

1. Click the Launch button to run TextSamplerDemo using Java™


Web Start (download JDK 6). Alternatively, to compile and run
the example yourself, consult the example index.
31

2. Type some text in the text field and press Enter. Do the same in
the password field. The label beneath the fields is updated when
you press Enter.
3. Try entering valid and invalid dates into the formatted text
field. Note that when you press Enter the label beneath the
fields is updated only if the date is valid.
4. Select and edit text in the text area and the text pane. Use
keyboard bindings, Ctrl-X, Ctrl-C, and Ctrl-V, to cut, copy, and
paste text, respectively.
5. Try to edit the text in the editor pane, which has been made
uneditable with a call to setEditable.
6. Look in the text pane to find an example of an embedded
component and an embedded icon.

The TextSamplerDemo example uses the text components in very basic ways. The
following table tells you more about what you can do with each kind of text
component.

Group Description Swing Classes


Also known simply as text fields, text
controls can display only one line of
JTextField and its
editable text. Like buttons, they generate
Text subclasses
action events. Use them to get a small
Controls JPasswordField and
amount of textual information from the
JFormattedTextField
user and perform an action after the text
entry is complete.
JTextArea can display multiple lines of
editable text. Although a text area can
Plain display text in any font, all of the text is in
Text the same font. Use a text area to allow the JTextArea
Areas user to enter unformatted text of any
length or to display unformatted help
information.
32

A styled text component can display


editable text using more than one font.
Some styled text components allow
embedded images and even embedded
components. Styled text components are
powerful and multi-faceted components
suitable for high-end needs, and offer
Styled more avenues for customization than the JEditorPane
Text other text components. and its subclass
Areas JTextPane
Because they are so powerful and flexible,
styled text components typically require
more initial programming to set up and
use. One exception is that editor panes can
be easily loaded with formatted text from
a URL, which makes them useful for
displaying uneditable help information.

This Tutorial provides information about the foundation laid by the


JTextComponent class and tells you how to accomplish some common text-related
tasks. Because the JTextComponent class and its subclasses have too many
features to be completely described in this Tutorial, please visit the Swing and
AWT forum at java.net for help and information.

How to Use Buttons, Check Boxes, and Radio Buttons

To create a button, you can instantiate one of the many classes that descend from
the AbstractButton class. The following table shows the Swing-defined
AbstractButton subclasses that you might want to use:

Class Summary Where Described


JButton A common button. How to Use the
Common Button API
and How to Use
JButton Features
JCheckBox A check box button. How to Use Check
Boxes
33

JRadioButton One of a group of radio buttons. How to Use Radio


Buttons
JMenuItem An item in a menu. How to Use Menus
JCheckBoxMenuItem A menu item that has a check box. How to Use Menus
and How to Use
Check Boxes
JRadioButtonMenuItem A menu item that has a radio How to Use Menus
button. and How to Use
Radio Buttons
JToggleButton Implements toggle functionality Used in some
inherited by JCheckBox and examples
JRadioButton. Can be instantiated
or subclassed to create two-state
buttons.

Note: If you want to collect a group of buttons into a row or column, then you
should check out tool bars.

First, this section explains the basic button API that AbstractButton defines — and
thus all Swing buttons have in common. Next, it describes the small amount of API
that JButton adds to AbstractButton. After that, this section shows you how to use
specialized API to implement check boxes and radio buttons.

How to Use the Common Button API

Here is a picture of an application that displays three buttons:

Try this:

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

2. Click the left button.


It disables the middle button (and itself, since it is no longer
useful) and enables the right button.
3. Click the right button.
It enables the middle button and the left button, and disables
itself.

As the ButtonDemo example shows, a Swing button can display both text and an
image. In ButtonDemo, each button has its text in a different place, relative to its
image. The underlined letter in each button's text shows the mnemonic — the
keyboard alternative — for each button. In most look and feels, the user can click a
button by pressing the Alt key and the mnemonic. For example, Alt-M would click
the Middle button in ButtonDemo.

When a button is disabled, the look and feel automatically generates the button's
disabled appearance. However, you could provide an image to be substituted for
the normal image. For example, you could provide gray versions of the images
used in the left and right buttons.

How you implement event handling depends on the type of button you use and
how you use it. Generally, you implement an action listener, which is notified
every time the user clicks the button. For check boxes you usually use an item
listener, which is notified when the check box is selected or deselected.

Below is the code from ButtonDemo.java that creates the buttons in the previous
example and reacts to button clicks. The bold code is the code that would remain if
the buttons had no images.
//In initialization code:
ImageIcon leftButtonIcon =
createImageIcon("images/right.gif");
ImageIcon middleButtonIcon =
createImageIcon("images/middle.gif");
ImageIcon rightButtonIcon =
createImageIcon("images/left.gif");

b1 = new JButton("Disable middle button", leftButtonIcon);


b1.setVerticalTextPosition(AbstractButton.CENTER);
b1.setHorizontalTextPosition(AbstractButton.LEADING); //aka
LEFT, for left-to-right locales
b1.setMnemonic(KeyEvent.VK_D);
35

b1.setActionCommand("disable");

b2 = new JButton("Middle button", middleButtonIcon);


b2.setVerticalTextPosition(AbstractButton.BOTTOM);
b2.setHorizontalTextPosition(AbstractButton.CENTER);
b2.setMnemonic(KeyEvent.VK_M);

b3 = new JButton("Enable middle button", rightButtonIcon);


//Use the default text position of CENTER, TRAILING (RIGHT).
b3.setMnemonic(KeyEvent.VK_E);
b3.setActionCommand("enable");
b3.setEnabled(false);

//Listen for actions on buttons 1 and 3.


b1.addActionListener(this);
b3.addActionListener(this);

b1.setToolTipText("Click this button to disable "


+ "the middle button.");
b2.setToolTipText("This middle button does nothing "
+ "when you click it.");
b3.setToolTipText("Click this button to enable the "
+ "middle button.");
...
}

public void actionPerformed(ActionEvent e) {


if ("disable".equals(e.getActionCommand())) {
b2.setEnabled(false);
b1.setEnabled(false);
b3.setEnabled(true);
} else {
b2.setEnabled(true);
b1.setEnabled(true);
b3.setEnabled(false);
}
}

protected static ImageIcon createImageIcon(String path) {


java.net.URL imgURL = ButtonDemo.class.getResource(path);
...//error handling omitted for clarity...
return new ImageIcon(imgURL);
}

How to Use JButton Features


36

Ordinary buttons — JButton objects — have just a bit more functionality than the
AbstractButton class provides: You can make a JButton be the default button.

At most one button in a top-level container can be the default button. The default
button typically has a highlighted appearance and acts clicked whenever the top-
level container has the keyboard focus and the user presses the Return or Enter
key. Here is a picture of a dialog, implemented in the ListDialog example, in which
the Set button is the default button:

You set the default button by invoking the setDefaultButton method on a top-level
container's root pane. Here is the code that sets up the default button for the
ListDialog example:

//In the constructor for a JDialog subclass:


getRootPane().setDefaultButton(setButton);
The exact implementation of the default button feature depends on the look and
feel. For example, in the Windows look and feel, the default button changes to
whichever button has the focus, so that pressing Enter clicks the focused button.
When no button has the focus, the button you originally specified as the default
button becomes the default button again.

How to Use Check Boxes

The JCheckBox class provides support for check box buttons. You can also put
check boxes in menus, using the JCheckBoxMenuItem class. Because JCheckBox
and JCheckBoxMenuItem inherit from AbstractButton, Swing check boxes have
all the usual button characteristics, as discussed earlier in this section. For example,
you can specify images to be used in check boxes.
37

Check boxes are similar to radio buttons but their selection model is different, by
convention. Any number of check boxes in a group — none, some, or all — can be
selected. A group of radio buttons, on the other hand, can have only one button
selected.

Here is a picture of an application that uses four check boxes to customize a


cartoon:

Try this:

1. Click the Launch button to run the CheckBox Demo using


Java™ Web Start (download JDK 6). Alternatively, to compile
and run the example yourself, consult the example index.
2. Click the Chin button or press Alt-c.
The Chin check box becomes unselected, and the chin
disappears from the picture. The other check boxes remain
selected. This application has one item listener that listens to all
the check boxes. Each time the item listener receives an event,
the application loads a new picture that reflects the current state
of the check boxes.

A check box generates one item event and one action event per click. Usually, you
listen only for item events, since they let you determine whether the click selected
or deselected the check box. Below is the code from CheckBoxDemo.java that
creates the check boxes in the previous example and reacts to clicks.

//In initialization code:


chinButton = new JCheckBox("Chin");
chinButton.setMnemonic(KeyEvent.VK_C);
chinButton.setSelected(true);
38

glassesButton = new JCheckBox("Glasses");


glassesButton.setMnemonic(KeyEvent.VK_G);
glassesButton.setSelected(true);

hairButton = new JCheckBox("Hair");


hairButton.setMnemonic(KeyEvent.VK_H);
hairButton.setSelected(true);

teethButton = new JCheckBox("Teeth");


teethButton.setMnemonic(KeyEvent.VK_T);
teethButton.setSelected(true);

//Register a listener for the check boxes.


chinButton.addItemListener(this);
glassesButton.addItemListener(this);
hairButton.addItemListener(this);
teethButton.addItemListener(this);
...
public void itemStateChanged(ItemEvent e) {
...
Object source = e.getItemSelectable();

if (source == chinButton) {
//...make a note of it...
} else if (source == glassesButton) {
//...make a note of it...
} else if (source == hairButton) {
//...make a note of it...
} else if (source == teethButton) {
//...make a note of it...
}

if (e.getStateChange() == ItemEvent.DESELECTED)
//...make a note of it...
...
updatePicture();
}

How to Use Radio Buttons


39

Radio buttons are groups of buttons in which, by convention, only one button at a
time can be selected. The Swing release supports radio buttons with the
JRadioButton and ButtonGroup classes. To put a radio button in a menu, use the
JRadioButtonMenuItem class. Other ways of displaying one-of-many choices are
combo boxes and lists. Radio buttons look similar to check boxes, but, by
convention, check boxes place no limits on how many items can be selected at a
time.

Because JRadioButton inherits from AbstractButton, Swing radio buttons have all
the usual button characteristics, as discussed earlier in this section. For example,
you can specify the image displayed in a radio button.

Here is a picture of an application that uses five radio buttons to let you choose
which kind of pet is displayed:

Try this:

1. Click the Launch button to run the RadioButton Demo using


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

2. Click the Dog button or press Alt-d.


The Dog button becomes selected, which makes the Bird
button become unselected. The picture switches from a bird to a
dog. This application has one action listener that listens to all
the radio buttons. Each time the action listener receives an
event, the application displays the picture for the radio button
that was just clicked.
40

Each time the user clicks a radio button (even if it was already selected), the button
fires an action event. One or two item events also occur — one from the button that
was just selected, and another from the button that lost the selection (if any).
Usually, you handle radio button clicks using an action listener.

Below is the code from RadioButtonDemo.java that creates the radio buttons in the
previous example and reacts to clicks.

//In initialization code:


//Create the radio buttons.
JRadioButton birdButton = new JRadioButton(birdString);
birdButton.setMnemonic(KeyEvent.VK_B);
birdButton.setActionCommand(birdString);
birdButton.setSelected(true);

JRadioButton catButton = new JRadioButton(catString);


catButton.setMnemonic(KeyEvent.VK_C);
catButton.setActionCommand(catString);

JRadioButton dogButton = new JRadioButton(dogString);


dogButton.setMnemonic(KeyEvent.VK_D);
dogButton.setActionCommand(dogString);

JRadioButton rabbitButton = new JRadioButton(rabbitString);


rabbitButton.setMnemonic(KeyEvent.VK_R);
rabbitButton.setActionCommand(rabbitString);

JRadioButton pigButton = new JRadioButton(pigString);


pigButton.setMnemonic(KeyEvent.VK_P);
pigButton.setActionCommand(pigString);

//Group the radio buttons.


ButtonGroup group = new ButtonGroup();
group.add(birdButton);
group.add(catButton);
group.add(dogButton);
group.add(rabbitButton);
group.add(pigButton);
41

//Register a listener for the radio buttons.


birdButton.addActionListener(this);
catButton.addActionListener(this);
dogButton.addActionListener(this);
rabbitButton.addActionListener(this);
pigButton.addActionListener(this);
...
public void actionPerformed(ActionEvent e) {
picture.setIcon(new ImageIcon("images/"
+ e.getActionCommand()
+ ".gif"));
}

For each group of radio buttons, you need to create a ButtonGroup instance and
add each radio button to it. The ButtonGroup takes care of unselecting the
previously selected button when the user selects another button in the group.

You should generally initialize a group of radio buttons so that one is selected.
However, the API doesn't enforce this rule — a group of radio buttons can have no
initial selection. Once the user has made a selection, exactly one button is selected
from then on.

How to Use Combo Boxes

A JComboBox, which lets the user choose one of several choices, can have two
very different forms. The default form is the uneditable combo box, which features
a button and a drop-down list of values. The second form, called the editable
combo box, features a text field with a small button abutting it. The user can type a
value in the text field or click the button to display a drop-down list. Here's what
the two forms of combo boxes look like in the Java look and feel:
42

Uneditable combo box, before (top) Editable combo box, before and after
and after the button is clicked the arrow button is clicked

Combo boxes require little screen space, and their editable (text field) form is
useful for letting the user quickly choose a value without limiting the user to the
displayed values. Other components that can display one-of-many choices are
groups of radio buttons and lists. Groups of radio buttons are generally the easiest
for users to understand, but combo boxes can be more appropriate when space is
limited or more than a few choices are available. Lists are not terribly attractive,
but they're more appropriate than combo boxes when the number of items is large
(say, over 20) or when selecting multiple items might be valid.

Because editable and uneditable combo boxes are so different, this section treats
them separately. This section covers these topics:

 Using an Uneditable Combo Box


 Handling Events on a Combo Box
 Using an Editable Combo Box
 Providing a Custom Renderer
 The Combo Box API
 Examples that Use Combo Boxes

Using an Uneditable Combo Box

The application shown here uses an uneditable combo box for choosing a pet
picture:
43

Try this:

1. Click the Launch button to run the ComboBox Demo using


Java™ Web Start (download JDK 6). Alternatively, to compile
and run the example yourself, consult the example index.
2. Choose an animal name from the combo box to view its picture.
3. Compare the operation and GUI of this program to one that
uses radio buttons: run RadioButtonDemo (it requires release
6). You might want to compare the source code as well:
ComboBoxDemo.java vs. RadioButtonDemo.java.

The following code, taken from ComboBoxDemo.java, creates an uneditable


combo box and sets it up:
String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };

//Create the combo box, select item at index 4.


//Indices start at 0, so 4 specifies the pig.
JComboBox petList = new JComboBox(petStrings);
petList.setSelectedIndex(4);
petList.addActionListener(this);
This combo box contains an array of strings, but you could just as easily use icons
instead. To put anything else into a combo box or to customize how the items in a
combo box look, you need to write a custom renderer. An editable combo box
would also need a custom editor. Refer to Providing a Custom Renderer for
information and an example.
44

The preceding code registers an action listener on the combo box. To see the action
listener implementation and learn about other types of listeners supported by
combo box, refer to Handling Events on a Combo Box.

No matter which constructor you use, a combo box uses a combo box model to
contain and manage the items in its menu. When you initialize a combo box with
an array or a vector, the combo box creates a default model object for you. As with
other Swing components, you can customize a combo box in part by implementing
a custom model — an object that implements the ComboBoxModel interface.

Note: Be careful when implementing a custom model for a combo box. The
JComboBox methods that change the items in the combo box's menu, such as
insertItemAt, work only if the data model implements the
MutableComboBoxModel interface (a subinterface of ComboBoxModel). Refer to
the API tables to see which methods are affected.

Something else to watch out for — even for uneditable combo boxes — is ensuring
that your custom model fires list data events when the combo box's data or state
changes. Even immutable combo box models, whose data never changes, must fire
a list data event (a CONTENTS_CHANGED event) when the selection changes.
One way to get the list data event firing code for free is to make your combo box
model a subclass of AbstractListModel.

Handling Events on a Combo Box

Here's the code from ComboBoxDemo.java that registers and implements an action
listener on the combo box:
public class ComboBoxDemo ... implements ActionListener {
...
petList.addActionListener(this) {
...
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
String petName = (String)cb.getSelectedItem();
updateLabel(petName);
}
...
}
45

This action listener gets the newly selected item from the combo box, uses it to
compute the name of an image file, and updates a label to display the image. The
combo box fires an action event when the user selects an item from the combo
box's menu. See How to Write an Action Listener, for general information about
implementing action listeners.

Combo boxes also generate item events, which are fired when any of the items'
selection state changes. Only one item at a time can be selected in a combo box, so
when the user makes a new selection the previously selected item becomes
unselected. Thus two item events are fired each time the user selects a different
item from the menu. If the user chooses the same item, no item events are fired.
Use addItemListener to register an item listener on a combo box. How to Write an
Item Listener gives general information about implementing item listeners.

Although JComboBox inherits methods to register listeners for low-level events —


focus, key, and mouse events, for example — we recommend that you don't listen
for low-level events on a combo box. Here's why: A combo box is a compound
component — it is comprised of two or more other components. The combo box
itself fires high-level events such as action events. Its subcomponents fire low-
level events such as mouse, key, and focus events. The low-level events and the
subcomponent that fires them are look-and-feel-dependent. To avoid writing look-
and-feel-dependent code, you should listen only for high-level events on a
compound component such as a combo box. For information about events,
including a discussion about high- and low-level events, refer to Writing Event
Listeners.

Using an Editable Combo Box

Here's a picture of a demo application that uses an editable combo box to enter a
pattern with which to format dates.

Try this:
46

1. Click the Launch button to run the ComboBox2 Demo using


Java™ Web Start (download JDK 6). Alternatively, to compile
and run the example yourself, consult the example index. Enter
a new pattern by choosing one from the combo box's menu. The
program reformats the current date and time.
2. Enter a new pattern by typing one in and pressing Enter. Again
the program reformats the current date and time.

The following code, taken from ComboBoxDemo2.java, creates and sets up the
combo box:

String[] patternExamples = {
"dd MMMMM yyyy",
"dd.MM.yy",
"MM/dd/yy",
"yyyy.MM.dd G 'at' hh:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"H:mm:ss:SSS",
"K:mm a,z",
"yyyy.MMMMM.dd GGG hh:mm aaa"
};
...
JComboBox patternList = new JComboBox(patternExamples);
patternList.setEditable(true);
patternList.addActionListener(this);
This code is very similar to the previous example, but warrants a few words of
explanation. The bold line of code explicitly turns on editing to allow the user to
type values in. This is necessary because, by default, a combo box is not editable.
This particular example allows editing on the combo box because its menu does
not provide all possible date formatting patterns, just shortcuts to frequently used
patterns.

An editable combo box fires an action event when the user chooses an item from
the menu and when the user types Enter. Note that the menu remains unchanged
when the user enters a value into the combo box. If you want, you can easily write
an action listener that adds a new item to the combo box's menu each time the user
types in a unique value.
47

See Internationalization to learn more about formatting dates and other types of
data.

Providing a Custom Renderer

A combo box uses a renderer to display each item in its menu. If the combo box is
uneditable, it also uses the renderer to display the currently selected item. An
editable combo box, on the other hand, uses an editor to display the selected item.
A renderer for a combo box must implement the ListCellRenderer interface. A
combo box's editor must implement ComboBoxEditor. This section shows how to
provide a custom renderer for an uneditable combo box.

The default renderer knows how to render strings and icons. If you put other
objects in a combo box, the default renderer calls the toString method to provide a
string to display. You can customize the way a combo box renders itself and its
items by implementing your own ListCellRenderer.

Here's a picture of an application that uses a combo box with a custom renderer:

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

The full source code for this example is in CustomComboBoxDemo.java. To get


the image files it requires, consult the example index.
48

The following statements from the example create an instance of


ComboBoxRenderer (a custom class) and set up the instance as the combo box's
renderer:

JComboBox petList = new JComboBox(intArray);


...
ComboBoxRenderer renderer = new ComboBoxRenderer();
renderer.setPreferredSize(new Dimension(200, 130));
petList.setRenderer(renderer);
petList.setMaximumRowCount(3);

The last line sets the combo box's maximum row count, which determines the
number of items visible when the menu is displayed. If the number of items in the
combo box is larger than its maximum row count, the menu has a scroll bar. The
icons are pretty big for a menu, so our code limits the number of rows to 3. Here's
the implementation of ComboBoxRenderer, a renderer that puts an icon and text
side-by-side:

class ComboBoxRenderer extends JLabel


implements ListCellRenderer {
...
public ComboBoxRenderer() {
setOpaque(true);
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
}

/*
* This method finds the image and text corresponding
* to the selected value and returns the label, set up
* to display the text and image.
*/
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
//Get the selected index. (The index param isn't
//always valid, so just use the value.)
49

int selectedIndex = ((Integer)value).intValue();

if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}

//Set the icon and text. If icon was null, say so.
ImageIcon icon = images[selectedIndex];
String pet = petStrings[selectedIndex];
setIcon(icon);
if (icon != null) {
setText(pet);
setFont(list.getFont());
} else {
setUhOhText(pet + " (no image available)",
list.getFont());
}

return this;
}
...
}
As a ListCellRenderer, ComboBoxRenderer implements a method called
getListCellRendererComponent, which returns a component whose
paintComponent method is used to display the combo box and each of its items.
The easiest way to display an image and an icon is to use a label. So
ComboBoxRenderer is a subclass of label and returns itself. The implementation of
getListCellRendererComponent configures the renderer to display the currently
selected icon and its description.

These arguments are passed to getListCellRendererComponent:

 JList list — a list object used behind the scenes to display the items.
The example uses this object's colors to set up foreground and
background colors.
 Object value — the object to render. An Integer in this example.
50

 int index — the index of the object to render.


 boolean isSelected — indicates whether the object to render is
selected. Used by the example to determine which colors to use.
 boolean cellHasFocus — indicates whether the object to render has
the focus.

Note that combo boxes and lists use the same type of renderer —
ListCellRenderer. You can save yourself some time by sharing renderers between
combo boxes and lists, if it makes sense for your program.

How to Make Frames (Main Windows)

A Frame is a top-level window with a title and a border. The size of the frame
includes any area designated for the border. The dimensions of the border area may
be obtained using the getInsets method. Since the border area is included in the
overall size of the frame, the border effectively obscures a portion of the frame,
constraining the area available for rendering and/or displaying subcomponents to
the rectangle which has an upper-left corner location of (insets.left, insets.top), and
has a size of width - (insets.left + insets.right) by height - (insets.top +
insets.bottom).

A frame, implemented as an instance of the JFrame class, is a window that has


decorations such as a border, a title, and supports button components that close or
iconify the window. Applications with a GUI usually include at least one frame.
Applets sometimes use frames, as well.

To make a window that is dependent on another window — disappearing when the


other window is iconified, for example — use a dialog instead of frame.. To make
a window that appears within another window, use an internal frame.

Creating and Showing Frames

Here is a picture of the extremely plain window created by the FrameDemo


demonstration application. You can find the source code in FrameDemo.java. You
can run FrameDemo ( download JDK 6).
51

The following FrameDemo code shows how to create and set up a frame.

//1. Create the frame.


JFrame frame = new JFrame("FrameDemo");

//2. Optional: What happens when the frame closes?


frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//3. Create components and put them in the frame.


//...create emptyLabel...
frame.getContentPane().add(emptyLabel, BorderLayout.CENTER);

//4. Size the frame.


frame.pack();

//5. Show it.


frame.setVisible(true);
Here are some details about the code:

1. The first line of code creates a frame using a constructor that lets you
set the frame title. The other frequently used JFrame constructor is the
no-argument constructor.
2. Next the code specifies what happens when your user closes the
frame. The EXIT_ON_CLOSE operation exits the program when
your user closes the frame. This behavior is appropriate for this
program because the program has only one frame, and closing the
frame makes the program useless.

See Responding to Window-Closing Events for more information.

3. The next bit of code adds a blank label to the frame content pane. If
you're not already familiar with content panes and how to add
components to them, please read Adding Components to the Content
Pane.
52

For frames that have menus, you'd typically add the menu bar to the
frame here using the setJMenuBar method. See How to Use Menus
for details.

4. The pack method sizes the frame so that all its contents are at or above
their preferred sizes. An alternative to pack is to establish a frame size
explicitly by calling setSize or setBounds (which also sets the frame
location). In general, using pack is preferable to calling setSize, since
pack leaves the frame layout manager in charge of the frame size, and
layout managers are good at adjusting to platform dependencies and
other factors that affect component size.

This example does not set the frame location, but it is easy to do so
using either the setLocationRelativeTo or setLocation method. For
example, the following code centers a frame onscreen:

frame.setLocationRelativeTo(null);

5. Calling setVisible(true) makes the frame appear onscreen. Sometimes


you might see the show method used instead. The two usages are
equivalent, but we use setVisible(true) for consistency's sake.

Specifying Window Decorations

By default, window decorations are supplied by the native window system.


However, you can request that the look-and-feel provide the decorations for a
frame. You can also specify that the frame have no window decorations at all, a
feature that can be used on its own, or to provide your own decorations, or with
full-screen exclusive mode.

Besides specifying who provides the window decorations, you can also specify
which icon is used to represent the window. Exactly how this icon is used depends
on the window system or look and feel that provides the window decorations. If the
window system supports minimization, then the icon is used to represent the
minimized window. Most window systems or look and feels also display the icon
in the window decorations. A typical icon size is 16x16 pixels, but some window
systems use other sizes.

The following snapshots show three frames that are identical except for their
window decorations. As you can tell by the appearance of the button in each frame,
all three use the Java look and feel. The first uses decorations provided by the
53

window system, which happen to be Microsoft Windows, but could as easily be


any other system running the Java platform.The second and third use window
decorations provided by the Java look and feel. The third frame uses Java look and
feel window decorations, but has a custom icon.

Window decorations Window decorations Custom icon; window


provided by the look and provided by the window decorations provided by the
feel system look and feel

Here is an example of creating a frame with a custom icon and with window
decorations provided by the look and feel:

//Ask for window decorations provided by the look and feel.


JFrame.setDefaultLookAndFeelDecorated(true);

//Create the frame.


JFrame frame = new JFrame("A window");

//Set the frame icon to an image loaded from a file.


frame.setIconImage(new ImageIcon(imgURL).getImage());

As the preceding code snippet implies, you must invoke the


setDefaultLookAndFeelDecorated method before creating the frame whose
decorations you wish to affect. The value you set with
setDefaultLookAndFeelDecorated is used for all subsequently created JFrames.
You can switch back to using window system decorations by invoking
JFrame.setDefaultLookAndFeelDecorated(false). Some look and feels might not
support window decorations; in this case, the window system decorations are used.

The full source code for the application that creates the frames pictured above is in
FrameDemo2.java. Besides showing how to choose window decorations,
FrameDemo2 also shows how to disable all window decorations and gives an
example of positioning windows. It includes two methods that create the Image
objects used as icons — one is loaded from a file, and the other is painted from
scratch.
54

Try this::

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

2. Bring up two windows, both with look-and-feel-provided


decorations, but with different icons.
The Java look and feel displays the icons in its window
decorations. Depending on your window system, the icon may
be used elsewhere to represent the window, especially when the
window is minimized.
3. Bring up one or more windows with window system
decorations.
See if your window system treats these icons differently.
4. Bring up one or more windows with no window decorations.
Play with the various types of windows to see how the window
decorations, window system, and frame icons interact.

Responding to Window-Closing Events

By default, when the user closes a frame onscreen, the frame is hidden. Although
invisible, the frame still exists and the program can make it visible again. If you
want different behavior, then you need to either register a window listener that
handles window-closing events, or you need to specify default close behavior
using the setDefaultCloseOperation method. You can even do both.

The argument to setDefaultCloseOperation must be one of the following values,


the first three of which are defined in the WindowConstants interface
(implemented by JFrame, JInternalPane, and JDialog):

DO_NOTHING_ON_CLOSE
Do not do anything when the user requests that the window close.
Instead, the program should probably use a window listener that
performs some other action in its windowClosing method.
HIDE_ON_CLOSE (the default for JDialog and JFrame)
55

Hide the window when the user closes it. This removes the window
from the screen but leaves it displayable.
DISPOSE_ON_CLOSE (the default for JInternalFrame)
Hide and dispose of the window when the user closes it. This removes
the window from the screen and frees up any resources used by it.
EXIT_ON_CLOSE (defined in the JFrame class)
Exit the application, using System.exit(0). This is recommended for
applications only. If used within an applet, a SecurityException may
be thrown.

Note: DISPOSE_ON_CLOSE can have results similar to EXIT_ON_CLOSE if


only one window is onscreen. More precisely, when the last displayable window
within the Java virtual machine (VM) is disposed of, the VM may terminate. See
AWT Threading Issues for details.

The default close operation is executed after any window listeners handle the
window-closing event. So, for example, assume that you specify that the default
close operation is to dispose of a frame. You also implement a window listener that
tests whether the frame is the last one visible and, if so, saves some data and exits
the application. Under these conditions, when the user closes a frame, the window
listener will be called first. If it does not exit the application, then the default close
operation — disposing of the frame — will then be performed.

For more information about handling window-closing events, see How to Write
Window Listeners. Besides handling window-closing events, window listeners can
also react to other window state changes, such as iconification and activation.

The Frame API

The following tables list the commonly used JFrame constructors and methods.
Other methods you might want to call are defined by the java.awt.Frame,
java.awt.Window, and java.awt.Component classes, from which JFrame descends.

Because each JFrame object has a root pane, frames have support for interposing
input and painting behavior in front of the frame children, placing children on
different "layers", and for Swing menu bars. These topics are introduced in Using
Top-Level Containers and explained in detail in How to Use Root Panes.

The API for using frames falls into these categories:


56

 Creating and Setting Up a Frame


 Setting the Window Size and Location
 Methods Related to the Root Pane

Creating and Setting Up a Frame


Method or Constructor Purpose
JFrame() Create a frame that is initially
JFrame(String) invisible. The String argument
provides a title for the frame. To make
the frame visible, invoke
setVisible(true) on it.
void setDefaultCloseOperation(int) Set or get the operation that occurs
int getDefaultCloseOperation() when the user pushes the close button
on this frame. Possible choices are:

 DO_NOTHING_ON_CLOSE
 HIDE_ON_CLOSE
 DISPOSE_ON_CLOSE
 EXIT_ON_CLOSE

The first three constants are defined in


the WindowConstants interface, which
JFrame implements. The
EXIT_ON_CLOSE constant is defined
in the JFrame class.
void setIconImage(Image) Set or get the icon that represents the
Image getIconImage() frame. Note that the argument is a
(in Frame) java.awt.Image object, not a
javax.swing.ImageIcon (or any other
javax.swing.Icon implementation).
void setTitle(String) Set or get the frame title.
String getTitle()
(in Frame)
void setUndecorated(boolean) Set or get whether this frame should be
boolean isUndecorated() decorated. Works only if the frame is
(in Frame) not yet displayable (has not been
packed or shown). Typically used with
full-screen exclusive mode or to
57

enable custom window decorations.


static void Determine whether subsequently
setDefaultLookAndFeelDecorated(boolean created JFrames should have their
) Window decorations (such as borders,
static boolean and widgets for closing the window)
isDefaultLookAndFeelDecorated() provided by the current look-and-feel.
Note that this is only a hint, as some
look and feels may not support this
feature.
Setting the Window Size and Location
Method Purpose
void pack() Size the window so that all its contents are at
(in Window) or above their preferred sizes.
void setSize(int, int) Set or get the total size of the window. The
void setSize(Dimension) integer arguments to setSize specify the
Dimension getSize() width and height, respectively.
(in Component)
void setBounds(int, int, int, int) Set or get the size and position of the
void setBounds(Rectangle) window. For the integer version of
Rectangle getBounds() setBounds, the window upper left corner is at
(in Component) the x, y location specified by the first two
arguments, and has the width and height
specified by the last two arguments.
void setLocation(int, int) Set or get the location of the upper left corner
Point getLocation() of the window. The parameters are the x and
(in Component) y values, respectively.
void Position the window so that it is centered
setLocationRelativeTo(Component) over the specified component. If the
(in Window) argument is null, the window is centered
onscreen. To properly center the window,
you should invoke this method after the
window size has been set.
Methods Related to the Root Pane
Method Purpose
void setContentPane(Container) Set or get the frame content pane. The content
Container getContentPane() pane contains the visible GUI components within
58

the frame.
JRootPane createRootPane() Create, set, or get the frame root pane. The root
void setRootPane(JRootPane) pane manages the interior of the frame including
JRootPane getRootPane() the content pane, the glass pane, and so on.
void setJMenuBar(JMenuBar) Set or get the frame menu bar to manage a set of
JMenuBar getJMenuBar() menus for the frame.
void setGlassPane(Component) Set or get the frame glass pane. You can use the
Component getGlassPane() glass pane to intercept mouse events or paint on
top of your program GUI.
void Set or get the frame layered pane. You can use
setLayeredPane(JLayeredPane) the frame layered pane to put components on top
JLayeredPane getLayeredPane() of or behind other components.

How to Use Trees

With the JTree class, you can display hierarchical data. A JTree object does not actually
contain your data; it simply provides a view of the data. Like any non-trivial Swing
component, the tree gets data by querying its data model. Here is a picture of a tree:

As the preceding figure shows, JTree displays its data vertically. Each row displayed by the
tree contains exactly one item of data, which is called a node. Every tree has a root node
from which all nodes descend. By default, the tree displays the root node, but you can decree
otherwise. A node can either have children or not. We refer to nodes that can have children
— whether or not they currently have children — as branch nodes. Nodes that can not have
children are leaf nodes.

Branch nodes can have any number of children. Typically, the user can expand and collapse
branch nodes — making their children visible or invisible — by clicking them. By default,
all branch nodes except the root node start out collapsed. A program can detect changes in
branch nodes' expansion state by listening for tree expansion or tree-will-expand events, as
described in How to Write a Tree Expansion Listener and How to Write a Tree-Will-Expand
Listener.
59

A specific node in a tree can be identified either by a TreePath, an object that encapsulates a
node and all of its ancestors, or by its display row, where each row in the display area
displays one node.

 An expanded node is a non-leaf node that will display its children when all its
ancestors are expanded.
 A collapsed node is one which hides them.
 A hidden node is one which is under a collapsed ancestor.

The rest of this section discusses the following topics:

 Creating a Tree
 Responding to Node Selection
 Customizing a Tree's Display
 Dynamically Changing a Tree
 Creating a Data Model
 The Tree API
 Examples that Use Trees

Creating a Tree

Here is a picture of an application, the top half of which displays a tree in a scroll pane.

Try this:

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

2. Expand one or more nodes.


You can do this by clicking the circle to the left of the item.
3. Collapse a node.
You do this by clicking the circle to the left of an expanded node.

The following code, taken from TreeDemo.java, creates the JTree object and puts it in a
scroll pane:

//Where instance variables are declared:


private JTree tree;
...
public TreeDemo() {
...
DefaultMutableTreeNode top =
new DefaultMutableTreeNode("The Java Series");
createNodes(top);
tree = new JTree(top);
...
JScrollPane treeView = new JScrollPane(tree);
...
}
The code creates an instance of DefaultMutableTreeNode to serve as the root node for the
tree. It then creates the rest of the nodes in the tree. After that, it creates the tree, specifying
the root node as an argument to the JTree constructor. Finally, it puts the tree in a scroll pane,
a common tactic because showing the full, expanded tree would otherwise require too much
space.

Here is the code that creates the nodes under the root node:

private void createNodes(DefaultMutableTreeNode top) {


DefaultMutableTreeNode category = null;
DefaultMutableTreeNode book = null;

category = new DefaultMutableTreeNode("Books for Java Programmers");


top.add(category);

//original Tutorial
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial: A Short Course on the Basics",
"tutorial.html"));
category.add(book);
61

//Tutorial Continued
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial Continued: The Rest of the JDK",
"tutorialcont.html"));
category.add(book);

//JFC Swing Tutorial


book = new DefaultMutableTreeNode(new BookInfo
("The JFC Swing Tutorial: A Guide to Constructing GUIs",
"swingtutorial.html"));
category.add(book);

//...add more books for programmers...

category = new DefaultMutableTreeNode("Books for Java Implementers");


top.add(category);

//VM
book = new DefaultMutableTreeNode(new BookInfo
("The Java Virtual Machine Specification",
"vm.html"));
category.add(book);

//Language Spec
book = new DefaultMutableTreeNode(new BookInfo
("The Java Language Specification",
"jls.html"));
category.add(book);
}

The argument to the DefaultMutableTreeNode constructor is the user object which is an


object that contains or points to the data associated with the tree node. The user object can be
a string, or it can be a custom object. If you implement a custom object, you should
implement its toString method so that it returns the string to be displayed for that node.
JTree, by default, renders each node using the value returned from toString, so it is important
that toString returns something meaningful. Sometimes, it is not feasible to override toString;
in such a scenario you can override the convertValueToText of JTree to map the object from
the model into a string that gets displayed.

For example, the BookInfo class used in the previous code snippet is a custom class that
holds two pieces of data: the name of a book, and the URL for an HTML file describing the
book. The toString method is implemented to return the book name. Thus, each node
associated with a BookInfo object displays a book name.
62

Note: You can specify text formatting in a tree node by putting HTML tags in the string for
the node. See Using HTML in Swing Components for details.

To summarize, you can create a tree by invoking the JTree constructor, specifying the class
that implements TreeNode as an argument. You should probably put the tree inside a scroll
pane, so that the tree would not take up too much space. You do not have to do anything to
make the tree nodes expand and collapse in response to user clicks. However, you do have to
add some code to make the tree respond when the user selects a node — by clicking the
node, for example.

Responding to Node Selection

Responding to tree node selections is simple. You implement a tree selection listener and
register it on the tree. The following code shows the selection-related code from the
TreeDemo program:
//Where the tree is initialized:
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);

//Listen for when the selection changes.


tree.addTreeSelectionListener(this);
...
public void valueChanged(TreeSelectionEvent e) {
//Returns the last path element of the selection.
//This method is useful only when the selection model allows a single selection.
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
tree.getLastSelectedPathComponent();

if (node == null)
//Nothing is selected.
return;

Object nodeInfo = node.getUserObject();


if (node.isLeaf()) {
BookInfo book = (BookInfo)nodeInfo;
displayURL(book.bookURL);
} else {
displayURL(helpURL);
}
}
The preceding code performs these tasks:

 Gets the default TreeSelectionModel for the tree, and then sets it up so that at
most one tree node at a time can be selected.
63

 Registers an event handler on the tree. The event handler is an object that
implements the TreeSelectionListener interface.
 In the event handler, determines which node is selected by invoking the tree's
getLastSelectedPathComponent method.
 Uses the getUserObject method to get the data associated with the node.

For more details about handling tree selection events, see How to Write a Tree Selection
Listener.

Customizing a Tree's Display

Here is a picture of some tree nodes, as drawn by the Java, Windows, and Mac OS look and
feel implementations.

Java look and feel Windows look and feel Mac OS look and feel

As the preceding figures show, a tree conventionally displays an icon and some text for each
node. You can customize these, as we will show shortly.

A tree typically also performs some look-and-feel-specific painting to indicate relationships


between nodes. You can customize this painting in a limited way. First, you can use
tree.setRootVisible(true) to show the root node or tree.setRootVisible(false) to hide it.
Second, you can use tree.setShowsRootHandles(true) to request that a tree's top-level nodes
— the root node (if it is visible) or its children (if not) — have handles that let them be
expanded or collapsed.

If you are using the Java look and feel, you can customize whether lines are drawn to show
relationships between tree nodes. By default, the Java look and feel draws angled lines
between nodes. By setting the JTree.lineStyle client property of a tree, you can specify a
different convention. For example, to request that the Java look and feel use only horizontal
lines to group nodes, use the following code:

tree.putClientProperty("JTree.lineStyle", "Horizontal");

To specify that the Java look and feel should draw no lines, use this code:

tree.putClientProperty("JTree.lineStyle", "None");
64

The following snapshots show the results of setting the JTree.lineStyle property, when using
the Java look and feel.

"Angled" (default) "Horizontal" "None"

No matter what the look and feel, the default icon displayed by a node is determined by
whether the node is a leaf and, if not, whether it is expanded. For example, in the Windows
and Motif look and feel implementations, the default icon for each leaf node is a dot; in the
Java look and feel, the default leaf icon is a paper-like symbol. In all the look-and-feel
implementations we have shown, branch nodes are marked with folder-like symbols. Some
look and feels might have different icons for expanded branches versus collapsed branches.

You can easily change the default icon used for leaf, expanded branch, or collapsed branch
nodes. To do so, you first create an instance of DefaultTreeCellRenderer. You could always
create your own TreeCellRenderer implementation from scratch, reusing whatever
components you like. Next, specify the icons to use by invoking one or more of the following
methods on the renderer: setLeafIcon (for leaf nodes), setOpenIcon (for expanded branch
nodes), setClosedIcon (for collapsed branch nodes). If you want the tree to display no icon
for a type of node, then specify null for the icon. Once you have set up the icons, use the
tree's setCellRenderer method to specify that the DefaultTreeCellRenderer paint its nodes.
Here is an example, taken from TreeIconDemo.java:

ImageIcon leafIcon = createImageIcon("images/middle.gif");


if (leafIcon != null) {
DefaultTreeCellRenderer renderer =
new DefaultTreeCellRenderer();
renderer.setLeafIcon(leafIcon);
tree.setCellRenderer(renderer);
}
Here is the screenshot of TreeIconDemo:
65

Try this:

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

If you want finer control over the node icons or you want to provide tool tips, you can do so
by creating a subclass of DefaultTreeCellRenderer and overriding the
getTreeCellRendererComponent method. Because DefaultTreeCellRenderer is a subclass of
JLabel, you can use any JLabel method — such as setIcon — to customize the
DefaultTreeCellRenderer.

The following code, from TreeIconDemo2.java, creates a cell renderer that varies the leaf
icon depending on whether the word "Tutorial" is in the node's text data. The renderer also
specifies tool-tip text, as the bold lines show.

Try this:

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

//...where the tree is initialized:


//Enable tool tips.
ToolTipManager.sharedInstance().registerComponent(tree);

ImageIcon tutorialIcon = createImageIcon("images/middle.gif");


if (tutorialIcon != null) {
tree.setCellRenderer(new MyRenderer(tutorialIcon));
66

}
...
class MyRenderer extends DefaultTreeCellRenderer {
Icon tutorialIcon;

public MyRenderer(Icon icon) {


tutorialIcon = icon;
}

public Component getTreeCellRendererComponent(


JTree tree,
Object value,
boolean sel,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {

super.getTreeCellRendererComponent(
tree, value, sel,
expanded, leaf, row,
hasFocus);
if (leaf && isTutorialBook(value)) {
setIcon(tutorialIcon);
setToolTipText("This book is in the Tutorial series.");
} else {
setToolTipText(null); //no tool tip
}

return this;
}

protected boolean isTutorialBook(Object value) {


DefaultMutableTreeNode node =
(DefaultMutableTreeNode)value;
BookInfo nodeInfo =
(BookInfo)(node.getUserObject());
String title = nodeInfo.bookName;
if (title.indexOf("Tutorial") >= 0) {
return true;
}

return false;
}
}
67

Here is the result:

You might be wondering how a cell renderer works. When a tree paints each node, neither
the JTree nor its look-and-feel-specific implementation actually contains the code that paints
the node. Instead, the tree uses the cell renderer's painting code to paint the node. For
example, to paint a leaf node that has the string "The Java Programming Language", the tree
asks its cell renderer to return a component that can paint a leaf node with that string. If the
cell renderer is a DefaultTreeCellRenderer, then it returns a label that paints the default leaf
icon followed by the string.

A cell renderer only paints; it cannot handle events. If you want to add event handling to a
tree, you need to register your handler on either the tree or, if the handling occurs only when
a node is selected, the tree's cell editor. For information about cell editors, see Concepts:
Editors and Renderers. That section discusses table cell editors and renderers, which are
similar to tree cell editors and renderers.

Dynamically Changing a Tree

The following figure shows an application called DynamicTreeDemo that lets you add nodes
to and remove nodes from a visible tree. You can also edit the text in each node.
68

The application is based on an example provided by tutorial reader Richard Stanford.

Try this:

 Click the Launch button to run the DynamicTreeDemo using Java™


Web Start (download JDK 6). Alternatively, to compile and run the
example yourself, consult the example index.

Here is the code that initializes the tree:


rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());

tree = new JTree(treeModel);


tree.setEditable(true);
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
By explicitly creating the tree's model, the code guarantees that the tree's model is an
instance of DefaultTreeModel. That way, we know all the methods that the tree model
supports. For example, we know that we can invoke the model's insertNodeInto method,
even though that method is not required by the TreeModel interface.

To make the text in the tree's nodes editable, we invoke setEditable(true) on the tree. When
the user has finished editing a node, the model generates a tree model event that tells any
listeners — including the JTree — that tree nodes have changed. Note that although
DefaultMutableTreeNode has methods for changing a node's content, changes should go
through the DefaultTreeModel cover methods. Otherwise, the tree model events would not
be generated, and listeners such as the tree would not know about the updates.

To be notified of node changes, we can implement a TreeModelListener. Here is an example


of a tree model listener that detects when the user has typed in a new name for a tree node:

class MyTreeModelListener implements TreeModelListener {


public void treeNodesChanged(TreeModelEvent e) {
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode)
(e.getTreePath().getLastPathComponent());

/*
* If the event lists children, then the changed
* node is the child of the node we have already
* gotten. Otherwise, the changed node and the
* specified node are the same.
*/
69

try {
int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode)
(node.getChildAt(index));
} catch (NullPointerException exc) {}

System.out.println("The user has finished editing the node.");


System.out.println("New value: " + node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
}

Here is the code that the Add button's event handler uses to add a new node to the tree:

treePanel.addObject("New Node " + newNodeSuffix++);


...
public DefaultMutableTreeNode addObject(Object child) {
DefaultMutableTreeNode parentNode = null;
TreePath parentPath = tree.getSelectionPath();

if (parentPath == null) {
//There is no selection. Default to the root node.
parentNode = rootNode;
} else {
parentNode = (DefaultMutableTreeNode)
(parentPath.getLastPathComponent());
}

return addObject(parentNode, child, true);


}
...
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
Object child,
boolean shouldBeVisible) {
DefaultMutableTreeNode childNode =
new DefaultMutableTreeNode(child);
...
treeModel.insertNodeInto(childNode, parent,
parent.getChildCount());
70

//Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
The code creates a node, inserts it into the tree model, and then, if appropriate, requests that
the nodes above it be expanded and the tree scrolled so that the new node is visible. To insert
the node into the model, the code uses the insertNodeInto method provided by the
DefaultTreeModel class.

Creating a Data Model

If DefaultTreeModel does not suit your needs, then you will need to write a custom data
model. Your data model must implement the TreeModel interface. TreeModel specifies
methods for getting a particular node of the tree, getting the number of children of a
particular node, determining whether a node is a leaf, notifying the model of a change in the
tree, and adding and removing tree model listeners.

Interestingly, the TreeModel interface accepts any kind of object as a tree node. It does not
require that nodes be represented by DefaultMutableTreeNode objects, or even that nodes
implement the TreeNode interface. Thus, if the TreeNode interface is not suitable for your
tree model, feel free to devise your own representation for tree nodes. For example, if you
have a pre-existing hierarchical data structure, you do not need to duplicate it or force it into
the TreeNode mold. You just need to implement your tree model so that it uses the
information in the existing data structure.

The following figure shows an application called GenealogyExample that displays the
descendants or ancestors of a particular person. (Thanks to tutorial reader Olivier Berlanger
for providing this example.)

Try this:

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

You can find the custom tree model implementation in GenealogyModel.java. Because the
model is implemented as an Object subclass instead of, say, a subclass of DefaultTreeModel,
it must implement the TreeModel interface directly. This requires implementing methods for
getting information about nodes, such as which is the root and what are the children of a
particular node. In the case of GenealogyModel, each node is represented by an object of
type Person, a custom class that does not implement TreeNode.

A tree model must also implement methods for adding and removing tree model listeners,
and must fire TreeModelEvents to those listeners when the tree's structure or data changes.
For example, when the user instructs GenealogyExample to switch from showing ancestors
to showing descendants, the tree model makes the change and then fires an event to inform
its listeners (such as the tree component).

How to Load Children Lazily

Lazy loading is a characteristic of an application when the actual loading and instantiation of
a class is delayed until the point just before the instance is actually used.

Do we gain anything by loading them lazily? Yes, this would definitely add to the
performance of an application. By lazily loading, you can dedicate the memory resources to
load and instantiate an object only when it is actually used. You can also speed up the initial
loading time of an application.

One of the ways you can lazily load children of a Tree is by utilizing the
TreeWillExpandListener interface. For example, you can declare and load root, grandparent
and parent of a Tree along with the application as shown in the following code:

Let us declare the root, grandparent and parent as shown below:

class DemoArea extends JScrollPane


implements TreeWillExpandListener {
.......
.......
72

private TreeNode createNodes() {


DefaultMutableTreeNode root;
DefaultMutableTreeNode grandparent;
DefaultMutableTreeNode parent;

root = new DefaultMutableTreeNode("San Francisco");

grandparent = new DefaultMutableTreeNode("Potrero Hill");


root.add(grandparent);

parent = new DefaultMutableTreeNode("Restaurants");


grandparent.add(parent);

dummyParent = parent;
return root;
}

You can load above declared nodes to the tree as shown in the following code:
TreeNode rootNode = createNodes();
tree = new JTree(rootNode);
tree.addTreeExpansionListener(this);
tree.addTreeWillExpandListener(this);
.......
.......
setViewportView(tree);

Now, you can load children lazily to the application whenever the parent node Restaurants is
visible in the application. To do this, let us declare two children in a separate method and call
that method as shown in the following code:

private void LoadLazyChildren(){


DefaultMutableTreeNode child;
child = new DefaultMutableTreeNode("Thai Barbeque");
dummyParent.add(child);
child = new DefaultMutableTreeNode("Goat Hill Pizza");
dummyParent.add(child);
textArea.append(" Thai Barbeque and Goat Hill Pizza are loaded lazily");
}

.......
.......

public void treeWillExpand(TreeExpansionEvent e)


73

throws ExpandVetoException {
saySomething("You are about to expand node ", e);
int n = JOptionPane.showOptionDialog(
this, willExpandText, willExpandTitle,
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
willExpandOptions,
willExpandOptions[1]);

LoadLazyChildren();
}

See How to Write a Tree-Will-Expand Listener for a description of Tree-Will-Expand


listeners.

Vous aimerez peut-être aussi