Java Tutorial Swing
Java Tutorial Swing
Java Tutorial Swing
1
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
The code for simple dialogs can be minimal. For example, here’s an
informational dialog:
An Overview of Dialogs
Every dialog is dependent on a frame. When that frame is destroyed, so are its dependent dialogs. When
the frame is iconified, its dependent dialogs disappear from the screen. When the frame is deiconified, its
dependent dialogs return to the screen. The AWT automatically provides this behavior.
A dialog can be modal. When a modal dialog is visible, it blocks user input
to all other windows in the program. The dialogs that JOptionPane provides
are modal. To create a non-modal dialog, you must use the JDialog class
directly.
The JDialog class is a subclass of the AWT java.awt.Dialog class. It
adds to Dialog a root pane and support for a default close operation. These
are the same features that JFrame has, and using JDialog directly is very
similar to using JFrame directly. If you’re going to use JDialog directly,
2
then you should understand the material in Using Top-Level Containers and
How to Make Frames, especially Responding to Window-Closing Events.
Even when you use JOptionPane to implement a dialog, you’re still using a
JDialog behind the scenes. The reason is that JOptionPane is simply a
container that can automatically create a JDialog and add itself to the
JDialog’s content pane.
JOptionPane Features
Using JOptionPane, you can create and customize several different kinds of dialogs. JOptionPane
provides support for laying out standard dialogs, providing icons, specifying the dialog’s title and text, and
customizing the button text. Other features allow you to customize the components the dialog displays and
specify where the dialog should appear onscreen. You can even specify that an option pane put itself into
an internal frame (JInternalFrame) instead of a JDialog.
Note: The internal frames that JOptionPane creates currently behave differently from modal dialogs.
They don’t behave modally, and in general seem more like frames than like dialogs. For this reason, we
don’t currently recommend their use.
3
(Java Look & Feel shown)
4
//custom title, error icon
JOptionPane.showMessageDialog(frame,
“Eggs aren’t supposed to be green.”,
“Inane error”,
JOptionPane.ERROR_MESSAGE);
//custom title, no icon
JOptionPane.showMessageDialog(frame,
“Eggs aren’t supposed to be green.”,
“A plain message”,
JOptionPane.PLAIN_MESSAGE);
//custom title, custom icon
JOptionPane.showMessageDialog(frame,
“Eggs aren’t supposed to be green.”,
“Inane custom dialog”,
JOptionPane.INFORMATION_MESSAGE,
icon);
showOptionDialog
Displays a modal dialog with the specified buttons, icons, message, title, and so on. With this method, you
can change the text that appears on the buttons of standard dialogs. You can also perform many other kinds
of customization.
5
The arguments to all of the showXxxDialog methods and JOptionPane constructors are
standardized, though the number of arguments for each method and constructor varies. The following list
describes each argument. To see the exact list of arguments for a particular method, see The Dialog API.
Component parentComponent
The first argument to each showXxxDialog method is always the parent component, which must be a
frame, a component inside a frame, or null. If you specify a frame, then the dialog will appear over the
center of the frame, and depend on that frame. If you specify a component inside a frame, then the dialog
will appear over the center of that component, and depend on that component’s frame. If you specify null,
then the look and feel picks an appropriate position for the dialog—generally the center of the screen, and
the dialog doesn’t depend on any visible frame.
6
Customizing Button Text
When you use JOptionPane to create a dialog, you can choose either to use the standard button text
(which might vary by look and feel) or to specify different text.
Even if you change the strings that the standard dialog buttons display, the
return value is still one of the pre-defined integers. For example, a
YES_NO_OPTION dialog always returns one of the following values:
YES_OPTION, NO_OPTION, or CLOSED_OPTION.
If you’re designing a custom dialog, on the other hand, then you need to
design your dialog’s API so that you can query the dialog about what the
user chose. For example, the dialog implemented in CustomDialog.java
has a getValidatedText method that returns the text the user entered.
7
DialogDemo contains two dialogs that implement a property change listener.
One of these dialogs is a custom modal dialog, implemented in
CustomDialog , that uses JOptionPane both to get the standard icon and to
get layout assistance. The other dialog, whose code is below, uses a standard
Yes/No JOptionPane, Though this dialog is rather useless as written, its
code is simple enough that you can use it as a template for more complex
dialogs.
Besides setting the property change listener, the following code also calls the
JDialog’s setDefaultCloseOperation method and implements a window
listener that handles the window close attempt properly. If you don’t care to
be notified when the user closes the window explicitly, then ignore the bold
code.
final JOptionPane optionPane = new JOptionPane(
“The only way to close this dialog is by\n”
+ “pressing one of the following buttons.\n”
+ “Do you understand?”,
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION);
final JDialog dialog = new JDialog(frame,
“Click a button”,
true);
dialog.setContentPane(optionPane);
dialog.setDefaultCloseOperation(
JDialog.DO_NOTHING_ON_CLOSE);
dialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
setLabel(“Thwarted user attempt to close window.”);
}
});
optionPane.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (dialog.isVisible()
&& (e.getSource() == optionPane)
&& (prop.equals(JOptionPane.VALUE_PROPERTY) ||
prop.equals(JOptionPane.INPUT_VALUE_PROPERTY)))
{
//If you were going to check something
//before closing the window, you’d do
//it here.
dialog.setVisible(false);
}
}
});
dialog.pack();
dialog.setVisible(true);
8
The API is listed as follows:
• Showing Standard Modal Dialogs (using JOptionPane Class Methods
• Methods for Using JOptionPanes Directly
• Frequently Used JDialog Constructors and Methods
Showing Standard Modal Dialogs (Using JOptionPane Class Methods)
Method Purpose
static void
showMessageDialog(Component,
Show a one-button, modal dialog that gives the
Object)
user some information. The arguments specify
static void
showMessageDialog(Component, (in order) the parent component, message, title,
Object, String, int) message type, and icon for the dialog. See
static void Creating and Showing Simple Dialogs for a
showMessageDialog(Component, discussion of the arguments and their effects.
Object, String, int, Icon)
Show a customized modal dialog. The arguments
static int specify (in order) the parent component,
showOptionDialog(Component, Object, message, title, option type, message type, icon,
String, int, int, Icon, Object[], options, and initial value for the dialog. See
Object) Creating and Showing Simple Dialogs for a
discussion of the arguments and their effects.
static int
showConfirmDialog(Component,
Object)
static int Show a modal dialog that asks the user a
showConfirmDialog(Component, question. The arguments specify (in order) the
Object, String, int) parent component, message, title, option type,
static int message type, and icon for the dialog. See
showConfirmDialog(Component, Creating and Showing Simple Dialogs for a
Object, String, int, int) discussion of the arguments and their effects.
static int
showConfirmDialog(Component,
Object, String, int, int, Icon)
static String
showInputDialog(Object) Show a modal dialog that prompts the user for
static String input. The single-argument version specifies just
showInputDialog(Component, Object) the message, with the parent component assumed
static String to be null. The arguments for the other versions
showInputDialog(Component, Object, specify (in order) the parent component,
String, int) message, title, message type, icon, options, and
static String initial value for the dialog. See Creating and
showInputDialog(Component, Object, Showing Simple Dialogs for a discussion of the
String, int, Icon, Object[], arguments and their effects.
Object)
static void
showInternalMessageDialog(...)
static void
Implement a standard dialog as an internal frame.
showInternalOptionDialog(...)
See the JOptionPane API documentation
static void
showInternalConfirmDialog(...) for the exact list of arguments.
static String
showInternalInputDialog(...)
Methods for Using JOptionPanes Directly
9
Method or Constructor Purpose
JOptionPane()
JOptionPane(Object)
JOptionPane(Object, int)
Creates a JOptionPane instance. See
JOptionPane(Object, int, int)
JOptionPane(Object, int, int, Icon) Creating and Showing Simple Dialogs
JOptionPane(Object, int, int, Icon, for a discussion of the arguments and
Object[]) their effects.
JOptionPane(Object, int, int, Icon,
Object[], Object)
static Frame Handy JOptionPane class methods
getFrameForComponent(Component) that find the frame or desktop pane,
static JDesktopPane respectively, that the specified
getDesktopPaneForComponent(Component) component is in.
Frequently Used JDialog Constructors and Methods
Method or Constructor Purpose
JDialog()
JDialog(Dialog) Creates a JDialog instance. The Frame
JDialog(Dialog, boolean)
argument, if any, is the frame (usually a
JDialog(Dialog, String)
JFrame object) that the dialog depends on.
JDialog(Dialog, String, boolean)
Make the boolean argument true to specify
JDialog(Dialog, String, boolean,
GraphicsConfiguration) a modal dialog, false or absent to specify a
JDialog(Frame) non-modal dialog. You can also specify the
JDialog(Frame, boolean) title of the dialog, using a string argument.
JDialog(Frame, String) The constructors taking the
JDialog(Frame, String, boolean) java.awt.GraphicsConfiguration
JDialog(Frame, String, boolean, argument were introduced in 1.4.
GraphicsConfiguration)
Get and set the content pane, which is usually
void setContentPane(Container) the container of all the dialog’s components.
Container getContentPane() See Using Top-Level Containers for more
information.
Get and set what happens when the user tries
to close the dialog. Possible values:
DISPOSE_ON_CLOSE,
void setDefaultCloseOperation(int)
DO_NOTHING_ON_CLOSE,
int getDefaultCloseOperation()
HIDE_ON_CLOSE (the default). See
Responding to Window-Closing Events for
more information.
10
subject.]
11
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
The following code, taken from FrameDemo, is a typical example of the code used to create and set up a
frame.
//1. Optional: Specify who draws the window
decorations.
JFrame.setDefaultLookAndFeelDecorated(true);
//2. Create the frame.
JFrame frame = new JFrame(“FrameDemo”);
//3. Optional: What happens when the frame
closes?
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLO
SE);
//4. Create components and put them in the frame.
//...create emptyLabel...
frame.getContentPane().add(emptyLabel,
BorderLayout.CENTER);
//5. Size the frame.
frame.pack();
12
1. Calling setDefaultLookAndFeelDecorated(true) requests that any subsequently created
frames have window decorations provided by the look and feel, and not by the window system. For
details, see Specifying Window Decorations.
2. The next line of code creates a frame using a constructor that lets you set the frame’s title. The other
frequently used JFrame constructor is the no-argument constructor.
3. Next the code specifies what should happen when the user closes the frame. The EXIT_ON_CLOSE
operation, not surprisingly, makes the program exit when the 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.
4. The next bit of code adds a blank label to the frame’s 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.
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.
5. 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’s size explicitly by calling setSize or setBounds
(which also sets the frame’s location). In general, using pack is preferable to calling setSize,
since pack leaves the frame’s layout manager in charge of the frame’s size, and layout managers are
good at adjusting to platform dependencies and other factors that affect component size.
This example doesn’t set the frame’s location, but it’s easy to do so
using either the setLocationRelativeTo or setLocation method.
For example, the following code centers a frame onscreen:
frame.setLocationRelativeTo(null);
6. 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.
Note: The pack and setVisible methods realize the frame and the components it contains. Once a
component has been realized, any code that modifies or inspects it should be executed on the event-
dispatching thread. For more information, refer to Threads and Swing .
The following snapshots show two frames that are identical except for their
window decorations. As you can tell by the appearance of the Close window
button in each frame, they both use the Java look and feel. However, only
the first frame uses window decorations provided by the Java look and feel.
The second uses decorations provided by the window system, which
happens to be Windows but could as easily be any other platform running
the 1.4 version of the Java platform.
13
A frame with look-and-feel-provided
A frame with window-system-provided decorations
decorations
The difference in window decorations is caused by calls to the JFrame static
method setLookAndFeelDecorated, which occur before each frame is
created. To use decorations provided by the look and feel, the argument to
setLookAndFeelDecorated must be true. To switch back to window system
(platform-specific) decorations, the argument must be false. Some look and
feels might not support this feature; in this case, the window system
decorations are used.
Version Note: Before 1.4, the decorations on a frame were always provided by the window system and
could not be changed.
The source code that creates these frames is in FrameDemo2.java . You can
run FrameDemo2 using Java Web Start . Besides showing how to choose
window decorations, FrameDemo2 also shows how to disable all window
decorations and gives an example of positioning windows.
14
Version Note: As of 1.4, 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 if it is 1.4 or a compatible version. In earlier versions
such as 1.3 the VM remains running even when all windows have been disposed of. See bug 4030718
for details.
The default close operation is executed after the 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 doesn’t exit the application, then the default close operation — disposing of the frame
— will then be performed.
Because each JFrame object has a root pane, frames have support for
interposing input and painting behavior in front of the frame’s 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:
• 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
Create a frame that is initially invisible.
The String argument provides a title for
JFrame() the frame. You can also use setTitle to
JFrame(String) set a frame’s title. To make the frame
visible, invoke setVisible(true) on
it.
void setDefaultCloseOperation(int) Set or get the operation that occurs when
int getDefaultCloseOperation() the user pushes the close button on this
frame. Possible choices are:
• DO_NOTHING_ON_CLOSE
15
• 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, and was introduced
in 1.3.
Set or get whether the window system
void setUndecorated(boolean) should provide decorations for this frame.
boolean isUndecorated() Works only if the frame is not yet
displayable (hasn’t been packed or
(in Frame)
shown). Typically used with full-screen
exclusive mode .
Determine whether subsequently created
JFrames should have their Window
static void
setDefaultLookAndFeelDecorated(boolean) decorations (such as borders, widgets for
static boolean closing the window, title) provided by the
isDefaultLookAndFeelDecorated() current look and feel. Note that this is only
a hint, as some look and feels may not
support this feature. Introduced in 1.4.
Setting the Window Size and Location
Method Purpose
void pack() Size the window so that all its contents are at or
(in Window) above their preferred sizes.
void setSize(int, int)
void setSize(Dimension) Set or get the total size of the window. The integer
Dimension getSize() arguments to setSize specify the width and
(in Component) height, respectively.
void setBounds(int, int, int, Set or get the size and position of the window. For
int) the integer version of setBounds, the window’s
void setBounds(Rectangle) upper left corner is at the x, y location specified by
Rectangle getBounds() the first two arguments, and has the width and
(in Component) height specified by the last two arguments.
void setLocation(int, int) Set or get the location of the upper left corner of the
Point getLocation() window. The parameters are the x and y values,
(in Component) respectively.
Position the window so that it’s centered over the
void specified component. If the argument is null, the
setLocationRelativeTo(Component) window is centered onscreen. To properly center the
(in Window) window, you should invoke this method after the
window’s size has been set.
Methods Related to the Root Pane
Method Purpose
void setContentPane(Container) Set or get the frame’s content pane. The content pane
contains the frame’s visible GUI components and should
16
Container getContentPane() be opaque.
JRootPane createRootPane() Create, set, or get the frame’s root pane. The root pane
void setRootPane(JRootPane) manages the interior of the frame including the content
JRootPane getRootPane() pane, the glass pane, and so on.
void setJMenuBar(JMenuBar) Set or get the frame’s menu bar to manage a set of menus
JMenuBar getJMenuBar() for the frame.
void setGlassPane(Component) Set or get the frame’s glass pane. You can use the glass
Component getGlassPane() pane to intercept mouse events or paint on top of your
program’s GUI.
void Set or get the frame’s layered pane. You can use the
setLayeredPane(JLayeredPane) frame’s layered pane to put components on top of or
JLayeredPane getLayeredPane() behind other components.
17
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
By default, panels are opaque. This makes them work well as content panes,
and can help painting efficiency, as described in Painting . You can make a
panel transparent by invoking setOpaque(false). A transparent panel
draws no background, so that any components underneath show through.
An Example
The Converter application described in The Anatomy of a Swing-Based Program uses panels in several
ways:
• One JPanel instance serves as a content pane for the application’s frame. This content pane uses a
two-row GridLayout to lay out its contents, and an empty border to put 5 pixels of space around
them. See Using Top-Level Containers for information about content panes.
• Two instances of a JPanel subclass, named ConversionPanel, are used to both contain
components and coordinate communication between components. The panels also have titled
borders, which describe their contents and paint a line around them. The panels use left-to-right
BoxLayout objects to lay out their contents.
• Two instances of an unnamed JPanel subclass are used to group components and restrict their size.
The panels use top-to-bottom BoxLayout objects to lay out their contents.
The following picture is the same as a picture in The Anatomy of a Swing-Based Program . It shows a
colorized version of Converter, in which each panel has a background color.
As the figure shows, panels are useful for grouping components, simplifying
component layout, and putting borders around groups of components. The
rest of this section gives hints on grouping and laying out components. For
information about using borders, see How to Use Borders .
18
can easily make a panel use any other layout manager by invoking the setLayout method or specifying
a layout manager when creating the panel. Here is an example of the first approach:
JPanel aPanel = new JPanel();
aPanel.setLayout(new BorderLayout());
Here is an example of setting the layout manager at instantiation:
JPanel aPanel = new JPanel(new BorderLayout());
For more information about choosing and using layout managers, see Laying
Out Components Within a Container .
Adding Components
When you add components to a panel, you use the add method. Exactly which arguments you specify to
the add method depend on which layout manager the panel uses. When the layout manager is
FlowLayout, BoxLayout, GridLayout, or GridBagLayout, you’ll typically use the one-
argument add method, like this:
aFlowPanel.add(aComponent);
aFlowPanel.add(anotherComponent);
When the layout manager is BorderLayout, you need to provide a second argument specifying the
added component’s position within the panel. For example:
aBorderPanel.add(aComponent, BorderLayout.CENTER);
aBorderPanel.add(anotherComponent, BorderLayout.SOUTH);
For information on the arguments to use with BorderLayout, see How to Use Borders .
19
Component
getComponent(int)
Component
getComponentAt(int,
int) Get the specified component or components. You can get a
Component component based on its index or x, y position.
getComponentAt(Point)
Component[]
getComponents()
void remove(Component)
void remove(int) Remove the specified component(s).
void removeAll()
Setting/Getting the Layout Manager
Method Purpose
void Set or get the layout manager for this panel. The layout
setLayout(LayoutManager) manager is responsible for positioning the panel’s components
LayoutManager getLayout() within the panel’s bounds according to some philosophy.
20
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
The code to create a scroll pane can be minimal. For example, here’s a
picture of a demo program that puts a text area in a scroll pane because the
text area’s size grows dynamically as text is appended to it:
Here’s the code that creates the text area, makes it the scroll pane’s client, and adds the scroll pane to the
frame:
textArea = new JTextArea(5, 30);
JScrollPane scrollPane = new
JScrollPane(textArea);
...
contentPane.setPreferredSize(new Dimension(400,
100));
...
contentPane.add(scrollPane, BorderLayout.CENTER);
The program provides the text area as an argument to JScrollPane’s constructor. This establishes the
text area as the scroll pane’s client. The scroll pane handles everything else: creating the scroll bars when
necessary, redrawing the client when the user moves the scroll knobs, and so on.
Without the bold line of code, the scroll pane would compute its preferred
size so that the text area, at its preferred size, would fit entirely within the
scroll pane. Consequently, the scroll pane would have no scroll bars. The
bold line sets the preferred size of the scroll pane’s container such that the
scroll pane is forced into a shorter-than-preferred area. Thus when the
program first appears onscreen, the scroll pane has a vertical scroll bar.
Refer to Sizing a Scroll Pane for information about using other techniques
for making a scroll pane the size you want.
The rest of this section discusses the following topics: [PENDING: We’ll
add a note that mousewheel scrolling is possible as of 1.4]
• How a Scroll Pane Works
• Setting the Scroll Bar Policy
• Providing Custom Decorations
• Implementing a Scrolling-Savvy Client
• Sizing a Scroll Pane
• Dynamically Changing the Client’s Size
21
• The Scroll Pane API
• Examples that Use Scroll Panes
The scroll pane in this application looks very different from the one in the previous demo program. Rather
than displaying text, this scroll pane contains a large image. The scroll pane also has two scroll bars, a row
header, a column header, and four corners, three of which have been customized.
This program establishes the scroll pane’s client when creating the scroll pane:
// where the member variables are declared
private ScrollablePicture picture;
...
// where the GUI is created
picture = new ScrollablePicture( ... );
JScrollPane pictureScrollPane = new
JScrollPane(picture);
You can change a scroll pane’s client dynamically by calling the setViewportView method. Note that
JScrollPane has no corresponding getViewportView method, so you should cache the client
object in a variable if you need to refer to it later.
When the user manipulates the scroll bars in a scroll pane, the area of the
client that is visible changes accordingly. This picture shows the relationship
between the scroll pane and its client and indicates the classes that the scroll
pane commissions to help:
22
A scroll pane uses a JViewport instance to manage the visible area of the client. The viewport is
responsible for computing the bounds of the current visible area, based on the positions of the scroll bars,
and displaying it.
A scroll pane uses two separate instances of JScrollBar for the scroll
bars. The scroll bars provide the interface for the user to manipulate the
visible area. The following figure shows the three areas of a scroll bar: the
knob, the buttons, and the track.
When the user moves the knob on the vertical scroll bar up and down, the visible area of the client moves
up and down. Similarly, when the user moves the knob on the horizontal scroll bar to the right and left, the
visible area of the client moves back and forth accordingly. The position of the knob relative to its track is
proportionally equal to the position of the visible area relative to the client. In the Java Look & Feel and
some others, the size of the knob gives a visual clue as to how much of the client is visible.
23
Setting the Scroll Bar Policy
On startup, the scroll pane in the ScrollDemo application has two scroll bars. If you make the window
as large as your screen, both scroll bars disappear because they are no longer needed. If you then shrink
the height of the window without changing its width, the vertical scroll bar reappears. Further
experimentation will show that in this application both scroll bars disappear and reappear as needed. This
behavior is controlled by the scroll pane’s scroll bar policy, Actually, it’s two policies: you specify the
policy for each scroll bar separately.
ScrollDemo doesn’t explicitly set the scroll pane’s sroll bar policies—it uses
the default. But you can set the policies when you create the scroll pane or
change them dynamically.
Of the constructors provided by JScrollPane, these two let you set the
scroll bar policies when you create the scroll pane:
JScrollPane(Component, int, int)
JScrollPane(int, int)
The first int specifies the policy for the vertical scroll bar, the second specifies the policy for the
horizontal scroll bar. You can also set the policies dynamically with the
setHorizontalScrollBarPolicy and setVerticalScrollBarPolicy methods. With both
the constructors and the methods, use one of the following constants defined in the
ScrollPaneConstants interface (which is implemented by JScrollPane):
Policy Description
VERTICAL_SCROLLBAR_AS_NEEDED The default. The scroll bar appears when the viewport
HORIZONTAL_SCROLLBAR_AS_NEEDED is smaller than the client and disappears when the
viewport is larger than the client.
VERTICAL_SCROLLBAR_ALWAYS Always display the scroll bar. The knob disappears if
HORIZONTAL_SCROLLBAR_ALWAYS the viewport is large enough to show the whole client.
VERTICAL_SCROLLBAR_NEVER Never display the scroll bar. Use this option if you don’t
HORIZONTAL_SCROLLBAR_NEVER want the user to directly control what part of the client
is shown. Perhaps you have an application that requires
all scrolling to occur programmatically.
24
As shown in the figure, the scroll pane in ScrollDemo has custom row and column headers.
Additionally, because all four sides are populated, all four corners are present. The program customizes
three of the corners—two just fill their area with the same color as the Rules, and the other contains a
toggle button. The fourth corner, the lower right corner, is the default provided by the scroll pane. Notice
that because the row and column headers are always present in this example, that the toggle button is also
always present.
If a corner contains a control that the user needs access to all the time, make
sure the sides that intersect at the corner are always present. For example, if
this application placed the toggle in the lower right corner where the scroll
bars intersect, then the toggle would disappear if the user resized the
window and even one of the scroll bars disappeared.
The scroll pane’s row and column headers are provided by a custom
JComponent subclass, Rule , that draws a ruler in centimeters or inches.
Here’s the code that creates and sets the scroll pane’s row and column
headers:
//...where the member variables are defined:
private Rule columnView;
private Rule rowView;
...
//...where the GUI is initialized:
ImageIcon david = new
ImageIcon(“images/youngdad.jpeg”);
...
// Create the row and column headers
columnView = new Rule(Rule.HORIZONTAL, true);
columnView.setPreferredWidth(david.getIconWidth()
);
rowView = new Rule(Rule.VERTICAL, true);
rowView.setPreferredHeight(david.getIconHeight())
;
25
...
pictureScrollPane.setColumnHeaderView(columnView)
;
pictureScrollPane.setRowHeaderView(rowView);
...
You can use any component for a scroll pane’s row and column headers. The scroll pane puts the row and
column headers in JViewPorts of their own. Thus, when scrolling horizontally, the column header
follows along, and when scrolling vertically, the row header follows along.
As a JComponent subclass, our custom Rule class puts its rendering code in
its paintComponent method. Careful scrutiny of the code reveals that
special effort is taken to draw only within the current clipping bounds. Your
custom row and column headers should do the same to ensure speedy
scrolling.
You can also use any component for the corners of a scroll pane.
ScrollDemo illustrates this by putting a toggle button in the upper left
corner, and custom Corner objects in the upper right and lower left
corners. Here’s the code that creates the Corner objects and calls setCorner
to place them:
// Create the corners.
JPanel buttonCorner = new JPanel();
isMetric = new JToggleButton(“cm”, true);
isMetric.setFont(new Font(“SansSerif”,
Font.PLAIN, 11));
isMetric.setMargin(new Insets(2,2,2,2));
isMetric.addItemListener(new UnitsListener());
buttonCorner.add(isMetric); //Use the default
FlowLayout
...
// Set the corners.
pictureScrollPane.setCorner(JScrollPane.UPPER_LEF
T_CORNER,
buttonCorner);
pictureScrollPane.setCorner(JScrollPane.LOWER_LEF
T_CORNER,
new Corner());
pictureScrollPane.setCorner(JScrollPane.UPPER_RIG
HT_CORNER,
new Corner());
[PENDING: We might want to discuss the newer LEADING/TRAILING constants.]
Remember that the size of each corner is determined by the size of the sides
intersecting there. For some components you must take care that the specific
instance of the component fits in its corner. For example, the program sets
the font and margins on the toggle button so that it fits within the space
established by the headers. It’s not an issue with the Corner class because
that class colors its entire bounds, whatever they happen to be, with a solid
color.
As you can see from the code, constants indicate the corner positions. This
figure shows the constant for each position:
26
The constants are defined in the ScrollPaneConstants interface, which JScrollPane
implements.
Here again are the three control areas of a scroll bar: the knob, the buttons,
and the track.
You might have noticed when manipulating the scroll bars in ScrollDemo that clicking the buttons
scrolls the image to a tick boundary. You might also have noticed that clicking in the track scrolls the
picture by a “screenful”. More generally, the button scrolls the visible area by a unit increment and the
track scrolls the visible area by a block increment. The behavior you see in the example is not the scroll
pane’s default behavior, but is specified by the client in its implementation of the Scrollable interface.
27
value required to position the image on a tick mark boundary. Here’s the
implementation:
public int getScrollableUnitIncrement(Rectangle
visibleRect,
int orientation,
int direction) {
//get the current position
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL)
currentPosition = visibleRect.x;
else
currentPosition = visibleRect.y;
//return the number of pixels between
currentPosition
//and the nearest tick mark in the indicated
direction
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement) *
maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement :
newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
*
maxUnitIncrement - currentPosition;
}
}
If the image is already on a tick mark boundary, this method returns the number of pixels between ticks.
Otherwise, it returns the number of pixels from the current location to the nearest tick.
28
Some of these classes include other methods related to scrolling.
If the client is not scrolling-savvy, then the scroll pane sizes itself so that the
client displays at its preferred size. For typical unsavvy clients, this makes
the scroll pane redundant. That is, the scroll pane has no scroll bars because
the client’s preferred size is big enough to display the entire client. In this
case, if the client doesn’t change size dynamically, you should probably limit
the size of the scroll pane by setting its preferred size or the preferred size of
its container.
If the client is scrolling-savvy, then the scroll pane uses the value returned
by the client’s getPreferredScrollableViewportSize method to
compute the size of its viewport. Implementations of this method generally
report a preferred size for scrolling that’s smaller than the component’s
standard preferred size. For example, by default, the value returned by
JList’s implementation of getPreferredScrollableViewportSize is just
big enough to display eight rows.
Scrolling-savvy classes, like lists, tables, text components, and trees, often
provide one or more methods that let programmers affect the size returned
from getPreferredScrollableViewportSize. For example, you can set
the number of visible rows in a list or a tree by calling the
setVisibleRowCount. The list or tree takes care of figuring out the size
needed to display that number of rows.
Refer to the Methods in Other Classes Related to Scrolling for information
about scrolling-related methods provided by classes other than
JScrollPane. And remember—if you don’t like the value that
getPreferredScrollableViewportSize returns, you can always set the
preferred size of the scroll pane or its container.
Here’s a picture of an application that changes the client’s size whenever the
user places a circle whose bounds fall outside of the client’s current bounds.
The program also changes the client’s size when the user clears the drawing
area:
29
You can find the full source code for this example in ScrollDemo2.java , which is based on an
example provided by tutorial reader John Vella.
Here’s the code that changes the drawing area’s size when necessary:
if (changed) {
//Update client’s preferred size because the area
taken up
//by the graphics has gotten larger or smaller
(if cleared)
drawingArea.setPreferredSize(/* the new size */);
//This lets the scroll pane know to update itself
//and its scroll bars.
drawingArea.revalidate();
}
Note that when the client changes size, the scroll bars adjust. The scroll pane doesn’t resize, nor does the
viewport.
The API for using scroll panes falls into these categories:
• Setting Up the Scroll Pane
• Decorating the Scroll Pane
• Implementing the Scrollable Interface
• Methods in Other Classes Related to Scrolling
Setting Up the Scroll Pane
Method or Constructor Purpose
JScrollPane() Create a scroll pane. The Component parameter,
JScrollPane(Component) when present, sets the scroll pane’s client. The two
JScrollPane(int, int)
30
int parameters, when present, set the vertical and
JScrollPane(Component, int, int)
horizontal scroll bar policies (respectively).
void setViewportView(Component) Set the scroll pane’s client.
Set or get the vertical scroll policy.
ScrollPaneConstants defines three values
void for specifying this policy:
setVerticalScrollBarPolicy(int) VERTICAL_SCROLLBAR_AS_NEEDED (the
int getVerticalScrollBarPolicy() default),
VERTICAL_SCROLLBAR_ALWAYS, and
VERTICAL_SCROLLBAR_NEVER.
Set or get the horizontal scroll policy.
ScrollPaneConstants defines three values
void
setHorizontalScrollBarPolicy(int) for specifying this policy:
HORIZONTAL_SCROLLBAR_AS_NEEDED
int
getHorizontalScrollBarPolicy() (the default),
HORIZONTAL_SCROLLBAR_ALWAYS, and
HORIZONTAL_SCROLLBAR_NEVER.
void setViewportBorder(Border)
Border getViewportBorder() Set or get the border around the viewport.
31
greater than 0 indicates down or right.
Get the preferred size of the viewport. This
allows the client to influence the size of the
Dimension
viewport in which it is displayed. If the
getPreferredScrollableViewportSize()
viewport size is unimportant, implement this
method to return getPreferredSize.
Get whether the scroll pane should force the
boolean client to be the same width or height as the
getScrollableTracksViewportWidth() viewport. A return value of true from
boolean either of these methods effectively disallows
getScrollableTracksViewportHeight() horizontal or vertical scrolling
(respectively).
Methods in Other Classes Related to Scrolling
Method Purpose
void ensureIndexIsVisible(int) Scroll so that the row at the specified index is visible. This
method calls scrollRectToVisible and works only
(in JList)
if the list is in a container that supports scrolling.
void setVisibleRowCount(int) Set or get how many rows of the tree are visible. The
int getVisibleRowCount() getPreferredScrollableViewportSize method
(in JTree) uses the visible row count to compute its return value.
void scrollPathToVisible(TreePath) Scroll so that the specified tree path or row at the specified
void scrollRowToVisible(int) index is visible. These methods call
scrollRectToVisible and work only if the tree is in
(in JTree)
a container that supports scrolling.
void setScrollsOnExpand(boolean) Set or get whether scrolling occurs automatically when the
boolean getScrollsOnExpand() user expands a node. True by default. This feature works
(in JTree) only when the tree is in a container that supports scrolling.
void
setPreferredScrollableViewportSize Set the value to be returned by
(Dimension) getPreferredScrollableViewportSize.
(in JTable)
32
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Below is the code from SplitPaneDemo that creates and sets up the split pane.
//Create a split pane with the two scroll panes
in it.
splitPane = new
JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
listScrollPane, pictureScrollPane);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation(150);
//Provide minimum sizes for the two components in
the split pane
Dimension minimumSize = new Dimension(100, 50);
listScrollPane.setMinimumSize(minimumSize);
pictureScrollPane.setMinimumSize(minimumSize);
The constructor used by this example takes three arguments. The first indicates the split direction. The
other arguments are the two components to put in the split pane. Refer to Setting the Components in a
Split Pane for information about JSplitPane methods that set the components dynamically.
33
The split pane in this example is split horizontally—the two components
appear side by side—as specified by the JSplitPane.HORIZONTAL_SPLIT
argument to the constructor. Split pane provides one other option, specified
with JSplitPane.VERTICAL_SPLIT, that places one component above the
other. You can change the split direction after the split pane has been created
with the setOrientation method.
Two small arrows appear at the top of the divider in the example’s split
pane. These arrows let the user collapse (and then expand) either of the
components with a single click. The current look and feel determines
whether these controls appear by default. In the Java Look & Feel, they are
turned off by default. The example turned them on with a call to the
setOneTouchExpandable.
Like other containers, JSplitPane supports the add method. Split pane puts
the first component added in the left or top position. The danger of using
add is that you can inadvertantly call it too many times, in which case, the
split pane’s layout manager will throw a rather esoteric-looking exception. If
you are using the add method and a split pane is already populated, you first
need to remove the existing components with remove.
If you put only one component in a split pane, then the divider will be stuck
at the right side or the bottom of the split pane, depending on its split
direction.
34
Use the setDividerLocation method to position the divider programmatically. You can specify the
new position by pixel or by percentage. To get the current divider location specified in pixels call
getDividerLocation.
//Set divider at pixel 150
splitPane.setDividerLocation(150);
//25% of the space goes to left/top
splitPane.setDividerLocation(0.25);
Another handy method, resetToPreferredSizes, sets the divider location such that the two
components are at their preferred sizes. This is the initial arrangement of a split pane, unless specified
otherwise.
The user can set the divider location by dragging it. A split pane does not
allow the user to make either of its components smaller than the
component’s minimum size by dragging the divider. So, you can affect the
divider’s range by setting the minimum sizes of the two components in the
split pane. For example, to ensure that a minimum amount of each
component in a split is visible at all times, set the minimum size of each
component. To allow the user to drag the divider all the way to the edge of
the split pane, use 0 for the minimum size.
If you don’t set the minimum size of the two components in a split pane,
then you might end up with a split pane whose divider won’t move. By
default, a split pane sizes its components at their preferred size, which for
many components is equal to the minimum size. This means that both
components are already displayed at their minimum sizes and the divider is
stuck. Most programs that put standard components in a split pane need to
set the minimum sizes of the components in the split pane to a smaller size.
If the top portion of the split pane looks familiar to you, it’s because the program puts the split pane
created by SplitPaneDemo inside a second split pane. A simple JLabel is the other component in the
second split pane. This is not the most practical use of a nested split pane, but it gets the point across.
Here’s the code, which you can find in SplitPaneDemo2.java :
35
//Create an instance of SplitPaneDemo
SplitPaneDemo splitPaneDemo = new
SplitPaneDemo();
JSplitPane top = splitPaneDemo.getSplitPane();
...
//Create a regular old label
label = new JLabel(“Click on an image name in the
list.”,
JLabel.CENTER);
//Create a split pane and put “top” (a split
pane)
//and JLabel instance in it.
JSplitPane splitPane = new
JSplitPane(JSplitPane.VERTICAL_SPLIT,
top, label);
Refer to Solving Common Component Problems for information about fixing a border problem that can
appear when nesting split panes.
36
setOneTouchExpandable(boolean) the divider to expand/collapse the divider. The default
boolean isOneTouchExpandable() depends on the look and feel. In the Java Look & Feel,
it’s off by default.
Managing the Split Pane’s Contents
Method Purpose
void
setTopComponent(Component)
void
setBottomComponent(Component)
void
Set or get the indicated component. Each method works
setLeftComponent(Component)
void regardless of the split pane’s orientation. Top and left are
setRightComponent(Component) equivalent, and bottom and right are equivalent.
Component getTopComponent()
Component getBottomComponent()
Component getLeftComponent()
Component getRightComponent()
void remove(Component)
void removeAll() Remove the indicated component(s) from the split pane.
Add the component to the split pane. You can add only
two components to a split pane. The first component
void add(Component) added is the top/left component. The second component
added is the bottom/right component. Any attempt to add
more components results in an exception.
Positioning the Divider
Method Purpose
void
Set or get the current divider location. When setting the
setDividerLocation(double)
divider location, you can specify the new location as a
void setDividerLocation(int)
percentage (double) or a pixel location (int).
int getDividerLocation()
Move the divider such that both components are at their
void resetToPreferredSizes() preferred sizes. This is how a split pane divides itself at
startup, unless specified otherwise.
void
setLastDividerLocation(int) Set or get the previous position of the divider.
int getLastDividerLocation()
int
Get the minimum and maximum locations for the divider.
getMaximumDividerLocation()
int These are set implicitly by setting the minimum sizes of
getMinimumDividerLocation() the split pane’s two components.
37
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Here is a picture of an application that has a tabbed pane with four tabs:
As the TabbedPaneDemo example shows, a tab can have a tool tip, and it can
display both text and an image. The example shows the tabs in their default
position, at the top of the tabbed pane. You can change the tab position to be
at the left, right, or bottom of the tabbed pane using the setTabPlacement
method.
Below is the code from TabbedPaneDemo.java that creates the tabbed
pane in the previous example. Note that no event-handling code is necessary.
The JTabbedPane object takes care of mouse events for you.
ImageIcon icon = new
ImageIcon(“images/middle.gif”);
JTabbedPane tabbedPane = new JTabbedPane();
Component panel1 = makeTextPanel(“Blah”);
tabbedPane.addTab(“One”, icon, panel1, “Does
nothing”);
tabbedPane.setSelectedIndex(0);
Component panel2 = makeTextPanel(“Blah blah”);
tabbedPane.addTab(“Two”, icon, panel2, “Does
twice as much nothing”);
Component panel3 = makeTextPanel(“Blah blah
blah”);
tabbedPane.addTab(“Three”, icon, panel3, “Still
does nothing”);
Component panel4 = makeTextPanel(“Blah blah blah
blah”);
tabbedPane.addTab(“Four”, icon, panel4, “Does
nothing at all”);
38
The following tables list the commonly used JTabbedPane constructors and methods. The API for
using tabbed panes falls into these categories:
• Creating and Setting Up a Tabbed Pane
• Inserting, Removing, Finding, and Selecting Tabs
• Changing Tab Appearance
Creating and Setting Up a Tabbed Pane
Method or Constructor Purpose
Create a tabbed pane. The first optional argument specifies
where the tabs should appear. By default, the tabs appear at the
top of the tabbed pane. You can specify these positions (defined
in the SwingConstants interface, which JTabbedPane
JTabbedPane() implements): TOP, BOTTOM, LEFT, RIGHT. The second
JTabbedPane(int) optional argument specifies the tab layout policy. You can
JTabbedPane(int, int) specify one of these policies (defined in JTabbedPane):
WRAP_TAB_LAYOUT or SCROLL_TAB_LAYOUT Scrollable
tabs were introduced in 1.4 and, although supported, are not
recommended. For more information, please see the Java Look
and Feel Design Guidelines.
addTab(String, Icon, Add a new tab to the tabbed pane. The first argument specifies
Component, String) the text on the tab. The optional icon argument specifies the
addTab(String, Icon, tab’s icon. The component argument specifies the component
Component) that the tabbed pane should show when the tab is selected. The
addTab(String, Component) fourth argument, if present, specifies the tool tip text for the tab.
Set or get the policy that the tabbed pane uses in laying out the
void tabs when all the tabs do not fit within a single run. Possible
setTabLayoutPolicy(int) values are WRAP_TAB_LAYOUT and SCROLL_TAB_LAYOUT.
int getTabLayoutPolicy() The default, and preferred, policy is WRAP_TAB_LAYOUT.
Introduced in 1.4.
void setTabPlacement(int) Set or get where the tabs appear, relative to the content. Possible
values (defined in SwingConstants, which is implemented
int getTabPlacement() by JTabbedPane) are TOP, BOTTOM, LEFT, and RIGHT.
Inserting, Removing, Finding, and Selecting Tabs
Method Purpose
Insert a tab at the specified index, where the
insertTab(String, Icon, Component,
first tab is at index 0. The arguments are the
String, int)
same as for addTab.
remove(Component) Remove the tab corresponding to the specified
removeTabAt(int) component or index.
removeAll() Remove all tabs.
int indexOfComponent(Component)
int indexOfTab(String) Return the index of the tab that has the specified
int indexOfTab(Icon) component, title, or icon.
void setSelectedIndex(int) Select the tab that has the specified component
void setSelectedComponent(Component) or index. Selecting a tab has the effect of
displaying its associated component.
int getSelectedIndex() Return the index or component for the selected
Component getSelectedComponent() tab.
Changing Tab Appearance
Method Purpose
39
void setComponentAt(int,
Component) Set or get which component is associated with the tab at the
Component specified index. The first tab is at index 0.
getComponentAt(int)
void setTitleAt(int,
String) Set or get the title of the tab at the specified index.
String getTitleAt(int)
void setIconAt(int,
Icon)
Icon getIconAt(int)
void
setDisabledIconAt(int, Set or get the icon displayed by the tab at the specified index.
Icon)
Icon
getDisabledIconAt(int)
void
setBackgroundAt(int,
Color) Set or get the background or foreground color used by the tab at
Color the specified index. By default, a tab uses the tabbed pane’s
getBackgroundAt(int) background and foreground colors. For example, if the tabbed
void pane’s foreground is black, then each tab’s title is black except for
setForegroundAt(int, any tabs for which you specify another color using
Color) setForegroundAt.
Color
getForegroundAt(int)
void setEnabledAt(int,
boolean) Set or get the enabled state of the tab at the specified index.
boolean isEnabledAt(int)
void setMnemonicAt(int,
int) Set or get the keyboard mnemonic for accessing the specified tab.
int getMnemonicAt(int) Introduced in 1.4.
40
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
The following pictures show an application that contains a tool bar above a
text area.
By default, the user can drag the tool bar to a different edge of its container
or out into a window of its own. The next figure shows how the application
looks after the user has dragged the tool bar to the right edge of its container.
For the drag-out behavior to work correctly, the tool bar must be in a container that uses
BorderLayout. The component that the tool bar affects is generally in the center of the container. The
tool bar must be the only other component in the container; it must not be in the center.
The next figure shows how the application looks after the user has dragged
the tool bar outside its window.
The following code implements the tool bar. You can find the entire program
in ToolBarDemo.java . It relies on three images.
41
Note: If any buttons in your tool bar duplicate functionality of other components, such as menu items,
then you should probably create and add the tool-bar buttons as described in How to Use Actions.
PENDING: We need to change the text and examples to reflect the fact that, whenever practical, toolbars
using the Java look and feel should use the images from the Java look and feel graphics respository .
Here is an example of doing so:
String imgLocation =
“toolbarButtonGraphics/navigation/Back24.gif”;
URL imageURL =
getClass().getResource(imgLocation);
if (imageURL != null) {
button = new JButton(new ImageIcon(imageURL));
}
You need to include the JLF graphics repository JAR file in the class path of the program. For example:
java -cp .;jars/jlfgr-1_0.jar ToolBarDemo
[Microsoft Windows]
java -cp .:jars/jlfgr-1_0.jar ToolBarDemo [UNIX]
42
• Using the setFloatable(false) to make a tool bar immovable.
• Adding a separator to a tool bar.
• Adding a non-button component to a tool bar.
Here is a picture of the new UI,
You can find the entire program in ToolBarDemo2.java . It relies on three images.
Because the tool bar can no longer be dragged, it no longer has bumps at its
left edge. Here’s the code that turns off dragging:
toolBar.setFloatable(false);
The biggest visible difference is that the tool bar contains two new components, which are preceded by a
blank space—a separator. Here is the code that adds the separator:
toolBar.addSeparator();
Here is the code that adds the new components:
//fourth button
button = new JButton(“Another button”);
...
toolBar.add(button);
//fifth component is NOT a button!
JTextField textField = new JTextField(“A text
field”);
...
toolBar.add(textField);
You can easily make the components in a tool bar be aligned along their tops or bottoms, instead of
centered, by invoking the setAlignmentY method. For example, to align the tops of all the
components in a tool bar, invoke setAlignmentY(TOP_ALIGNMENT) on each component. Similarly,
you can use the setAlignmentX method to specify the alignment of components when the tool bar is
vertical. This flexibility of layout is possible because tool bars use BoxLayout to position their
components. For more information, see How to Use BoxLayout .
43
add(Action) method to create the button and add it to
the tool bar. As of 1.3, that method is no longer
recommended. You can instead associate a button with an
Action using the setAction(Action) method.
44
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
You should consider carefully whether to base your program’s GUI around
frames or internal frames. Switching from internal frames to frames or vice
versa isn’t necessarily a simple task. By experimenting with both frames and
internal frames, you can get an idea of the tradeoffs involved in choosing
one over the other.
Here is a picture of an application that has two internal frames (one of which
is iconified) inside a regular frame:
As the figure shows, the window decorations on an internal frame reflect the
Java Look & Feel. However, the window that contains the internal frame has
the decorations for the native look and feel (in this case, Motif).
The following code, taken from InternalFrameDemo.java , creates the
desktop and internal frames in the previous example.
...//In the constructor of InternalFrameDemo, a
JFrame subclass:
desktop = new JDesktopPane();
createFrame(); //Create first window
setContentPane(desktop);
...
//Make dragging faster:
//desktop.putClientProperty(“JDesktopPane.dragMod
e”, //pre-1.3 code
“outline”);
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MOD
E); //1.3+ code
...
protected void createFrame() {
MyInternalFrame frame = new MyInternalFrame();
frame.setVisible(true); //necessary as of 1.3
desktop.add(frame);
try {
45
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
}
Note: Just as for a regular frame, you must invoke setVisible(true) or show() on an internal
frame to display it. In early versions of the Java 2 platform (such as v1.2.2), this code has no effect
because the internal frame is visible by default. However, starting in the 1.3 release, the internal frame
does not appear until you explicitly make it visible.
46
that work only if the internal frame’s container is a layered pane such as a
JDesktopPane.
Performance Tip: Because dragging internal frames can be slow, Swing 1.1.1 and the Java 2 Platform, v
1.2.2, add a way to make it zippy: outline dragging. With outline dragging, only the outline of the internal
frame is painted at the current mouse position while the window’s being dragged. The internal frame’s
innards are not repainted at a new position until dragging stops. The default, slower behavior is to
reposition and repaint the entire internal frame continuously while it’s being moved.
47
As of v 1.3, use the new JDesktopPane method
setDragMode to specify outline dragging. For example:
desktop.setDragMode(JDesktopPane.OUTL
INE_DRAG_MODE);
48
Method Purpose
void Set or get the internal frame’s content pane, which generally
setContentPane(Container) contains all of the internal frame’s GUI, with the exception of
Container getContentPane() the menu bar and window decorations.
void setJMenuBar(JMenuBar) Set or get the internal frame’s menu bar. Note that some early
JMenuBar getJMenuBar() Swing releases do not include this method.
Specifying the Internal Frame’s
Visibility, Size, and Location Method Purpose
void pack() Size the internal frame so that its components are at their
preferred sizes.
void setLocation(Point) Set the position of the internal frame. (Inherited from
void setLocation(int, int) Component).
void setBounds(Rectangle)
Explicitly set the size and location of the internal frame.
void setBounds(int, int,
(Inherited from Component).
int, int)
void setSize(Dimension)
void setSize(int, int)
Explicitly set the size of the internal
frame. (Inherited from Component).
Performing Window Operations on the Internal Frame
Method Purpose
Set or get what the internal
frame does when the user
attempts to “close” the internal
frame. The default value is
void setDefaultCloseOperation(int) HIDE_ON_CLOSE. Other
int getDefaultCloseOperation() possible values are
DO_NOTHING_ON_CLOSE
and DISPOSE_ON_CLOSE.
See Responding to Window-
Closing Events for details.
Add or remove an internal
frame listener
void
(JInternalFrame’s
addInternalFrameListener(InternalFrameListener)
void removeInternalFrameListener equivalent of a window
(InternalFrameListener) listener). See How to Write an
Internal Frame Listener for
more information.
If the internal frame’s parent is
a layered pane such as a
void moveToFront()
void moveToBack() desktop pane, moves the
internal frame to the front or
back (respectively) of its layer.
void setClosed(boolean) Set or get whether the internal
49
boolean isClosed() frame is currently closed.
Iconify or deiconify the
void setIcon(boolean) internal frame, or determine
boolean isIcon() whether it’s currently
iconified.
Maximize or restore the
void setMaximum(boolean)
boolean isMaximum() internal frame, or determine
whether it’s maximized.
Set or get whether the internal
void setSelected(boolean) frame is the currently
boolean isSelected() “selected” (activated) internal
frame.
Controlling Window Decorations and Capabilities
Method Purpose
void setFrameIcon(Icon) Set or get the icon displayed in the title bar of the internal
Icon getFrameIcon() frame (usually in the top-left corner).
void setClosable(boolean)
boolean isClosable() Set or get whether the user can close the internal frame.
void
setIconifiable(boolean) Set or get whether the internal frame can be iconified.
boolean isIconifiable()
void
setMaximizable(boolean) Set or get whether the user can maximize this internal frame.
boolean isMaximizable()
void setResizable(boolean)
boolean isResizable() Set or get whether the internal frame can be resized.
void setTitle(String)
String getTitle() Set or get the window title.
50
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
51
Here’s the code from the example program that creates the layered pane:
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300,
310));
layeredPane.setBorder(BorderFactory.createTitledB
order(
“Move the Mouse to Move Duke”));
layeredPane.addMouseMotionListener(new
MouseMotionAdapter() {
...
});
The code uses JLayeredPane’s only constructor—the no-argument constructor—to create the layered
pane. The rest of the code uses methods inherited from superclasses to give the layered pane a preferred
size and a border, and add a mouse-motion listener to it. The mouse-motion listener just moves the Duke
image around in response to mouse movement.
Ultimately, the example adds the layered pane to the frame’s content pane:
Container contentPane = getContentPane();
...
contentPane.add(layeredPane);
As we’ll show you a bit later, you add components to a layered pane using an add method. When adding
a component to a layered pane, you specify the component’s depth, and optionally, its position within its
depth. The layered pane in the demo program contains six labels—the five colored labels and a sixth one
that displays the Duke image. As the program demonstrates, both the depth of a component and its
position with that depth can change dynamically.
52
• Adding Components and Setting Component Depth
• Setting a Component’s Position Within Its Depth
• Laying Out Components in a Layered Pane
• The Layered Pane API
• Examples that Use Layered Panes
The example program uses a two-argument version of the add method. The
first argument is the component to add, the second is an Integer object,
specifying the depth. This program uses the for loop’s iteration variable to
specify depths. The actual values don’t matter much. What matters is the
relative value of the depths and that you are consistent within your program
in how you use each depth.
Note: If you use the root pane’s layered pane, be sure to use its depth conventions. Refer to The Layered
Pane for details. That section shows you how to modify LayeredPaneDemo to use the root pane’s
layered pane. With the modifications, you can see how the dragging Duke image relates to the combo box
in the control panel.
A note of caution: When adding a component to a layered pane you specify the layer with an Integer.
When using setLayer to change a component’s layer, you use an int. You might think that if you use
53
an int instead of an Integer with the add method, the compiler would complain or your program
would throw an illegal argument exception. But the compiler says nothing, which results in a common
layered pane problem. You can use the API tables at the end of this section to check the types of the
arguments and return values for methods that deal with layers.
Positions are specified with an int between -1 and (n - 1), where n is the
number of components at the depth. Unlike layer numbers, the smaller the
position number, the higher the component within its depth. Using -1 is the
same as using n - 1; it indicates the bottom-most position. Using 0 specifies
that the component should be in the top-most position within its depth. As
the following figure shows, with the exception of -1, a lower position
number indicates a higher position within a depth.
A component’s position within its layer can change dynamically. In the example, you can use the check
box to determine whether Duke label is in the top position at its depth. Here’s the actionPerformed
method for the action listener registered on the check box:
public void actionPerformed(ActionEvent e) {
if (onTop.isSelected())
layeredPane.moveToFront(dukeLabel);
else
layeredPane.moveToBack(dukeLabel);
}
When the user selects the check box, the moveToFront method moves Duke to the front (position 0).
And when the user deselects check box, Duke gets moved to the back with the moveToBack method.
You can also use the setPosition method or the three-argument version of setLayer to change a
component’s position.
54
The example uses the setBounds method to set the size and position of each
of the labels:
dukeLabel.setBounds(15, 225,
icon.getIconWidth(),
icon.getIconHeight());
...
label.setBounds(origin.x, origin.y, 140, 140);
When the user moves the mouse around, the program calls setPosition to change Duke’s position:
dukeLabel.setLocation(e.getX()-XFUDGE, e.getY()-
YFUDGE);
Although a layered pane has no layout manager by default, you can still assign a layout manager to the
layered pane. All of the layout managers provided by the Java platform arrange the components as if they
were all on one layer. Here’s a version of the previous demo that sets the layered pane’s layout manager to
an instance of GridLayout, using that layout manager to lay out six colored labels.
You can find the code for this program in LayeredPaneDemo2.java . This program also needs the
dukeWaveRed.gif image file.
Many programs use intermediate containers (such as panels) and their layout
managers to lay out components on the same layer, but use absolute
positioning to lay out components on different layers. For more information
about absolute positioning, see Doing Without a Layout Manager (Absolute
Positioning) .
The API for using layered pane falls into these categories:
55
• Creating or Getting a Layered Pane
• Layering Components
• Setting Component’s Intra-Layer Positions
Creating or Getting a Layered Pane
Method or Constructor Purpose
JLayeredPane() Create a layered pane.
JLayeredPane getLayeredPane()
(in JApplet, JDialog, JFrame, and Get the automatic layered pane in an applet, dialog,
frame, or internal frame.
JInternalFrame)
Layering Components
Method Purpose
Add the specified component to the layered pane. The
second argument, when present, is an Integer that
void add(Component) indicates the layer. The third argument, when present,
void add(Component, Object) indicates the component’s position within its layer. If
void add(Component, Object, you use the one-argument version of this method, the
int) component is added to layer 0. If you use the one- or
two-argument version of this method, the component is
placed underneath all other components currently in the
same layer.
void setLayer(Component, int) Change the component’s layer. The second argument
void setLayer(Component, int, indicates the layer. The third argument, when present,
int) indicates the component’s position within its layer.
int getLayer(Component)
int getLayer(JComponent) Get the layer for the specified component.
int highestLayer()
int lowestLayer() Compute the highest or lowest layer currently in use.
void moveToFront(Component) Move the specified component to the front or back of its
void moveToBack(Component) layer.
56
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Using Top-Level Containers tells you the basics of using root panes—
getting the content pane, setting its layout manager, and adding Swing
components to it. This section tells you more about root panes, including the
components that make up a root pane and how you can use them. Another
place to get information about root panes is The Swing Connection,
especially the article Understanding Containers.
57
Here’s a picture of an application that demonstrates glass pane features. It
contains a check box that lets you set whether the glass pane is “visible”—
whether it can get events and paint itself onscreen. When the glass pane is
visible, it blocks all input events from reaching the components in the
content pane. It also paints a red dot in the place where it last detected a
mouse-pressed event.
The following code from GlassPaneDemo.java shows and hides the glass
pane. This program happens to create its own glass pane, setting it using the
JFrame setGlassPane method. However, if a glass pane doesn’t do any
painting, the program might simply attach listeners to the default glass pane,
as returned by getGlassPane.
...//where GlassPaneDemo’s UI is initialized:
JCheckBox changeButton =
new JCheckBox(“Glass pane \”visible\””);
changeButton.setSelected(false);
changeButton.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
myGlassPane.setVisible(e.getStateChange()
== ItemEvent.SELECTED);
}
});
The next code snippet implements the mouse-event handling for the glass
pane. If a mouse event occurs over the check box or menu bar, then the glass
pane redispatches the event so that the check box or menu component
receives it. So that the check box and menu behave properly, they also
receive all drag events that started with a press in the button or menu bar.
...//In the implementation of the glass pane’s mouse
listener:
public void mouseMoved(MouseEvent e) {
redispatchMouseEvent(e, false);
}
58
glassPane,
glassPanePoint,
contentPane);
int eventID = e.getID();
if (containerPoint.y < 0) {
inMenuBar = true;
//...set container and containerPoint accordingly...
testForDrag(eventID);
}
component = SwingUtilities.getDeepestComponentAt(
container,
containerPoint.x,
containerPoint.y);
if (component.equals(liveButton)) {
inButton = true;
testForDrag(eventID);
}
if (repaint) {
toolkit.beep();
glassPane.setPoint(glassPanePoint);
glassPane.repaint();
}
}
59
The table below describes the intended use for each layer and the named constant defined in the
JLayeredPane class that corresponds to it:
From... To...
layeredPane = new
layeredPane = getLayeredPane();
JLayeredPane();
final int YFUDGE = 57; final int YFUDGE = 27;
Point origin = new Point(10,
Point origin = new Point(10, 70);
20);
contentPane.add(layeredPane); //contentPane.add(layeredPane);
frame.setSize(new Dimension(350,
frame.pack();
400));
60
Because the program now uses the root pane’s layered pane, the Duke label
can be dragged all around inside the window and over the components in the
control panel. What do you suppose happens when you bring up the combo
box? Will Duke be under the combo box’s menu or over it?
Notice that the layered pane’s mouse listener is not notified of events when
the mouse is over the combo box or the checkbox.
61
the root pane (if any) that contains the
component.
Invokes the SwingUtilities
JRootPane getRootPane()
getRootPane method on the
(in JComponent)
JComponent.
Set or get which button (if any) is the
void setDefaultButton(JButton) default button in the root pane. A look-and-
JButton getDefaultButton() feel-specific action, such as pressing Enter,
causes the button’s action to be performed.
Setting or Getting the Glass Pane
Method Purpose
setGlassPane(Component)
Component getGlassPane() Sets or gets the glass
(in JApplet, JDialog, JFrame, JInternalFrame, JRootPane, and pane.
JWindow)
Setting or Getting the Content Pane
Method Purpose
setContentPane(Container)
Container getContentPane() Set or get the content
(in JApplet, JDialog, JFrame, JInternalFrame, JRootPane, and pane.
JWindow)
62
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
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.
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
63
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);
b1.setActionCommand(“disable”);
64
}
}
Performance Note: Because the HTML rendering support needs many classes, users on older systems
might notice a delay the first time a component with HTML text is shown. One way to avoid the possible
delay is not to show the HTML-containing component immediately, and to create it (or another component
that uses HTML) on a background thread.
ButtonDemo2 adds font, color, and other text formatting to the buttons
you’ve already seen in ButtonDemo. Here is a picture of ButtonDemo2:
The new demo has multiple lines of text and other HTML formatting on the left and right buttons. You can
run ButtonDemo2 using Java Web Start .
Because the left and right buttons have multiple lines, not to mention
multiple text styles, they are implemented using HTML. The middle button,
on the other hand, uses just one line, font, and color, so it doesn’t require
HTML. Here is the code that specifies the text formatting for these three
buttons:
b1 = new
JButton(“<html><center><b><u>D</u>isable</b><br>”
• “<font color=#ffffdd>middle button</font>”,
leftButtonIcon);
Font font = b1.getFont().deriveFont(Font.PLAIN);
b1.setFont(font);
...
b2 = new JButton(“middle button”,
middleButtonIcon);
b2.setFont(font);
b2.setForeground(new Color(0xffffdd));
...
65
b3 = new
JButton(“<html><center><b><u>E</u>nable</b><br>”
• “<font color=#ffffdd>middle button</font>”,
rightButtonIcon);
b3.setFont(font);
Note that we had to use a <u> tag to cause the mnemonic character to be
underlined in the HTML-using buttons. Note also that when a button is
disabled, its HTML text unfortunately remains black, instead of becoming
gray.
Version Note: HTML support in buttons was first added in J2SE versions 1.2.2 and completed in 1.3. The
button components that supported HTML as of 1.2.2 are JMenuItem, JMenu, JButton,
JRadioButtonMenuItem, and JCheckBoxMenuItem. HTML support was added in 1.3 to
JToggleButton, JCheckBox, and JRadioButton. In early releases that don’t have HTML
support, specifying HTML text in a button results in an ugly-looking button like this one:
For further discussion and an example that lets you interactively change
HTML text, see Using HTML on a Label.
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:
66
//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.
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:
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);
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);
67
...
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();
}
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.
68
//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);
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. There’s no supported API for
unselecting all the buttons.
69
• Implementing the Button’s Functionality
• Check Box Constructors
• Radio Button Constructors
• Toggle Button Constructors
• Commonly Used Button Group Constructors/Methods
Setting or Getting the Button’s Contents
Method or Constructor Purpose
JButton(Action)
JButton(String, Icon) Create a JButton instance, initializing it to have the
JButton(String) specified text/image/action. The JButton(Action)
JButton(Icon) constructor was added to JButton in 1.3.
JButton()
void setAction(Action) Set or get the button’s properties according to values from
Action getAction() the Action instance. Introduced in 1.3.
void setText(String)
String getText() Set or get the text displayed by the button.
void setIcon(Icon) Set or get the image displayed by the button when the
Icon getIcon() button isn’t selected or pressed.
Set or get the image displayed by the button when it’s
void setDisabledIcon(Icon) disabled. If you don’t specify a disabled image, then the
Icon getDisabledIcon() look and feel creates one by manipulating the default
image.
void setPressedIcon(Icon) Set or get the image displayed by the button when it’s
Icon getPressedIcon() being pressed.
void setSelectedIcon(Icon)
Icon getSelectedIcon() Set or get the image displayed by the button when it’s
void selected. If you don’t specify a disabled selected image,
setDisabledSelectedIcon(Icon) then the look and feel creates one by manipulating the
Icon selected image.
getDisabledSelectedIcon()
setRolloverEnabled(boolean) Use setRolloverIcon(someIcon) to make the
boolean isRolloverEnabled()
button display the specified icon when the cursor passes
void setRolloverIcon(Icon)
over it. The setRolloverSelectedIcon method
Icon getRolloverIcon()
void lets you specify the rollover icon when the button is
setRolloverSelectedIcon(Icon) selected — this is useful for two-state buttons such as
Icon toggle buttons. Setting the rollover icon automatically
getRolloverSelectedIcon() calls setRollover(true), enabling rollover.
70
int getHorizontalTextPosition()
LEADING, and TRAILING (the default). For vertical
int getVerticalTextPosition() position: TOP, CENTER (the default), and BOTTOM.
void setMargin(Insets) Set or get the number of pixels between the button’s
Insets getMargin() border and its contents.
void setFocusPainted(boolean) Set or get whether the button should look different
boolean isFocusPainted() when it has the focus.
void setBorderPainted(boolean) Set or get whether the border of the button should be
boolean isBorderPainted() painted.
void setIconTextGap(int) Set or get the amount of space between the text and the
int getIconTextGap() icon displayed in this button. Introduced in 1.4.
Implementing the Button’s Functionality
Method or Constructor Purpose
Set or get the keyboard alternative to clicking the
button. One form of the setMnemonic method
void setMnemonic(int)
accepts a character argument; however, the Swing
char getMnemonic()
team recommends that you use an int argument
instead, specifying a KeyEvent.VK_X constant.
Set or get a hint as to which character in the text
void
setDisplayedMnemonicIndex(int) should be decorated to represent the mnemonic.
int getDisplayedMnemonicIndex() Note that not all look and feels may support this.
Introduced in 1.4.
void setActionCommand(String) Set or get the name of the action performed by the
String getActionCommand() button.
void
addActionListener(ActionListener) Add or remove an object that listens for action
ActionListener events fired by the button.
removeActionListener()
void addItemListener(ItemListener)
Add or remove an object that listens for item
ItemListener removeItemListener() events fired by the button.
Set or get whether the button is selected. Makes
void setSelected(boolean)
boolean isSelected() sense only for buttons that have on/off state, such
as check boxes.
71
JCheckBox(String, Icon,
false, then the check box is initially unselected. The
boolean)
JCheckBox() JCheckBox(Action) constructor was introduced in 1.3.
JCheckBoxMenuItem(Action)
JCheckBoxMenuItem(String)
JCheckBoxMenuItem(String, Create a JCheckBoxMenuItem instance. The arguments are
boolean) interpreted in the same way as the arguments to the
JCheckBoxMenuItem(Icon) JCheckBox constructors, except that any specified icon is
JCheckBoxMenuItem(String, shown in addition to the normal check box icon. The
Icon) JCheckBoxMenuItem(Action) constructor was
JCheckBoxMenuItem(String, introduced in 1.3.
Icon, boolean)
JCheckBoxMenuItem()
Radio Button Constructors
Constructor Purpose
JRadioButton(Action) Create a JRadioButton instance. The string argument
JRadioButton(String) specifies the text, if any, that the radio button should
JRadioButton(String, display. Similarly, the Icon argument specifies the image
boolean) that should be used instead of the look and feel’s default
JRadioButton(Icon) radio button image. Specifying the boolean argument as
JRadioButton(Icon, boolean) true initializes the radio button to be selected, subject to
JRadioButton(String, Icon) the approval of the ButtonGroup object. If the boolean
JRadioButton(String, Icon, argument is absent or false, then the radio button is
boolean) initially unselected. The JRadioButton(Action)
JRadioButton() constructor was introduced in 1.3.
Create a JRadioButtonMenuItem instance. The
JRadioButtonMenuItem(Action)
arguments are interpreted in the same way as the
JRadioButtonMenuItem(String)
arguments to the JRadioButton constructors, except
JRadioButtonMenuItem(Icon)
JRadioButtonMenuItem(String, that any specified icon is shown in addition to the normal
Icon) radio button icon. The
JRadioButtonMenuItem() JRadioButtonMenuItem(Action) constructor was
introduced in 1.3.
Toggle Button Constructors
Constructor Purpose
JToggleButton(Action) Create a JToggleButton instance, which is similar to a
JToggleButton(String) JButton, but with two states. Normally, you use a
JToggleButton(String, JRadioButton or JCheckBox instead of directly instantiating
boolean) JToggleButton, but JToggleButton can be useful when you
JToggleButton(Icon) don’t want the typical radio button or check box appearance. The
JToggleButton(Icon, string argument specifies the text, if any, that the toggle button
boolean) should display. Similarly, the Icon argument specifies the image
JToggleButton(String, that should be used. Specifying the boolean argument as true
Icon) initializes the toggle button to be selected. If the boolean argument
JToggleButton(String, is absent or false, then the toggle button is initially unselected.
Icon, boolean) The JToggleButton(Action) constructor was introduced in
JToggleButton()
1.3.
Commonly Used Button Group Constructors/Methods
Constructor or Method Purpose
ButtonGroup() Create a ButtonGroup instance.
void Add a button to the group, or remove a button from the group.
72
add(AbstractButton)
void
remove(AbstractButton)
public ButtonGroup Get the ButtonGroup, if any, that controls a button. For example:
getGroup() ButtonGroup group =
((DefaultButtonModel)button.getModel()).getGroup();
(in DefaultButtonModel)
Introduced in 1.3.
73
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
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
74
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.
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.
75
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 .
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 .
76
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 Return. 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.
See Internationalization to learn more about formatting dates and other
types of data.
77
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.
You can run CustomComboBoxDemo using Java Web Start . The full
TM
78
/*
• 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.)
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.
79
The API for using combo boxes falls into two categories:
• Setting or Getting the Items in the Combo Box’s Menu
• Customizing the Combo Box’s Operation
Setting or Getting the Items in the Combo Boxes’s Menu
Method Purpose
Create a combo box with the specified items in its menu. A
JComboBox()
combo box created with the default constructor has no items in
JComboBox(ComboBoxModel)
the menu initially. Each of the other constructors initializes the
JComboBox(Object[])
menu from its argument: a model object, an array of objects, or
JComboBox(Vector)
a Vector of objects.
Add or insert the specified object into the combo box’s menu.
void addItem(Object) The insert method places the specified object at the specified
void insertItemAt(Object, index, thus inserting it before the object currently at that index.
int) These methods require that the combo box’s data model be an
instance of MutableComboBoxModel.
Object getItemAt(int)
Object getSelectedItem() Get an item from the combo box’s menu.
void removeAllItems() Remove one or more items from the combo box’s menu. These
void removeItemAt(int) methods require that the combo box’s data model be an instance
void removeItem(Object) of MutableComboBoxModel.
int getItemCount() Get the number of items in the combo box’s menu.
void
setModel(ComboBoxModel) Set or get the data model that provides the items in the combo
ComboBoxModel getModel() box’s menu.
Set or get the Action associated with the combo box. For
void setAction(Action)
Action getAction() further information, see How to Use Actions . Introduced in
1.3.
Customizing the Combo Box’s Operation
Method or Constructor Purpose
Add an action listener to the combo box. The
listener’s actionPerformed method is called
void
addActionListener(ActionListener) when the user selects an item from the combo
box’s menu or, in an editable combo box, when the
user presses Return.
Add an item listener to the combo box. The
listener’s itemStateChanged method is called
void addItemListener(ItemListener)
when the selection state of any of the combo box’s
items change.
void setEditable(boolean) Set or get whether the user can type in the combo
boolean isEditable() box.
Set or get the object responsible for painting the
void setRenderer(ListCellRenderer) selected item in the combo box. The renderer is
used only when the combo box is uneditable. If the
ListCellRenderer getRenderer() combo box is editable, the editor is used to paint
the selected item instead.
void setEditor(ComboBoxEditor) Set or get the object responsible for painting and
ComboBoxEditor getEditor() editing the selected item in the combo box. The
editor is used only when the combo box is editable.
If the combo box is uneditable, the renderer is used
80
to paint the selected item instead.
81
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
This section uses the preceding example as a basis for discussing the following topics:
• Working With a List’s Model
• Selecting Items in a List
• Adding Items to and Removing Items from a List
• Writing a Custom Cell Renderer
• The List API
• Examples that Use Lists
82
JScrollPane listScrollPane = new
JScrollPane(list);
The code passes a Vector to the list’s constructor. The vector is filled with strings that were read in from
a properties file. Each string contains the name of an image file.
Other JList constructors let you initialize a list from an array of objects or
from an object that adheres to the ListModel interface. If you initialize a
list with an array or vector, the constructor implicitly creates a default list
model. The default list model is immutable—you cannot add, remove, or
replace items in the list. To create a list whose items can be changed
individually, set the list’s model to an instance of a mutable list model class,
such as an instance of DefaultListModel . You can set a list’s model when
you create the list or by calling the setModel method. See Adding Items to
and Removing Items from a List for an example.
83
implement one method: valueChanged. Here’s the valueChanged method
for the listener in SplitPaneDemo:
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting())
return;
JList theList = (JList)e.getSource();
if (theList.isSelectionEmpty()) {
picture.setIcon(null);
} else {
int index = theList.getSelectedIndex();
ImageIcon newImage = new ImageIcon(“images/” +
(String)imageList.elementAt(index));
picture.setIcon(newImage);
picture.setPreferredSize(new Dimension(
newImage.getIconWidth(),
newImage.getIconHeight() ));
picture.revalidate();
}
}
Many list selection events can be generated from a single user action such as a mouse click. The
getValueIsAdjusting method returns true if the user is still manipulating the selection. This
particular program is interested only in the final result of the user’s action, so the valueChanged
method updates the image only if getValueIsAdjusting returns false.
Because the list is in single selection mode, this code can use
getSelectedIndex to get the index of the just-selected item. JList
provides other methods for setting or getting the selection when the selection
mode allows more than one item to be selected. If you want, you can listen
for events on the list’s list selection model rather than on the list itself.
Examples of Handling List Selection Events provides an example that shows
how to listen for list selection events on the list selection model and lets you
change the selection mode of a list dynamically
Here’s the code from ListDemo that creates a mutable list model object, puts the initial items in it, and
uses the list model to create a list:
84
listModel = new DefaultListModel();
listModel.addElement(“Alison Huml”);
listModel.addElement(“Kathy Walrath”);
listModel.addElement(“Lisa Friendly”);
listModel.addElement(“Mary Campione”);
list = new JList(listModel);
This particular program uses an instance of DefaultListModel, a class provided by Swing. In spite of
the name, a list does not have a DefaultListModel unless your program explicitly makes it so. If this
class doesn’t suit your needs, you can write a custom list model, which must adhere to the ListModel
interface. Here’s the actionPerformed method for the action listener registered on the Fire button:
public void actionPerformed(ActionEvent e) {
int index = list.getSelectedIndex();
listModel.remove(index);
int size = listModel.getSize();
//Nobody’s left, disable firing
if (size == 0) {
fireButton.setEnabled(false);
//Adjust the selection
} else {
//removed item in last position
if (index == listModel.getSize())
index--;
//otherwise select same index
list.setSelectedIndex(index);
}
}
The bold line of code removes the selected item in the list. The remaining lines in the method disable the
fire button if the list is now empty, and make another selection.
Here’s the actionPerformed method for the action listener shared by the
Hire button and the text field:
public void actionPerformed(ActionEvent e) {
//User didn’t type in a name...
if (employeeName.getText().equals(“”)) {
Toolkit.getDefaultToolkit().beep();
return;
}
85
This code uses the list model’s addElement method to add the new name to the end of the list if the last
item in the list is selected or if there’s no selection. Otherwise, the code calls insertElementAt to
insert the new name after the current selection.
Whenever items are added to, removed from, or modified in a list, the list
model fires list data events. Refer to How to Write a List Data Listener for
information about listening for these events. That section contains an
example that is similar to ListDemo, but adds buttons that move items up or
down in the list.
Much of the operation of a list is managed by other objects. The items in the
list are managed by a list model object, the selection is managed by a list
selection model object, and most programs put a list in a scroll pane to
handle scrolling. For the most part, you don’t need to worry about the
models because JList creates them as necessary and you interact with them
implicitly with JList’s convenience methods.
That said, the API for using lists falls into these categories:
• Setting the Items in the List
• Laying out the Items in the List
• Managing the List’s Selection
• Working with a Scroll Pane
Setting the Items in the List
Method or Constructor Purpose
JList(ListModel)
JList(Object[]) Create a list with the initial list items specified. The second and
JList(Vector) third constructors implicitly create an immutable ListModel.
JList()
void
setModel(ListModel) Set or get the model that contains the contents of the list.
ListModel getModel()
86
void
setListData(Object[]) Set the items in the list. These methods implicitly create an
void immutable ListModel.
setListData(Vector)
Laying out the Items in the List
Method Purpose
Set or get the way list cells are laid out. The possible layout
formats are VERTICAL (a single column of cells; the default),
void
HORIZONTAL_WRAP (“newspaper” style with the content
setLayoutOrientation(int)
int getLayoutOrientation() flowing horizontally then vertically), or VERTICAL_WRAP
(“newspaper” style with the content flowing vertically then
horizontally). Introduced in 1.4.
Managing the List’s Selection
Method Purpose
void Register to receive notification of
addListSelectionListener(ListSelectionListener) selection changes.
Set the current selection as indicated. Use
void setSelectedIndex(int) setSelectionMode to set what
void setSelectedIndices(int[]) ranges of selections are acceptable. The
void setSelectedValue(Object, boolean) boolean argument specifies whether the
void setSelectionInterval(int, int) list should attempt to scroll itself so that
the selected item is visible.
int getSelectedIndex()
int getMinSelectionIndex()
int getMaxSelectionIndex() Get information about the current
int[] getSelectedIndices() selection as indicated.
Object getSelectedValue()
Object[] getSelectedValues()
Set or get the selection mode. Acceptable
values are: SINGLE_SELECTION,
void setSelectionMode(int) SINGLE_INTERVAL_SELECTION, or
int getSelectionMode() MULTIPLE_INTERVAL_SELECTION
(the default), which are defined in
ListSelectionModel.
void clearSelection()
boolean isSelectionEmpty() Set or get whether any items are selected.
87
ensureIndexIsVisible(int) viewport that this list is in.
int getFirstVisibleIndex()
int getLastVisibleIndex() Get the index of the first or last visible item.
void setVisibleRowCount(int)
int getVisibleRowCount() Set or get how many rows of the list are visible.
88
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Menus are unique in that, by convention, they aren’t placed with the other
components in the UI. Instead, a menu usually appears either in a menu bar
or as a popup menu. A menu bar contains one or more menus and has a
customary, platform-dependent location—usually along the top of a window.
A popup menu is a menu that is invisible until the user makes a platform-
specific mouse action, such as pressing the right mouse button, over a
popup-enabled component. The popup menu then appears under the cursor.
The following figure shows the Swing components that implement each part
of the menu system.
The rest of this section teaches you about the menu components and tells
you how to use various menu features:
• The menu component hierarchy
• Creating menus
• Handling events from menu items
• Enabling keyboard operation
• Bringing up a popup menu
• Customizing menu layout
• The Menu API
• Examples that use menus
89
As the figure shows, menu items (including menus) are simply buttons. You might be wondering how a
menu, if it’s only a button, shows its menu items. The answer is that when a menu is activated, it
automatically brings up a popup menu that displays the menu items.
Creating Menus
The following code creates the menus shown near the beginning of this menu section. The bold lines of
code create and connect the menu objects; the other code sets up or customizes the menu objects. You can
find the entire program in MenuLookDemo.java . To run the program, you need to have the following
image file: images/middle.gif.
Note: Because this code has no event handling, the menus do nothing useful except look like they should.
If you run the example, you’ll notice that despite the lack of custom event handling, menus and submenus
appear when they should, and the check boxes and radio buttons respond appropriately when the user
chooses them.
90
KeyEvent.VK_1, ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDesc
ription(
“This doesn’t really do anything”);
menu.add(menuItem);
menuItem = new JMenuItem(“Both text and icon”,
new ImageIcon(“images/middle.gif”));
menuItem.setMnemonic(KeyEvent.VK_B);
menu.add(menuItem);
menuItem = new JMenuItem(new
ImageIcon(“images/middle.gif”));
menuItem.setMnemonic(KeyEvent.VK_D);
menu.add(menuItem);
//a group of radio button menu items
menu.addSeparator();
ButtonGroup group = new ButtonGroup();
rbMenuItem = new JRadioButtonMenuItem(“A radio
button menu item”);
rbMenuItem.setSelected(true);
rbMenuItem.setMnemonic(KeyEvent.VK_R);
group.add(rbMenuItem);
menu.add(rbMenuItem);
rbMenuItem = new JRadioButtonMenuItem(“Another
one”);
rbMenuItem.setMnemonic(KeyEvent.VK_O);
group.add(rbMenuItem);
menu.add(rbMenuItem);
//a group of check box menu items
menu.addSeparator();
cbMenuItem = new JCheckBoxMenuItem(“A check box
menu item”);
cbMenuItem.setMnemonic(KeyEvent.VK_C);
menu.add(cbMenuItem);
cbMenuItem = new JCheckBoxMenuItem(“Another
one”);
cbMenuItem.setMnemonic(KeyEvent.VK_H);
menu.add(cbMenuItem);
//a submenu
menu.addSeparator();
submenu = new JMenu(“A submenu”);
submenu.setMnemonic(KeyEvent.VK_S);
menuItem = new JMenuItem(“An item in the
submenu”);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_2, ActionEvent.ALT_MASK));
submenu.add(menuItem);
menuItem = new JMenuItem(“Another item”);
submenu.add(menuItem);
menu.add(submenu);
91
As the code shows, to set the menu bar for a JFrame, you use the
setJMenuBar method. To add a JMenu to a JMenuBar, you use the
add(JMenu) method. To add menu items and submenus to a JMenu, you use
the add(JMenuItem) method. Other methods in the preceding code include
setAccelerator and setMnemonic, which are discussed in Enabling
Keyboard Operation. The setAccessibleDescription method is discussed
in How to Support Assistive Technologies .
The following picture shows a program that adds event detection to the
preceding example. The program’s code is in MenuDemo.java . Like
MenuLookDemo, MenuDemo uses the images/middle.gif image file.
92
//...Get information from the action event...
//...Display it in the text area...
}
You can specify a mnemonic either when constructing the menu item or with the setMnemonic method.
To specify an accelerator, use the setAccelerator method. Here are examples of setting mnemonics
and accelerators:
//Setting the mnemonic when constructing a menu item:
menuItem = new JMenuItem(“A text-only menu item”,
KeyEvent.VK_T);
//Setting the mnemonic after creation time:
menuItem.setMnemonic(KeyEvent.VK_T);
//Setting the accelerator:
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_T, ActionEvent.ALT_MASK));
As you can see, you set a mnemonic by specifying the KeyEvent constant corresponding to the key
the user should press. To specify an accelerator you must use a KeyStroke object, which combines a
93
key (specified by a KeyEvent constant) and a modifier-key mask (specified by an ActionEvent
constant).
The mouse listener brings up the popup menu by invoking the show method
on the appropriate JPopupMenu instance. The following code, taken from
PopupMenuDemo.java , shows how to create and show popup menus:
//...where instance variables are declared:
JPopupMenu popup;
//...where the GUI is constructed:
//Create the popup menu.
popup = new JPopupMenu();
menuItem = new JMenuItem(“A popup menu item”);
menuItem.addActionListener(this);
popup.add(menuItem);
menuItem = new JMenuItem(“Another popup menu item”);
menuItem.addActionListener(this);
popup.add(menuItem);
//Add listener to components that can bring up popup
menus.
MouseListener popupListener = new PopupListener();
output.addMouseListener(popupListener);
menuBar.addMouseListener(popupListener);
...
class PopupListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
94
Lightweight popup windows are more efficient than heavyweight windows,
but they don’t work well if you have any heavyweight components inside
your GUI. Specifically, when the lightweight popup’s display area intersects
the heavyweight component’s display area, the heavyweight component is
drawn on top. This is one of the reasons we recommend against mixing
heavyweight and lightweight components. If you absolutely need to use a
heavyweight component in your GUI, then you can invoke
JPopupMenu.setLightWeightPopupEnabled(false) to disable
lightweight popup windows. For details, see Mixing Heavy and Light
Components, an article in The Swing Connection.
95
Constructor or Method Purpose
JMenuBar() Creates a menu bar.
JMenu add(JMenu) Creates a menu bar.
void setJMenuBar(JMenuBar)
JMenuBar getJMenuBar() Sets or gets the menu bar of an applet, dialog,
(in JApplet, JDialog, JFrame, frame, internal frame, or root pane.
JInternalFrame, JRootPane)
Creating and Populating Menus
Constructor or Method Purpose
Creates a menu. The string specifies the text to display for the menu.
JMenu()
The Action specifies the text and other properties of the menu (see
JMenu(String)
JMenu(Action) How to Use Actions ). The constructor taking the Action
parameter was introduced in 1.3.
Adds a menu item to the current end of the menu. If the argument is
a string, then the menu automatically creates a JMenuItem object
that displays the specified text.
96
Action using the setAction method.
void addSeparator() Adds a separator to the current end of the popup menu.
Inserts a menu item into the menu at the specified position. The first
void insert(Component,
menu item is at position 0, the second at position 1, and so on. The
int)
Component argument specifies the menu item to add.
Removes the specified item(s) from the menu. If the argument is an
void remove(int)
void removeAll() integer, then it specifies the position of the menu item to be
removed.
By default, Swing implements a menu’s window using a
lightweight component. This can cause problems if you use any
static void
heavyweight components in your Swing program, as described in
setLightWeightPopupEnabled
Bringing Up a Popup Menu. (This is one of several reasons to avoid
(boolean)
using heavyweight components.) As a workaround, invoke
JPopupMenu.setLightWeightPopupEnabled(false).
void show(Component, int, Display the popup menu at the specified x,y position (specified in
int) that order by the integer arguments) in the coordinate system of the
specified component.
Implementing Menu Items
Constructor or Method Purpose
Creates an ordinary menu item. The icon
argument, if present, specifies the icon that the
menu item should display. Similarly, the string
argument specifies the text that the menu item
should display. The integer argument specifies the
keyboard mnemonic to use. You can specify any of
JMenuItem() the relevant VK constants defined in the
JMenuItem(String) KeyEvent class. For example, to specify the A
JMenuItem(Icon)
key, use KeyEvent.VK_A.
JMenuItem(String, Icon)
JMenuItem(String, int) The constructor with the Action
JMenuItem(Action)
parameter, which was introduced in 1.3,
sets the menu item’s Action, causing the
menu item’s properties to be initialized
from the Action. See How to Use Actions
for details.
JCheckBoxMenuItem()
Creates a menu item that looks and acts like a
JCheckBoxMenuItem(String)
check box. The string argument, if any, specifies
JCheckBoxMenuItem(Icon)
the text that the menu item should display. If you
JCheckBoxMenuItem(String, Icon)
specify true for the boolean argument, then the
JCheckBoxMenuItem(String, boolean)
JCheckBoxMenuItem(String, Icon, menu item is initially selected (checked).
boolean) Otherwise, the menu item is initially unselected.
JRadioButtonMenuItem() Creates a menu item that looks and acts like a
JRadioButtonMenuItem(String) radio button. The string argument, if any, specifies
JRadioButtonMenuItem(Icon) the text that the menu item should display. If you
JRadioButtonMenuItem(String, Icon) specify true for the boolean argument, then the
JRadioButtonMenuItem(String, menu item is initially selected. Otherwise, the
boolean) menu item is initially unselected.
JRadioButtonMenuItem(Icon,
97
boolean)
JRadioButtonMenuItem(String, Icon,
boolean)
void setState(boolean)
boolean getState() Set or get the selection state of a check box menu
item.
(in JCheckBoxMenuItem)
void setActionCommand(String) Set the name of the action performed by the menu
item.
void
addActionListener(ActionListener) Add an event listener to the menu item. See
void addItemListener(ItemListener) Handling Events from Menu Items for details.
Set the Action associated with the menu item.
void setAction(Action)
See How to Use Actions for details.
Many of the preceding methods are inherited from AbstractButton. See The Button API for
information about other useful methods that AbstractButton provides.
98
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Below is the code from SliderDemo.java that creates the slider in the previous example.
JSlider framesPerSecond = new
JSlider(JSlider.HORIZONTAL,
0, 30, FPS_INIT);
framesPerSecond.addChangeListener(new
SliderListener());
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setMinorTickSpacing(1);
framesPerSecond.setPaintTicks(true);
framesPerSecond.setPaintLabels(true);
framesPerSecond.setBorder(BorderFactory.createEmp
tyBorder(0,0,10,0));
. . .
//add the slider to the content pane
contentPane.add(framesPerSecond);
By default, spacing for major and minor tick marks is zero. To see tick marks, you must explicitly set the
spacing for either major or minor tick marks (or both) to a non-zero value and call
setPaintTicks(true). Just calling setPaintTicks(true) is not enough. To display standard,
numeric labels at major tick mark locations, set the major tick spacing, then call
setPaintLabels(true). The example program provides labels for its slider this way. But you don’t
have to settle for these labels. Customizing Labels on a Slider shows you how to customize slider labels.
99
When you move the slider’s knob, the stateChanged method of the slider’s
ChangeListener is called. For information about change listeners, refer to
How to Write a Change Listener. Here is the code for this example’s change
listener:
class SliderListener implements ChangeListener {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider)e.getSource();
if (!source.getValueIsAdjusting()) {
int fps = (int)source.getValue();
if (fps == 0) {
if (!frozen) stopAnimation();
} else {
delay = 1000 / fps;
timer.setDelay(delay);
timer.setInitialDelay(delay * 10);
if (frozen) startAnimation();
}
}
}
}
Notice that our stateChanged method changes the animation speed only if
getValueIsAdjusting returns false. Many change events are fired as the user moves the slider
knob. This program is interested only in the final result of the user’s action.
You can find the source for this program in SliderDemo2.java . As with the other example, you also
need the image files that compose the animation. See the examples index for links to all the files required
by this example.
The following code creates the slider and customizes its labels:
//Create the slider
JSlider framesPerSecond = new
JSlider(JSlider.VERTICAL,
0, 30, FPS_INIT);
framesPerSecond.addChangeListener(new
SliderListener());
100
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setPaintTicks(true);
//Create the label table
Hashtable labelTable = new Hashtable();
labelTable.put( new Integer( 0 ), new
JLabel(“Stop”) );
labelTable.put( new Integer( 3 ), new
JLabel(“Slow”) );
labelTable.put( new Integer( 30 ), new
JLabel(“Fast”) );
framesPerSecond.setLabelTable( labelTable );
framesPerSecond.setPaintLabels(true);
framesPerSecond.setBorder(BorderFactory.createEmp
tyBorder(0,0,0,10));
Each key-value pair in a Hashtable specifies the position and the value of one label. The hashtable key
must be an Integer and is a value within the slider’s range at which to place the label. The hashtable
value must be a Component. This program uses JLabel instances with text only. An interesting
variation would be to use JLabel instances with icons, or perhaps buttons that move the knob to the
label’s position.
If you want a set of numeric labels positioned at a specific interval, you can
use JSlider’s createStandardLabels method to create the Hashtable for
you. You can also modify the table returned by createStandardLabels to
then customize it.
101
void setValue(int) Set or get the slider’s current value. This method also
int getValue() positions the slider’s knob.
void setOrientation(int) Set or get the orientation of the slider. Possible values are
int getOrientation() JSlider.HORIZONTAL or JSlider.VERTICAL.
void setInverted(boolean) Set or get whether the maximum is shown at the left of a
boolean getInverted() horizontal slider or at the bottom of a vertical one, thereby
inverting the slider’s range.
void setMinimum(int)
void getMinimum() Set or get the minimum or maximum values of the slider.
void setMaximum(int) Together, these methods set or get the slider’s range.
void getMaximum()
void
setMajorTickSpacing(int)
Set or get the range between major and minor ticks. You
int getMajorTickSpacing()
must call setPaintTicks(true) for the tick marks to
void
setMinorTickSpacing(int) appear.
int getMinorTickSpacing()
void setPaintTicks(boolean)
boolean getPaintTicks() Set or get whether tick marks are painted on the slider.
Set or get whether labels are painted on the slider. You can
void
provide custom labels with setLabelTable or get
setPaintLabels(boolean)
boolean getPaintLabels() automatic labels by setting the major tick spacing to a non-
zero value.
Set or get the labels for the slider. You must call
void
setPaintLabels(true) for the labels to appear.
setLabelTable(Dictionary)
Dictionary getLabelTable() createStandardLabels is a convenience method for
creating a standard set of labels.
Hashtable
Create a standard set of numeric labels. The first int
createStandardLabels(int)
Hashtable argument specifies the increment, the second int argument
createStandardLabels(int, specifies the starting point. When left unspecified, the
int) slider’s minimum is used as the starting point.
102
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Here are some examples of spinners from various look and feels:
JSpinner A single-line input field that allows the user to select a number or
object value from an ordered sequence.
SpinnerModel The interface implemented by all spinner models.
AbstractSpinnerModel The usual superclass for spinner model implementations.
A subclass of AbstractSpinnerModel whose values are defined
SpinnerListModel
by an array or a List.
A subclass of AbstractSpinnerModel that supports sequences of
SpinnerDateModel
Dates.
A subclass of AbstractSpinnerModel that supports sequences of
SpinnerNumberModel
numbers.
Useful JSpinner Methods
Method or Constructor Purpose
Create a new JSpinner. The no-argument constructor
creates a spinner with an integer SpinnerNumberModel
JSpinner()
JSpinner(SpinnerModel) with an initial value of 0 and no minimum or maximum
limits. The optional parameter on the second constructor
allows you to specify your own SpinnerModel.
void Set or get the currently displayed element of the sequence.
setValue(java.lang.Object)
103
Object getValue()
Object getNextValue() Get the object in the sequence that comes after or before the
Object getPreviousValue() object returned by getValue.
SpinnerListModel Methods
Method Purpose
void setList(List)
Set or get the List that defines the sequence for this model.
List getList()
SpinnerDateModel Methods
Method Purpose
void setValue(Object)
Date getDate() Set or get the current Date for this sequence.
Object getValue()
void
setStart(Comparable) Set or get the first Date in this sequence.
Comparable getStart()
void
setEnd(Comparable) Set or get the last Date in this sequence.
Comparable getEnd()
Set or get the size of the date value increment used by the
getNextValue and getPreviousValue methods. The
void specified parameter must be one of the following constants, defined
setCalendarField(int) in Calendar: ERA, YEAR, MONTH, WEEK_OF_YEAR,
int getCalendarField() WEEK_OF_MONTH, DAY_OF_MONTH, DAY_OF_YEAR,
DAY_OF_WEEK, DAY_OF_WEEK_IN_MONTH, AM_PM,
HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND.
SpinnerNumberModel Methods
Method Purpose
void setValue(Object)
Number getNumber() Set or get the current value for this sequence.
void
Set or get the upper bound for numbers in this sequence. If the
setMaximum(Comparable)
maximum is null, there is no upper bound.
Comparable getMaximum()
void
Set or get the lower bound for numbers in this sequence. If the
setMinimum(Comparable)
minimum is null, there is no lower bound.
Comparable getMinimum()
void setStepSize(Number) Set or get the increment used by getNextValue and
Number getStepSize() getPreviousValue methods.
A text field is a basic text control that lets the user enter a small amount of
text. When the user indicates that text entry is complete (usually by pressing
104
Return), the text field fires an action event . Generally you use the
JTextField class to provide text fields. If you need to provide a password
field—an editable text field that doesn’t show the characters the user types—
use the JPasswordField class instead. This section discusses both text
fields and password fields.
If you want a text field that also provides a menu of strings to choose from,
consider using an editable combo box. If you need to obtain more than one
line of input from the user, then you should use one of the classes that
implements a general-purpose text area.
The applet below displays a basic text field and a text area. The text field is
editable; the text area isn’t. When the user presses Return in the text field,
the applet copies the text field’s contents to the text area, and then selects all
the text in the text field.
This is a picture of the applet’s GUI. To run the applet, click the picture. The applet will appear in a new
browser window.
You can find the source for the program in TextDemo.java . Here’s the
code from TextDemo that creates the text field in the applet:
textField = new JTextField(20);
textField.addActionListener(this);
...
contentPane.add(textField);
The integer argument passed to the JTextField constructor, 20 in the example, indicates the number of
columns in the field. This number is used along with metrics provided by the field’s current font to
calculate the field’s preferred width. It does not limit the number of characters the user can enter. To do
that, you need to implement a custom document, as described in General Rules for Using Text
Components.
The next lines of code register the applet as an action listener for the text
field and add the text field to the applet’s content pane. Here’s the
actionPerformed method that handles action events from the text field:
public void actionPerformed(ActionEvent evt) {
String text = textField.getText();
textArea.append(text + newline);
textField.selectAll();
}
105
Notice the use of JTextField’s getText method to retrieve the text currently contained by the text
field. The text returned by this method does not include a newline character for the Return key that fired
the action event.
This example illustrates using a basic, off-the-shelf text field for entering
textual data and performing a task when the text field fires an action event.
This is sufficient for many programs. Other programs, however, need more
advanced behavior. As a subclass of JTextComponent , JTextField can be
configured and customized. One common customization is to provide a text
field whose contents are validated.
This section covers the following advanced text field topics. To get most out
of the information, you need to understand the material presented in the
previous section, General Rules for Using Text Components.
• Creating a Validated Text Field
• Using a Document Listener on a Text Field
• Laying Out Label-Text Field Pairs
• Providing a Password Field
• The Text Field and Password Field API
• Examples that Use Text Fields and Password Fields
Version Note: This section uses the 1.2 API. If you’re using 1.4, you should instead read How to Use
Formatted Text Fields . If you’re using 1.3, you might want to use the input verification API instead of
or in addition to the techniques described in the following paragraphs. 1.4 allows more complete control
over focus, thanks to its redesigned focus subsystem .
Many programs require users to enter textual data of a certain type or format. For example, a program
might provide a text field for entering a date, a decimal number, or a phone number. The contents of such
a text field must be validated before being used for any purpose. A text field can be action-validated or
change-validated.
The data in an action-validated field is checked each time the field fires an
action event (each time the user presses the Return key). An action-validated
field might, at any given point in time, contain invalid data. However, the
data is validated before it’s used for anything. To create an action-validated
field, provide an action listener for your field and implement its
actionPerformed method as follows:
• Use getText to get the contents of the text field, or getPassword if you’re using a password
field.
• Evaluate the value returned.
• If the value is valid, do whatever task or calculation is required. If the value is invalid, report an error
and return without performing a task or calculation.
PasswordDemo, described later in this section, action-validates a password field in this manner.
The data in a change-validated field is checked each time the field changes.
A field that is change-validated can never contain invalid data because every
change (keystroke, cut, copy, and so on) that might cause the data to be
106
invalid is rejected. To create a change-validated text field you need to
provide a custom document for your text field. If you aren’t familiar with
documents yet, see Concepts: About Documents.
Warning: Do not use a document listener for change validation. By the time a document listener has been
notified of a change, it’s too late, the change has already taken place. See the last couple of paragraphs in
Listening for Changes on a Document for more information.
The application shown in the following figure has three change-validated text fields. The user enters loan
information into the first three text fields. Each time the user types a character, the program validates the
input and updates the result in the fourth text field.
107
insertString evaluates each character to be inserted into the text field. If the character is a digit, the
document allows it to be inserted. Otherwise, the method beeps and prints an error message. Thus
WholeNumberDocument allows the numbers in the range 0, 1, 2, ...
108
In addition to overriding insertString, FormattedDocument also
overrides the remove method. Recall that the remove method is called each
time a character or group of characters is to be removed from the document.
public void remove(int offs, int len)
throws BadLocationException
{
String currentText = getText(0, getLength());
String beforeOffset = currentText.substring(0, offs);
String afterOffset = currentText.substring(len + offs,
currentText.length());
String proposedResult = beforeOffset + afterOffset;
try {
if (proposedResult.length() != 0)
format.parseObject(proposedResult);
super.remove(offs, len);
} catch (ParseException e) {
Toolkit.getDefaultToolkit().beep();
System.err.println(“remove: could not parse: “
• proposedResult);
}
}
The FormattedDocument implementation of the remove method is similar to its implementation of
the insertString method. The format parses the result of the proposed change and performs the
removal or not, depending on whether the result is valid.
Note: The solution provided by this example is not a general solution for all types of formats. Some
formats—most notably DateFormat—can’t be change-validated simply by calling the parseObject
method. Here’s an example to help you understand why. Suppose you have a text field that contains the
date “May 25, 1996” and want to change it to “June 11, 1996”. You would select May and begin typing
“June”. As soon as you’ve typed the “J”, the field won’t parse because “J 25, 1996” is not a valid date
even though it’s a valid change. A number of solutions are possible for dates and other types of data where
a partially completed change creates an invalid result. You can change the change-validation such that it
rejects all definitely invalid changes (typing “X” into a date for example) but allows all possibly valid
changes. Or you can switch to an action-validated field.
109
}
This is an appropriate use of a document listener. For general information about document listeners, see
Listening for Changes on a Document and How to Write a Document Listener .
Rows of label and text field pairs such as those found in the loan calculator
are quite common on preference panels and panels that implement forms.
Here’s the code that lays out the label and text field pairs.
. . .
//Layout the labels in a panel
JPanel labelPane = new JPanel();
labelPane.setLayout(new GridLayout(0, 1));
labelPane.add(amountLabel);
labelPane.add(rateLabel);
labelPane.add(numPeriodsLabel);
labelPane.add(paymentLabel);
As the diagram illustrates, the program uses two GridLayout managers, one to lay out the column of
labels and one for the column of text fields. GridLayout guarantees that all of its components are the
same size, so all of the text fields are the same height and all of the labels are the same height. But the text
fields are not the same height as the labels. This is achieved with a third layout manager, a
BorderLayout. With just two components at East and Center, BorderLayout guarantees the
110
columns are the same height. Now the labels and text fields are the same height, and thus, they are
aligned.
Another way to get labels and text fields to align is to use the AWT’s most
flexible, complex layout manager: GridBagLayout . Refer to the
TextSamplerDemo program for an example. In particular, look at this handy
method, which you can probably copy verbatim into your programs:
addLabelTextRows(JLabel[] labels,
JTextField[] textFields,
GridBagLayout gridbag,
Container container)
Here’s the code from PasswordDemo that creates and sets up the password field.
JPasswordField passwordField = new
JPasswordField(10);
passwordField.setEchoChar(‘#’);
passwordField.addActionListener(new
ActionListener() {
...
});
As with text fields, the argument passed into the JPasswordField constructor indicates that the field
should be 10 columns wide. By default a password field displays an asterisk ‘*’ for each character typed.
The call to setEchoChar changes it to a pound sign ‘#’. Finally, the code adds an action listener to the
password field, which action-validates the value typed in by the user. Here’s the implementation of the
action listener’s actionPerformed method:
public void actionPerformed(ActionEvent e) {
JPasswordField input = (JPasswordField)e.getSource();
char[] password = input.getPassword();
if (isPasswordCorrect(password)) {
JOptionPane.showMessageDialog(f,
“Success! You typed the right password.”);
} else {
JOptionPane.showMessageDialog(f,
“Invalid password. Try again.”,
“Error Message”,
JOptionPane.ERROR_MESSAGE);
}
}
111
The getPassword returns a character array. Password information should not be stored or passed
around in strings because strings are not secure.
Note: The getPassword method did not exist in Swing 1.0.3 and earlier releases. In those releases, you
have to use getText. In that case, your program should convert the value returned from getText to a
character array upon return. You should not store the value in a String. In releases that contain the
getPassword method, getText has been deprecated.
You might also invoke methods on a text field or password field that it
inherits from its other ancestors, such as setPreferredSize,
setForeground, setBackground, setFont, and so on. See The
JComponent Class for tables of commonly used inherited methods.
The API for using text fields and password fields falls into these categories:
• Setting or Getting the Field’s Contents
• Fine Tuning the Field’s Appearance
• Implementing the Field’s Functionality
Setting or Getting the Field’s Contents
Method or Constructor Purpose
JTextField()
JTextField(String) Create a text field. When present, the int argument
JTextField(String, int) specifies the desired width in columns. The String
JTextField(int) argument contains the field’s initial text. The Document
JTextField(Document, argument provides a custom document for the field.
String, int)
JPasswordField()
JPasswordField(String) Create a password field. When present, the int argument
JPasswordField(String, int) specifies the desired width in columns. The String
JPasswordField(int) argument contains the field’s initial text. The Document
JPasswordField(Document, argument provides a custom document for the field.
String, int)
Set or get the text displayed by the text field. Note that
void setText(String)
getText is deprecated for password fields in Swing 1.0.3
String getText()
and higher releases.
char[] getPassword() Set or get the text displayed by the text field. Note that this
(in JPasswordField) method does not exist in Swing 1.0.3 and lower releases.
Fine Tuning the Field’s Appearance
Method Purpose
112
void setEditable(boolean) Set or get whether the user can edit the text in the text
boolean isEditable() field.
void setColumns(int); Set or get the number of columns displayed by the text
int getColumns() field. This is really just a hint for computing the field’s
preferred width.
int getColumnWidth() Get the width of the text field’s columns. This value is
established implicitly by the font.
Set or get how the text is aligned horizontally within its
void
area. You can use JTextField.LEFT,
setHorizontalAlignment(int);
int getHorizontalAlignment() JTextField.CENTER, and JTextField.RIGHT
for arguments.
void setEchoChar(char)
char getEchoChar() Set or get the echo character—the character displayed
instead of the actual characters typed by the user.
(in JPasswordField)
Implementing the Field’s Functionality
Method Purpose
void addActionListener(ActionListener)
void Add or remove an action listener.
removeActionListener(ActionListener)
113
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Version Note: Before 1.4, text field formatting required more effort. See Creating a Validated Text Field
for a full example.
[PENDING: One day, this page will discuss formatted text fields. For now, it
only has links to API. You can find more information about using the API in
Formatted Text Fields , which is part of the J2SE v 1.4 release notes.]
The following tables list some of the commonly used API for using
formatted text fields.
• Classes Related to Formatted Text Fields
• JFormattedTextField Methods
• DefaultFormatter Options
Classes Related to Formatted Text Fields
Class or Interface Purpose
Subclass of JTextField that supports formatting arbitrary
JFormattedTextField
values.
The superclass of all formatters for JFormattedTextField.
JFormattedTextField. A formatter enforces editing policies and navigation policies,
AbstractFormatter handles string-to-object conversions, and manipulates the
JFormattedTextField as necessary to enforce the desired
policy.
The superclass of all formatter factories. Each
JFormattedTextField.
JFormattedTextField uses a formatter factory to obtain the
AbstractFormatterFactory
formatter that best corresponds to the text field’s state.
DefaultFormatterFactory The formatter factory normally used. Dishes out formatters based
on details such as the passed-in parameters and focus state.
Subclass of
DefaultFormatter JFormattedTextField.AbstractFormatter that
formats arbitrary objects using the toString method.
Subclass of DefaultFormatter that formats and edits strings
MaskFormatter using a specified character mask. (For example, 7-digit phone
numbers can be specified using “###-####”.)
Subclass of DefaultFormatter that uses an instance of
InternationalFormatter java.text.Format to handle conversion to and from a
String.
Subclass of InternationalFormatter that supports
NumberFormatter
number formats using an instance of NumberFormat.
Subclass of InternationalFormatter that supports date
DateFormatter
formats using an instance of DateFormat.
JFormattedTextField Methods
114
Method or Constructor Purpose
JFormattedTextField()
JFormattedTextField(Object)
JFormattedTextField(Format)
JFormattedTextField(JFormattedTextField.
AbstractFormatter) Create a new formatted text field.
JFormattedTextField(JFormattedTextField.
AbstractFormatterFactory)
JFormattedTextField(JFormattedTextField.
AbstractFormatterFactory, Object)
Set or get the value of the formatted text
void setValue(Object) field. You must cast the return type based
Object getValue() on how the JFormattedTextField
has been configured.
DefaultFormatter Options
Method Purpose
void Set or get when edits are pushed back to the
setCommitsOnValidEdit(boolean) JFormattedTextField If true, commitEdit is
boolean getCommitsOnValidEdit() invoked after every valid edit.
Set or get the the behavior when inserting characters. If
void setOverwriteMode(boolean)
true (the default), new characters overwrite existing
boolean getOverwriteMode()
characters in the model as they are inserted.
Set or get whether the value being edited is allowed to
be invalid for a length of time. It is often convenient to
void setAllowsInvalid(boolean)
allow the user to enter invalid values until a commit is
boolean getAllowsInvalid()
attempted. If true, commitEdit is invoked after
every valid edit.
115
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Below is the code from LabelDemo.java that creates the labels in the
previous example.
ImageIcon icon = new
ImageIcon(“images/middle.gif”);
. . .
label1 = new JLabel(“Image and Text”,
icon,
JLabel.CENTER);
//Set the position of the text, relative to the
icon:
label1.setVerticalTextPosition(JLabel.BOTTOM);
label1.setHorizontalTextPosition(JLabel.CENTER);
label2 = new JLabel(“Text-Only Label”);
label3 = new JLabel(icon);
//Add labels to the JPanel.
add(label1);
add(label2);
add(label3);
Note that label alignment is different from X and Y alignment. X and Y alignment are used by layout
managers and can affect the way any component—not just a label—is sized or positioned. Label
alignment, on the other hand, has no effect on a label’s size or position. It simply determines where, inside
the label’s painting area, the label’s contents are positioned. In the usual case, the label’s painting area is
exactly the size needed to paint the label, and thus label alignment is irrelevant. For more information
about X and Y alignment, see How to Use BoxLayout .
Often, a label describes another component. When this is true, you can
improve your program’s accessibility by using the setLabelFor method to
identify the component the label describes. For example:
116
amountLabel.setLabelFor(amountField);
The preceding code, taken from the TextFieldDemo example discussed in How to Use Text Fields, lets
assistive technologies know that the label (amountLabel) provides information about the text field
(amountField). For more information about assistive technologies, see How to Support Assistive
Technologies .
Note: The information in this section documents a feature available only in Swing 1.1.1 Beta 1 or
compatible releases.
Have you ever wanted to put multiple lines on a label? Have you ever wanted to make part of a label bold
or italic? Now you can. As of Swing 1.1.1 Beta 1, JLabel supports multiple lines, multiple fonts, and a
whole lot more because you can specify a label’s text using HTML.
The action listener for the button executes this single line of code:
theLabel.setText(htmlTextArea.getText());
If the string in the text area on the left begins with <html>, then the label parses it as HTML. Otherwise,
the label assumes it’s straight text.
Because this is an early release of this feature, there are some gotchas to be
aware of:
• Don’t use <br> in your HTML. The label’s preferred size won’t be computed correctly (it will be
computed as if the <br> weren’t there). Use a regular paragraph tag (<p>) instead .
• If you specify an invalid HTML tag, an exception is thrown deep in the text package. Fix your
HTML and try again.
As of Swing 1.1.1 Beta 1, you can use HTML to set the text for labels and buttons. In the near future,
many more Swing components—tool tips, tabbed panes, and menu items, for example—will support
HTML.
117
Warning: There is no programmatic way to test whether a component supports HTML text. Do not
specify HTML text unless you are absolutely sure that your program is running in a release that supports
HTML text in the desired component.
void setIcon(Icon)
Icon getIcon() Set or get the image displayed by the label.
118
TRAILING. For vertical alignment: TOP, CENTER (the
default), and BOTTOM.
void Set or get where the button’s text should be placed,
setHorizontalTextPosition(int) relative to the button’s image. The SwingConstants
void
interface defines three possible values for horizontal
setVerticalTextPosition(int)
position: LEFT, CENTER, and RIGHT (the default). For
int getHorizontalTextPosition()
vertical position: TOP, CENTER (the default), and
int getVerticalTextPosition() BOTTOM.
void setIconTextGap(int) Set or get the number of pixels between the label’s text
int getIconTextGap() and its image.
Supporting Accessibility
Method Purpose
void setLabelFor(Component)
Component getLabelFor() Set or get which component the label describes.
119
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
ProgressMonitor
Not a visible component. Instead, an instance of this class monitors the progress of a task and pops up a
dialog if necessary. See How to Use Progress Monitors for details and an example of using a progress
monitor.
ProgressMonitorInputStream
An input stream with an attached progress monitor, which monitors reading from the stream. You use an
instance of this stream like any of the other input streams described in I/O: Reading and Writing (but no
‘rithmetic) . You can get the stream’s progress monitor with a call to getProgressMonitor and
configure it as described in How to Use Progress Monitors.
After you see a progress bar and a progress monitor in action, Deciding Whether to Use a Progress Bar or
a Progress Monitor can help you figure out which is appropriate for your application.
120
Below is the code from ProgressBarDemo.java that creates and sets up the progress bar:
//Where member variables are declared:
JProgressBar progressBar;
//...in the constructor for the demo’s frame:
progressBar = new JProgressBar(0,
task.getLengthOfTask());
progressBar.setValue(0);
progressBar.setStringPainted(true);
The constructor that creates the progress bar sets the progress bar’s minimum and maximum values. You
can also set these values with setMinimum and setMaximum. The minimum and maximum values
used in this program are 0 and the length of the task, which is typical of many programs and tasks.
However, a progress bar’s minimum and maximum values can be any value, even negative. The code
snippet also sets the progress bar’s current value to 0.
The call to setStringPainted causes the progress bar to display, within its
bounds, a textual indication of the percentage of the task that has completed.
By default, the progress bar displays the value returned by its
getPercentComplete method formatted as a percent, such as 33%.
Alternatively, you can replace the default with a different string by calling
setString. For example,
if (/*...half way done...*/)
progressBar.setString(“Half way there!”);
You start this example’s task by clicking the Start button. Once the task has
begun, a timer (an instance of the Timer class) fires an action event every
second. Here’s the ActionPerformed method of the timer’s action listener:
public void actionPerformed(ActionEvent evt) {
progressBar.setValue(task.getCurrent());
taskOutput.append(task.getMessage() + newline);
taskOutput.setCaretPosition(taskOutput.getDocumen
t().getLength());
if (task.done()) {
Toolkit.getDefaultToolkit().beep();
timer.stop();
startButton.setEnabled(true);
progressBar.setValue(progressBar.getMinimum());
}
}
121
The bold line of code gets the amount of work completed by the task and updates the progress bar with
that value. So this example’s progress bar measures the progress made by the task each second, not the
elapsed time. The rest of the code appends a message to the output log (a text area named taskOutput)
and, if the task is done, turns the timer off and resets the other controls.
[PENDING: We need to discuss and give an example that shows how to use
indeterminate progress bars. At its most basic, you use
pb.setIndeterminate(true) to make a progress indeterminate, and then
pb.setIndeterminate(false) to set it back to its default state.]
A progress monitor cannot be used again, so a new one must be created each time a new task is started.
This program creates a progress monitor each time the user starts a new task with the Start button.
122
The example updates the note each time the timer fires an action event. It updates the monitor’s
current value at the same time:
• progressMonitor.setNote(task.getMessage());
• progressMonitor.setProgress(task.getCurrent
());
• The last two arguments provide the minimum and maximum values, respectively, for the progress
bar displayed in the dialog.
After the example creates the progress monitor, it configures the monitor further:
progressMonitor.setProgress(0);
progressMonitor.setMillisToDecideToPopup(2 *
ONE_SECOND);
The first line sets the current position of the progress bar on the dialog. The second tells the progress
monitor to wait two seconds before deciding whether to bring up a dialog. If, after two seconds, the
progress monitor’s progress is less than its maximum, the monitor will bring up the dialog.
By the simple fact that this example uses a progress monitor, it adds a
feature that wasn’t present in the version of the program that uses a progress
bar. The user can cancel the task by clicking the Cancel button on the
dialog. Here’s the code in the example that checks to see if the user canceled
the task or if the task exited normally:
if (progressMonitor.isCanceled() || task.done())
{
progressMonitor.close();
task.stop();
Toolkit.getDefaultToolkit().beep();
timer.stop();
startButton.setEnabled(true);
}
Note that the progress monitor doesn’t itself cancel the task. It provides the GUI and API to allow the
program to do so easily.
123
• Your task displays a short message periodically while running. The progress monitor dialog provides
the setNote method so that the task can provide further information about what it’s doing. For
example, an installation task might report the name of each file as it’s installed.
• The task might not take a long time to complete. You decide at what point a running task is taking
long enough to warrant letting the user know about it. Progress monitor won’t pop up a dialog if the
task completes within the timeframe you set.
If you decide to use a progress monitor and the task you are monitoring is reading from an input stream,
use the ProgressMonitorInputStream class.
void setMaximum(int)
int getMaximum() Set or get the maximum value of the progress bar.
Set or get the model used by the progress bar. The model
void
setModel(BoundedRangeModel) establishes the progress bar’s constraints and values. So
BoundedRangeModel getModel() you can use this method as an alternative to using the
individual set/get methods listed above.
Controlling the Progress Bar’s Appearance
124
Method Purpose
By specifying true, put the progress bar into indeterminate
void
setIndeterminate(boolean) mode. Specifying false puts the progress bar back into its
default, determinate mode. Introduced in 1.4.
Set or get whether the progress bar is vertical or horizontal.
void setOrientation(int)
Acceptable values are JProgressBar.VERTICAL or
int getOrientation()
JProgressBar.HORIZONTAL.
void
setBorderPainted(boolean) Set or get whether the progress bar has a border.
boolean isBorderPainted()
Set or get whether the progress bar displays a percent string.
void
By default, the value of the percent string is the value returned
setStringPainted(boolean)
by getPercentComplete formatted as a percent. You can
boolean isStringPainted()
set the string to be displayed with setString.
void setString(String)
String getString() Set or get the percent string.
void setMaximum(int) Set or get the maximum value of the progress monitor.
int getMaximum() This value is used by the monitor to set up the progress
bar in the dialog.
void setProgress(int) Update the monitor’s progress.
Set or get the status note. This note is displayed on the
void setNote(String) dialog. To omit the status note from the dialog, provide
String getNote() null as the third argument to the monitor’s
constructor.
void
setMillisToDecideToPopup(int) Set or get the time after which the monitor should
int getMillisToDecideToPopup() decide whether to popup a dialog.
Terminating the Progress Monitor
Method Purpose
void close() Close the progress monitor. This disposes of the dialog.
125
boolean isCanceled() Determine whether the user pressed the Cancel button.
126
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
For components such as tabbed panes that have multiple parts, it often
makes sense to vary the tool-tip text to reflect the part of the component
under the cursor. For example, a tabbed pane might use this feature to
explain what will happen when you click the tab under the cursor. When you
implement a tabbed pane, you specify the tab-specific tool-tip text in an
argument to the addTab method.
Even in components that have no API for setting part-specific tool-tip text,
you can generally do the job yourself. If the component supports renderers,
then you can set the tool tip text on a custom renderer. An alternative that
works for all JComponents is creating a subclass of the component and
overriding its getToolTipText(MouseEvent) method.
The following table lists the JComponent tool tip API. For information on
individual components’ support for tool tips, see the how-to section for the
component in question.
Tool Tip API in JComponent
Method Purpose
127
If the specified string is non-null, then this method
registers the component as having a tool tip and makes
setToolTipText(String) the tool tip (when displayed) have the specified text. If
the argument is null, then this method turns off tool tips
for this component.
Returns the string that was previously specified with
String getToolTipText()
setToolTipText.
By default, returns the same value returned by
getToolTipText(). Multi-part components such
String as JTabbedPane, JTable, and JTree override this
getToolTipText(MouseEvent) method to return a string associated with the mouse
event location. For example, each tab in a tabbed pane
can have different tool-tip text.
Get the location (in the receiving component’s
coordinate system) where the upper left corner of the
Point component’s tool tip will appear. The argument is the
getToolTipLocation(MouseEvent) event that caused the tool tip to be shown. The default
return value is null, which tells the Swing system to
choose a location.
128
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Here’s a picture of an application that uses a color chooser to set the text
color in a banner:
[PENDING: We’ll update the snapshot and add call-outs to various parts of the color chooser:
• color chooser (everything inside and including the “Choose Text Color” border)
• preview panel (everything inside and including the “Preview” border)
• chooser panel (everything inside but NOT including the tabbed pane)]
You can run ColorChooserDemo using Java Web Start . The source code
TM
The color chooser consists of everything within the box labeled Choose
Text Color. This is what a standard color chooser looks like in the Java
Look & Feel. It contains two parts, a tabbed pane and a preview panel. The
129
three tabs in the tabbed pane select chooser panels. The preview panel below
the tabbed pane displays the currently selected color.
Here’s the code from the example that creates a JColorChooser instance
and adds it to the demo’s window:
public class ColorChooserDemo extends JPanel ...
{
public ColorChooserDemo() {
banner = new JLabel(“Welcome to the Tutorial
Zone!”,
JLabel.CENTER);
banner.setForeground(Color.yellow);
. . .
tcc = new JColorChooser(banner.getForeground());
. . .
add(tcc, BorderLayout.PAGE_END);
}
. . .
frame.setContentPane(new ColorChooserDemo());
The JColorChooser constructor in the previous code snippet takes a Color argument, which
specifies the chooser’s initially selected color. If you don’t specify the initial color, then the color chooser
displays Color.white. See the Color API documentation for a list of color constants you can use.
A basic color chooser, like the one used in the example program, is sufficient
for many programs. However, the color chooser API allows you to
customize a color chooser by providing it with a preview panel of your own
design, by adding your own chooser panels to it, or by removing existing
chooser panels from the color chooser. Additionally, the JColorChooser
class provides two methods that make it easy to use a color chooser within a
dialog.
The rest of this section discusses these topics:
• Another Example: ColorChooserDemo2
• Showing a Color Chooser in a Dialog
• Removing or Replacing the Preview Panel
• Creating a Custom Chooser Panel
• The Color Chooser API
130
• Examples that Use Color Choosers
This program customizes the banner’s text color chooser in these ways:
• Removes the preview panel
• Removes all of the default chooser panels
• Adds a custom chooser panel
Removing or Replacing the Preview Panel covers the first customization. Creating a Custom Chooser
Panel discusses the last two.
This program also adds a button that brings up a color chooser in a dialog,
which you can use to set the banner’s background color.
131
ColorChooserDemo2.this,
“Choose Background Color”,
banner.getBackground());
The first argument is the parent for the dialog, the second is the dialog’s title, and the third is the initially
selected color.
The dialog disappears under three conditions: the user chooses a color and
clicks the OK button, the user cancels the operation with the Cancel button,
or the user dismisses the dialog with a frame control. If the user chooses a
color, the showDialog method returns the new color. If the user cancels the
operation or dismisses the window, the method returns null. Here’s the code
from the example that updates the banner’s background color according to
the value returned by showDialog:
if (newColor != null) {
banner.setBackground(newColor);
}
The dialog created by showDialog is modal. If you want a non-modal dialog, you can use
JColorChooser’s createDialog method to create the dialog. This method also lets you specify
action listeners for the OK and Cancel buttons in the dialog window. Use JDialog’s show method to
display the dialog created by this method. For an example that uses this method, see Specifying Other
Editors in the How to Use Tables section.
Version note: In some releases, you either could not replace the preview panel or could not remove it
properly (the titled border would remain). We believe that both problems have been fixed as of 1.4.2. If
your program depends on removing or replacing the preview panel, you should test it against all releases
that it supports.
132
If you want to remove all of the default chooser panels and add one or more
of your own, you can do this with a single call to setChooserPanels.
ColorChooserDemo2 uses this method to replace the default chooser panels
with an instance of CrayonPanel , a custom chooser panel. Here’s the call
to setChooserPanels from that example:
//Override the chooser panels with our own.
AbstractColorChooserPanel panels[] = { new
CrayonPanel() };
tcc.setChooserPanels(panels);
The code is straighforward: it creates an array containing the CrayonPanel. Next the code calls
setChooserPanels to set the contents of the array as the color chooser’s chooser panels.
Icon getSmallDisplayIcon()
Returns a small icon to represent this chooser panel. This is currently unused. Future versions of the color
chooser might use this icon or the large one to represent this chooser panel in the display. The example’s
implementation of this method returns null.
Icon getLargeDisplayIcon()
Returns a large icon to represent this chooser panel. This is currently unused. Future versions of the color
chooser might use this icon or the small one to represent this chooser panel in the display. The example’s
implementation of this method returns null.
133
The Color Chooser API
The following tables list the commonly used JColorChooser constructors and methods. Other methods
you might call are listed in the API tables in The JComponent Class. The API for using color choosers
falls into these categories:
• Creating and Displaying the Color Chooser
• Customizing the Color Chooser’s GUI
• Setting or Getting the Current Color
Creating and Displaying the Color Chooser
Method or Constructor Purpose
Create a color chooser. The default constructor
creates a color chooser with an initial color of
JColorChooser() Color.white. Use the second constructor to
JColorChooser(Color) specify a different initial color. The
JColorChooser(ColorSelectionModel) ColorSelectionModel argument, when
present, provides the color chooser with a color
selection model.
Create and show a color chooser in a modal
dialog. The Component argument is the dialog’s
Color showDialog(Component, String,
parent, the String argument specifies the
Color)
dialog’s title, and the Color argument specifies
the chooser’s initial color.
Create a dialog for the specified color chooser. As
with showDialog, the Component argument
JDialog createDialog(Component, is the dialog’s parent and the String argument
String, specifies the dialog’s title. The other arguments
boolean, JColorChooser, are as follows: the boolean specifies whether
ActionListener, the dialog is modal, the JColorChooser is the
ActionListener) color chooser to display in the dialog, the first
ActionListener is for the OK button, and
the second is for the Cancel button.
Customizing the Color Chooser’s GUI
Method Purpose
Set or get the component used
to preview the color selection.
To remove the preview panel,
void setPreviewPanel(JComponent)
use new JPanel() as an
JComponent getPreviewPanel()
argument. To specify the
default preview panel, use
null.
void
setChooserPanels(AbstractColorChooserPanel[]) Set or get the chooser panels in
AbstractColorChooserPanel[] getChooserPanels() the color chooser.
134
is false. See Drag and Drop
for more details. Introduced in
1.4.
Setting or Getting the Current Color
Method Purpose
Set or get the currently selected color. The
three integer version of the setColor
void setColor(Color) method interprets the three integers together
void setColor(int, int, int) as an RGB color. The single integer version
void setColor(int) of the setColor method divides the
Color getColor() integer into four 8-bit bytes and interprets
the integer as an RGB color as follows:
135
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Note: If you intend to distribute your program as an unsigned JavaTM Web Start application, then instead
of using the JFileChooser API you should use the file services provided by the JNLP API. These
services — FileOpenService and FileSaveService — not only provide support for choosing
files in a restricted environment, but also take care of actually opening and saving them. An example of
using these services is in JWSFileChooserDemo. Documentation for using the JNLP API is in the Java
Web Start Developer’s Guide .
The rest of this section discusses how to use the JFileChooser API. A
JFileChooser object only presents the GUI for choosing files. Your
program is responsible for doing something with the chosen file, such as
opening or saving it. Refer to I/O: Reading and Writing (but no ‘rithmetic)
for information on how to read and write files.
The JFileChooser API makes it easy to bring up open and save dialogs.
The look and feel determines what these standard dialogs look like and how
they differ. In the Java look and feel, the save dialog looks the same as the
open dialog, except for the title on the dialog’s window and the text on the
button that approves the operation. Here is a picture of the Java look and
feel’s standard open dialog:
136
Here’s a snapshot of an application that brings up an open dialog and a save
dialog.
By default, a file chooser that hasn’t been shown before displays all files in
the user’s home directory. You can specify the file chooser’s initial directory
137
using one of JFileChooser’s other constructors, or you can set the directory
with the setCurrentDirectory method.
The example gets the name of the file and uses it in the log message. You
can call other methods on the File object, such as getPath, isDirectory,
or exists to get information about the file. You can also call other methods
such as delete and rename to change the file in some way. Of course, you
might also want to open or save the file using one of the reader or writer
classes provided by the Java platform. See I/O: Reading and Writing (but no
‘rithmetic) for information about using readers and writers to read and
write data to the file system.
The example program uses the same instance of JFileChooser to display a
standard save dialog. This time the program calls showSaveDialog:
int returnVal =
fc.showSaveDialog(FileChooserDemo.this);
By using the same file chooser instance to display its open and save dialogs, the program reaps these
benefits:
• The chooser remembers the current directory between uses so the open and save versions
automatically share the same current directory.
• You have to customize only one file chooser, and the customizations apply to both the open and save
versions of it.
Finally, the example program has commented-out lines of code that let you change the file selection mode.
For example, the following line of code makes the file chooser able to select only directories, and not files:
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_
ONLY);
138
Another possible selection mode is FILES_AND_DIRECTORIES. The default is FILES_ONLY. The
following picture shows an open dialog with the file selection mode set to DIRECTORIES_ONLY. Note
that, in the Java look and feel at least, only directories are visible — not files.
If you want to create a file chooser for a task other than opening or saving,
or if you want to customize the file chooser, keep reading. We’ll discuss the
following topics:
• Another Example: FileChooserDemo2
• Using a File Chooser for a Custom Task
• Filtering the List of Files
• Customizing the File View
• Providing an Accessory Component
• The File Chooser API
• Examples that Use File Choosers
139
As the figure shows, this file chooser has been customized for a special task
(Attach), provides a user-choosable file filter (Just Images), uses a special
file view for image files, and has an accessory component that displays a
thumbnail sketch of the currently selected image file.
The remainder of this section shows you the code that creates and
customizes this file chooser. See the examples index for links to all the files
required by this example.
The class has another method, showDialog, for displaying a file chooser for
a custom task in a dialog. In the Java look and feel, the only difference
between this dialog and the other file chooser dialogs is the title on the
dialog window and the label on the approve button. Here’s the code from
FileChooserDemo2 that brings up the file chooser dialog for the Attach
task:
JFileChooser fc = new JFileChooser();
int returnVal =
fc.showDialog(FileChooserDemo2.this, “Attach”);
The first argument to the showDialog method is the parent component for the dialog. The second
argument is a String that provides both the title for the dialog window and the label for the approve
button.
140
Once again, the file chooser doesn’t do anything with the selected file. The
program is responsible for implementing the custom task for which the file
chooser was created.
Application-controlled filtering
The application determines which files are shown. Create a custom subclass of FileFilter ,
instantiate it, and use the instance as an argument to setFileFilter. The file chooser shows only
those files that the filter accepts.
User-choosable filtering
The file chooser GUI provides a list of filters that the user can choose from. When the user chooses a
filter, the file chooser shows only those file accepted by that filter. FileChooserDemo2 adds a custom
file filter to the list of user-choosable filters:
fc.addChoosableFileFilter(new ImageFilter());
By default, the list of user-choosable filters includes the Accept All filter, which lets the user see all non-
hidden files. This example uses the following code to disable the Accept All filter:
fc.setAcceptAllFileFilterUsed(false);
Our custom file filter is implemented in ImageFilter.java and is a subclass of FileFilter.
The ImageFilter class implements the getDescription method to return “Just Images” — a
string to put in the list of user-choosable filters. ImageFilter also implements the accept method so
that it accepts all directories and any file that has a .png, .jpg, .jpeg, .gif, .tif, or .tiff
filename extension.
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
141
extension.equals(Utils.jpg) ||
extension.equals(Utils.png)) {
return true;
} else {
return false;
}
}
return false;
}
By accepting all directories, this filter allows the user to navigate around the file system. If the bold lines
were omitted from this method, the user would be limited to the directory with which the chooser was
initialized.
The preceding code sample uses the getExtension method and several
string constants from Utils.java , shown here:
public class Utils {
public final static String jpeg = “jpeg”;
public final static String jpg = “jpg”;
public final static String gif = “gif”;
public final static String tiff = “tiff”;
public final static String tif = “tif”;
public final static String png = “png”;
/*
• Get the extension of a file.
*/
public static String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf(‘.’);
String getTypeDescription(File f)
Returns a description of the file type. This is not yet used by any look and feel. Here is
ImageFileView’s implementation of this method:
public String getTypeDescription(File f) {
142
String extension = Utils.getExtension(f);
String type = null;
if (extension != null) {
if (extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg)) {
type = “JPEG Image”;
} else if (extension.equals(Utils.gif)){
type = “GIF Image”;
} else if (extension.equals(Utils.tiff) ||
extension.equals(Utils.tif)) {
type = “TIFF Image”;
} else if (extension.equals(Utils.png)){
type = “PNG Image”;
}
}
return type;
}
Icon getIcon(File f)
Returns an icon representing the file or its type. Here is ImageFileView’s implementation of this
method:
public Icon getIcon(File f) {
String extension = Utils.getExtension(f);
Icon icon = null;
if (extension != null) {
if (extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg)) {
icon = jpgIcon;
} else if (extension.equals(Utils.gif)) {
icon = gifIcon;
} else if (extension.equals(Utils.tiff) ||
extension.equals(Utils.tif)) {
icon = tiffIcon;
} else if (extension.equals(Utils.png)) {
icon = pngIcon;
}
}
return icon;
}
String getName(File f)
Returns the name of the file. Most implementations of this method should return null to indicate that the
look and feel should figure it out. Another common implementation returns f.getName().
String getDescription(File f)
Returns a description of the file. This is not yet used by any look and feel. The intent is to describe
individual files more specifically. A common implementation of this method returns null to indicate that
the look and feel should figure it out.
Boolean isTraversable(File f)
Returns whether a directory is traversable. Most implementations of this method should return null to
indicate that the look and feel should figure it out. Some applications might want to prevent users from
descending into a certain type of directory because it represents a compound document. The
isTraversable method should never return true for a non-directory.
143
of the image. Otherwise, the accessory component is empty. Aside from a previewer, probably the most
common use for the accessory component is a panel with more controls on it — say, checkboxes that
toggle some features.
The file chooser fires a property change event when the user selects an item
in the list. A program with an accessory component must register to receive
these events to update the accessory component whenever the selection
changes. In the example, the ImagePreview object itself registers for these
events. This keeps all the code related to the accessory component together
in one class.
Here is the example’s implementation of the propertyChange method,
which is the method called when a property change event is fired:
//where member variables are declared
File file = null;
...
public void propertyChange(PropertyChangeEvent e) {
boolean update = false;
String prop = e.getPropertyName();
144
• Customizing the File Chooser
Creating and Showing the File Chooser
Method or Constructor Purpose
JFileChooser()
Create a file chooser instance. The File and String
JFileChooser(File)
JFileChooser(String) arguments, when present, provide the initial directory.
int Show a modal dialog containing the file chooser. These
showOpenDialog(Component)
methods return APPROVE_OPTION if the user approved the
int
operation and CANCEL_OPTION if the user cancelled it.
showSaveDialog(Component)
int showDialog(Component, Another possible return value is ERROR_OPTION, which
String) means an unanticipated error occurred.
Selecting Files and Directories
Method Purpose
void setSelectedFile(File)
File getSelectedFile() Set or get the currently selected file.
void setSelectedFiles(File[])
File[] getSelectedFiles() Set or get the currently selected files.
void
changeToParentDirectory() Change the list to display the current directory’s parent.
void
rescanCurrentDirectory() Check the file system and update the chooser’s list.
145
Method Purpose
void
setAccessory(javax.swing.JComponent) Set or get the file chooser’s accessory
JComponent getAccessory() component.
FileFilter[] getChoosableFileFilters()
void addChoosableFileFilter(FileFilter)
Set, get, or modify the list of user-
boolean
removeChoosableFileFilter(FileFilter) choosable file filters.
void resetChoosableFileFilters()
FileFilter getAcceptAllFileFilter()
void setFileHidingEnabled(boolean) Set or get whether hidden files are
boolean isFileHidingEnabled() displayed.
146
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
The rest of this section tells you how to accomplish some common table-related tasks. Here are the topics
this section covers:
• Creating a Simple Table
• Adding a Table to a Container
• Setting and Changing Column Widths
• Detecting User Selections
• Creating a Table Model
• Detecting Data Changes
• Concepts: Cell Editors and Renderers
• Validating User-Entered Text
• Using a Combo Box as an Editor
• Specifying Other Editors
• Further Customizing Table Display and Event Handling
• Sorting and Otherwise Manipulating Data
• The Table API
• Examples that Use Tables
147
“Speed reading”, new Integer(20), new Boolean(true)},
{”Angela”, “Lih”,
“Teaching high school”, new Integer(4), new Boolean(false)}
};
Version Note: Before Swing 1.0.2, the scroll pane didn’t get the table header unless you created the scroll
pane using the JTable.createScrollPaneForTable method. Here are examples of the
recommended code, before and after Swing 1.0.2:
//1.0.1 code (causes deprecation warning
//in 1.0.2 and later releases):
scrollPane =
JTable.createScrollPaneForTable(table);
//Recommended code (causes missing column names
in 1.0.1):
scrollPane = new JScrollPane(table);
If you’re using a table without a scroll pane, then you must get the table
header component and place it yourself. For example:
container.setLayout(new BorderLayout());
148
container.add(table.getTableHeader(),
BorderLayout.NORTH);
container.add(table, BorderLayout.CENTER);
When the user resizes a column by dragging its right border, then either
other columns must change size, or the table’s size must change. By default,
the table’s size remains the same, and all columns to the right of the drag
point resize to accommodate space added or removed from the column to the
left of the drag point.
The following figures illustrate the default resizing behavior.
When the user resizes a column, some of the other columns must adjust
size for the table to stay the same size.
When the entire table is resized, all the columns are resized.
To customize initial column widths, you can invoke setPreferredWidth on
each of your table’s columns. This sets both the preferred widths of the
columns and their approximate relative widths. For example, adding the
following code to SimpleTableDemo makes its third column bigger than the
other columns:
TableColumn column = null;
for (int i = 0; i < 5; i++) {
149
column = table.getColumnModel().getColumn(i);
if (i == 2) {
column.setPreferredWidth(100); //sport column is bigger
} else {
column.setPreferredWidth(50);
}
}
Version Note: The setPreferredWidth method was first introduced in Swing 1.1 Beta 2. For
previous releases, you must use setMinWidth instead, making sure to invoke it on every column.
(Otherwise, the columns you miss will be very thin.)
AUTO_RESIZE_NEXT_COLUMN
Adjusts only the columns immediately to the left and right of the drag point.
AUTO_RESIZE_OFF
Adjust the table size instead.
Version Note: Before the Swing 1.1 Beta release, the default resize mode was
AUTO_RESIZE_ALL_COLUMNS. However, that mode isn’t intuitive, so we changed the default mode to
a mode added in Swing 1.1 Beta: AUTO_RESIZE_SUBSEQUENT_COLUMNS.
150
...
ListSelectionModel rowSM = table.getSelectionModel();
rowSM.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
//Ignore extra messages.
if (e.getValueIsAdjusting()) return;
ListSelectionModel lsm =
(ListSelectionModel)e.getSource();
if (lsm.isSelectionEmpty()) {
...//no rows are selected
} else {
int selectedRow = lsm.getMinSelectionIndex();
...//selectedRow is selected
}
}
});
The JTable constructor used by SimpleTableDemo creates its table model with code like this:
new AbstractTableModel() {
public String getColumnName(int col) {
return columnNames[col].toString();
}
public int getRowCount() { return rowData.length; }
public int getColumnCount() { return columnNames.length; }
public Object getValueAt(int row, int col) {
return rowData[row][col];
}
public boolean isCellEditable(int row, int col)
{ return true; }
public void setValueAt(Object value, int row, int col) {
rowData[row][col] = value;
fireTableCellUpdated(row, col);
}
}
As the preceding code shows, implementing a table model can be simple. Generally, you implement your
table model in a subclass of the AbstractTableModel class.
Your model might hold its data in an array, vector, or hashtable, or it might
get the data from an outside source such as a database. It might even
generate the data at execution time. For examples of getting data from a
database, see the table examples provided as part of the JFC/Swing and Java
2 Standard Edition SDK releases.
151
Version Note: In JFC/Swing-only releases (for use with JDK 1.1) the table examples are in the
examples/Table directory. In 1.2, the table examples are in the demo/jfc/Table directory. In 1.3,
the examples move to the demo/jfc/TableExamples directory. In 1.4, the directory is
demo/jfc/TableExample (no final s).
This table is different from the SimpleTableDemo table in the following ways:
• SimpleTableDemo’s table model, having been created automatically by JTable, isn’t smart
enough to know that the # of Years column contains numbers (which should generally be right
aligned). It also doesn’t know that the Vegetarian column contains boolean values, which can be
represented by check boxes. TableDemo’s custom table model, even though it’s simple, can easily
determine the data’s type, helping the JTable display the data in the best format.
• In SimpleTableDemo, all cells are editable. In TableDemo, we implemented the custom table
model so that it doesn’t let you edit the name columns; it does, however, let you edit the other
columns.
Below is the code from TableDemo.java that is different from the code in
SimpleTableDemo.java . Bold font indicates the code that makes this
table’s model different from the table model defined automatically in
SimpleTableDemo.
public TableDemo() {
...
MyTableModel myModel = new MyTableModel();
JTable table = new JTable(myModel);
table.setPreferredScrollableViewportSize(
new Dimension(500, 70));
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this window.
setContentPane(scrollPane);
...
}
152
return data[row][col];
}
/*
• Don’t need to implement this method unless your table’s
• editable.
*/
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
if (col < 2) {
return false;
} else {
return true;
}
}
/*
• Don’t need to implement this method unless your table’s
• data can change.
*/
public void setValueAt(Object value, int row, int col) {
...//debugging code not shown...
...//ugly special handling of Integers not shown...
data[row][col] = value;
fireTableCellUpdated(row, col);
...//debugging code not shown...
}
...
153
AbstractTableModel defines to help you fire table-model events are
fireTableCellUpdated, fireTableChanged, fireTableDataChanged,
fireTableRowsDeleted, fireTableRowsInserted,
fireTableRowsUpdated, and fireTableStructureChanged.
Instead, a single cell renderer is used to draw all of the cells in a column.
Often, this cell renderer is shared between all columns that contain the same
type of data. You can think of the renderer as a configurable ink stamp that
the table uses to stamp appropriately formatted data onto each cell. When
the user starts to edit a cell’s data, a cell editor takes over the cell,
controlling the cell’s editing behavior.
For example, each cell in the # of Years column in TableDemo contains
Number data—specifically, an Integer object. By default, the cell renderer
for a Number-containing column uses a single JLabel instance to draw the
appropriate numbers, right-aligned, on the column’s cells. If the user begins
editing one of the cells, the default cell editor uses a right-aligned
JTextField to control the cell editing.
To choose the renderer that displays the cells in a column, a table first
determines whether the you specified a renderer for that particular column.
(We’ll tell you how to specify renderers a bit later.) If you didn’t, then the
154
table invokes the table model’s getColumnClass method, which gets the
data type of the column’s cells. Next, the table compares the column’s data
type with a list of data types for which cell renderers are registered. This list
is initialized by the table, but you can add to it or change it. Currently, tables
put the following types of data in the list:
• Boolean—rendered with a check box.
• Number—rendered by a right-aligned label.
• ImageIcon—rendered by a centered label.
• Object—rendered by a label that displays the object’s string value.
The table chooses cell editors using a similar algorithm.
Remember that if you let a table create its own model, it uses Object as the
type of every column. TableDemo.java shows how to specify more
precise column types.
The next few sections tell you how to customize cell display and editing by
specifying cell renderers and editors either by column or by data type,
Version Note: As of 1.3, the ugly conversion code is unnecessary because the default text cell editor
automatically converts the data into the proper type.
What we’d really like to do is to check the user’s input while the user is
typing, and to have the cell editor return an Integer instead of a string. You
can accomplish one or both of these tasks by using a custom text field to
control the cell editing.
A custom text field can check the user’s input either while the user is typing,
or after the user has indicated the end of typing (such as by pressing return).
We call these two types of checking change-validation and action-
validation, respectively.
The following code, taken from TableEditDemo.java , sets up a change-
validated text field. The bold line of code makes the text field the editor for
all columns that contain data of type Integer.
final WholeNumberField integerField = new WholeNumberField(0,
5);
integerField.setHorizontalAlignment(WholeNumberField.RIGHT);
DefaultCellEditor integerEditor =
new DefaultCellEditor(integerField) {
//Override DefaultCellEditor’s getCellEditorValue method
//to return an Integer, not a String:
155
public Object getCellEditorValue() {
return new Integer(integerField.getValue());
}
};
table.setDefaultEditor(Integer.class,
integerEditor);
The WholeNumberField class used above is a custom JTextField subclass that allows the user to
enter only digits. The getValue method returns the int value of the WholeNumberField’s contents.
See How to Use Text Fields for more information about WholeNumberField. That section also
provides a more general-purpose validating text field, called DecimalField, that you can customize so
that it validates any number format that you specify.
The combo box editor is implemented in TableRenderDemo.java , which is discussed some more
in Further Customizing Table Display and Event Handling.
What if you want to specify an editor that isn’t a text field, check box, or
combo box? Well, because DefaultCellEditor doesn’t support other types
156
of components, you must do a little more work. You need to create a
subclass of the desired editor component, and the subclass must implement
the TableCellEditor interface. Then you set up the component as an
editor for a data type or column, using the setDefaultEditor or
setCellEditor method, respectively.
Version Note: The AbstractCellEditor class, which was added in 1.3, makes it much easier to add
support for non-standard editor components. We plan to add an example of using it. For now, you can find
information in the JTable release document and the API documentation for AbstractCellEditor .
You can find the code in TableDialogEditDemo.java . The example also requires
WholeNumberField.java .
Although renderers determine how each cell or column header looks, they
don’t handle events. To pick up the events that take place inside a table, you
should choose the appropriate technique for the sort of event you’re
interested in. For a cell that’s being edited, the editor should process events.
To detect row/column/cell selections and deselections, use a selection
listener as described in Detecting User Selections. To detect mouse clicks on
a column header, you can register a mouse listener on the table header. (See
TableSorter.java for an example.) To detect other events, you can
register the appropriate listener on the JTable object.
157
Creating a custom renderer can be as easy as creating a subclass of an
existing component and then implementing the single method in the
TableCellRenderer interface. In the preceding figure, the color renderer
used for Favorite Color cells is a subclass of JLabel. You can find the code
for the renderer in the ColorRenderer inner class in
TableDialogEditDemo.java . Here is the code that registers a
ColorRenderer instance as the default renderer for all Color data:
table.setDefaultRenderer(Color.class, new
ColorRenderer(true));
You can even specify a cell-specific renderer, if you like. To do this, you need to define a JTable
subclass that overrides the getCellRenderer method. For example, the following code makes the first
cell in the first column of the table use a custom renderer:
TableCellRenderer weirdRenderer = new WeirdRenderer();
table = new JTable(...) {
public TableCellRenderer getCellRenderer(int row, int column)
{
if ((row == 0) && (column == 0)) {
return weirdRenderer;
}
// else...
return super.getCellRenderer(row, column);
}
};
To add tool tips to cells or column headers, you need to get or create the cell
or header renderer, and then use the setToolTipText method of the
renderer’s component. TableRenderDemo.java adds tool tips to both the
cells and header for the Sport column with the following code:
//Set up tool tips for the sport cells.
DefaultTableCellRenderer renderer =
new DefaultTableCellRenderer();
renderer.setToolTipText(“Click for combo box”);
sportColumn.setCellRenderer(renderer);
//Set up tool tip for the sport column header.
TableCellRenderer headerRenderer =
sportColumn.getHeaderRenderer();
if (headerRenderer instanceof DefaultTableCellRenderer) {
((DefaultTableCellRenderer)headerRenderer).setToolTipText(
“Click the sport to see a list of choices”);
}
Version Note: As of 1.3, the getHeaderRenderer method returns null by default. To find the
default header renderer, you instead use the new getDefaultRenderer method, which is defined in
JTableHeader. For example, in the preceding snippet the red-colored code should be changed to this:
TableCellRenderer headerRenderer =
table.getTableHeader().
getDefaultRenderer();
You can find the 1.3 version of TableRenderDemo.java in example-
1dot3/TableRenderDemo.java .
158
TableColumn column = null;
Component comp = null;
int headerWidth = 0;
int cellWidth = 0;
Object[] longValues = model.longValues;
Version Note: Because the 1.3 getHeaderRenderer method returns null by default, you generally
use the 1.3 getDefaultRenderer method instead. For example, the red code from the previous
snippet would be replaced with this:
table.getTableHeader().getDefaultRend
erer()
You can find the 1.3 version of TableRenderDemo.java in example-
1dot3/TableRenderDemo.java .
You can use the TableMap and TableSorter classes when implementing your data manipulator.
TableMap.java implements TableModel and serves as a superclass for data manipulators.
TableSorter.java is a TableMap subclass that sorts the data provided by another table model.
You can either change these classes, using them as a basis for writing your own data manipulator, or use
the classes as-is to provide sorting functionality.
To implement sorting with TableSort, you need just three lines of code.
The following listing shows the differences between TableDemo and its
sorting cousin, TableSorterDemo.java .
TableSorter sorter = new TableSorter(myModel);
//ADDED THIS
//JTable table = new JTable(myModel);
//OLD
159
JTable table = new JTable(sorter);
//NEW
sorter.addMouseListenerToHeaderInTable(table);
//ADDED THIS
The addMouseListenerToHeaderInTable method adds a mouse listener that detects clicks over
the column headers. When the listener detects a click, it sorts the rows based on the clicked column. As the
following snapshot shows, when you click “Last Name”, the rows are reordered so that the row with
“Andrews” becomes the first row. When you Shift-click a column header, the rows are sorted in reverse
order.
JTableHeader The component that presents the column names to the user. By
default, the table generates this component automatically.
TableModel Respectively, the interface that a table model must implement and
AbstractTableModel the usual superclass for table model implementations.
TableCellRenderer Respectively, the interface that a table cell renderer must
DefaultTableCellRenderer implement and the usual implementation used.
TableCellEditor Respectively, the interface that a table cell editor must
DefaultCellEditor implement, the usual implementation used, and the usual
AbstractCellEditor superclass for table cell editor implementations.
Respectively, the interface that a table column model must
TableColumnModel implement and the usual implementation used. You don’t usually
DefaultTableColumnModel need to deal with the table column model directly unless you
need to get the column selection model, or get a column index or
object.
Controls all the attributes of a table column, including
TableColumn resizability; minimum, preferred, current, and maximum widths;
and an optional column-specific renderer/editor.
DefaultTableModel A Vector-based table model used by JTable when you
160
construct a table specifying no data model and no data.
Creating and Setting Up a Table
Constructor or Method Purpose
Create a table. The optional
TableModel argument
specifies the model that provides
the data to the table. The optional
JTable(TableModel) TableColumnModel and
JTable(TableModel, TableColumnModel) ListSelectionModel
JTable(TableModel, TableColumnModel, arguments let you specify the
ListSelectionModel) table column model and the row
JTable() selection model. As an alternative
JTable(int, int) to specifying a table model, you
JTable(Object[][], Object[]) can supply data and column
JTable(Vector, Vector) names, using arrays or vectors.
Another option is to specify no
data, optionally specifying the
number of rows and columns
(both integers) to be in the table.
Set the size of the visible part of
void
setPreferredScrollableViewportSize(Dimension) the table when it’s viewed within
a scroll pane.
void setMinWidth(int)
void setPreferredWidth(int) Set the minimum, preferred, or maximum width of the
void setMaxWidth(int) column.
(in TableColumn)
int getMinWidth()
int getPreferredWidth()
int getMaxWidth() Get the minimum, preferred, maximum, or current width
int getWidth() of the column.
(in TableColumn)
Using Editors and Renderers
Method Purpose
void setDefaultRenderer(Class,
TableCellRenderer) Set the renderer or editor used, by default, for
void setDefaultEditor(Class, all cells in all columns that return objects of the
TableCellEditor) specified type.
(in JTable)
void Set the renderer or editor used for all cells in
setCellRenderer(TableCellRenderer) this column.
161
void setCellEditor(TableCellEditor)
(in TableColumn)
Get the header renderer for this column.
TableCellRenderer
getDefaultRenderer() Get the header renderer used when none is
defined by a table column. Introduced in 1.3.
(in JTableHeader)
Implementing Selection
Method Purpose
Set the selection intervals allowed in the
table. Valid values are defined in
ListSelectionModel as
void setSelectionMode(int) SINGLE_SELECTION,
SINGLE_INTERVAL_SELECTION, and
MULTIPLE_INTERVAL_SELECTION (the
default).
void
setSelectionModel(ListSelectionModel) Set or get the model used to control row
ListSelectionModel selections.
getSelectionModel()
Set the table’s selection orientation. The
void setRowSelectionAllowed(boolean)
void boolean argument specifies whether that
setColumnSelectionAllowed(boolean) particular type of selection is allowed. By
void setCellSelectionEnabled(boolean) default, row selection is allowed, and column
and cell selection are not.
162
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
Swing provides five 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 of Swing’s 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 and places each
text component class into one of three groups:
Styled A styled text component can display and edit text JEditorPane
Text using more than one font. Some styled text and its subclass
Areas components allow embedded images and even JTextPane
embedded components. Styled text components
are powerful and multi-faceted components
suitable for high-end needs, and offer more
avenues for customization than the other text
components. Because they are so powerful and
163
flexible, styled text components typically require
more up-front programming to set up and use.
The 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.
164
Trail: Creating a GUI with JFC/Swing
Lesson: Using Swing Components
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’t 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 .
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.
165
The following code, taken from TreeDemo.java , creates the JTree object:
DefaultMutableTreeNode top =
new DefaultMutableTreeNode(“The Java Series”);
createNodes(top);
final JTree 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: Object-Oriented “
• “Programming for the Internet”,
“tutorial.html”));
category.add(book);
//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: “
166
• “A Guide to Constructing GUIs”,
“swingtutorial.html”));
category.add(book);
//...add many 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—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.
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.
Note: Swing 1.1.1 Beta 1 introduced the ability to specify HTML text for the string displayed by tree
nodes. See Using HTML on a Label for details.
167
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)
tree.getLastSelectedPathComponent();
if (node == null) 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.
• Creates an event handler and registers it 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 .
168
As the preceding figures show, a tree conventionally displays an icon and some text for each node. You
can customize these, as we’ll show shortly.
To specify that the Java Look & Feel should draw lines detailing the relationships between nodes (as
shown in the next figure), use this code:
tree.putClientProperty(“JTree.lineStyle”,
“Angled”);
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’s expanded.
For example, in the Windows and Motif Look & Feel implementations, the
default icon for each leaf node is a dot; in the Java Look & Feel, the default
leaf icon is a paper-like symbol. In all the look-and-feel implementations
we’ve shown, branch nodes are marked with folder-like symbols. The
Windows Look & Feel even has different icons for expanded branches
versus collapsed branches.
169
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 . 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’ve set up the icons,
use the tree’s setCellRenderer method to specify that the
DefaultTreeCellRenderer paint its nodes.
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. Here is an example of creating 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. You can find the entire example in TreeIconDemo2.java .
//...where the tree is initialized:
//Enable tool tips.
170
ToolTipManager.sharedInstance().registerComponent(tree)
;
...
tree.setCellRenderer(new MyRenderer());
...
class MyRenderer extends DefaultTreeCellRenderer {
ImageIcon tutorialIcon;
public MyRenderer() {
tutorialIcon = new ImageIcon(“images/middle.gif”);
}
return this;
}
return false;
}
}
Here is the result:
171
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: Cell Editors and Renderers.
That section discusses table cell editors and renderers, which are similar to
tree cell editors and renderers.
The application is based on an example provided by tutorial reader Richard Stanford. You can find the
source code in DynamicTreeDemo.java and DynamicTree.java . Here is the code that
initializes the tree:
172
rootNode = new DefaultMutableTreeNode(“Root
Node”);
treeModel = new DefaultTreeModel(rootNode);
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.
/*
• If the event lists children, then the changed
• node is the child of the node we’ve already
• gotten. Otherwise, the changed node and the
• specified node are the same.
*/
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:
public void actionPerformed(ActionEvent e) {
treePanel.addObject(“New Node “ + newNodeSuffix++);
}
...
public DefaultMutableTreeNode addObject(Object child) {
DefaultMutableTreeNode parentNode = null;
TreePath parentPath = tree.getSelectionPath();
if (parentPath == null) {
//There’s no selection. Default to the root node.
parentNode = rootNode;
} else {
173
parentNode = (DefaultMutableTreeNode)
(parentPath.getLastPathComponent());
}
174
defined in Person.java , and the application’s GUI is created by the main
method in GenealogyExample.java .
TreeModelListener The interface and event type used for detecting tree model
TreeModelEvent changes. For more information, see How to Write a Tree Model
Listener .
The interfaces and event type used for detecting tree expansion
TreeExpansionListener
TreeWillExpandListener and collapse. For more information, see How to Write a Tree
TreeExpansionEvent Expansion Listener and How to Write a Tree-Will-Expand
Listener .
An exception that a TreeWillExpandListener can throw
ExpandVetoException to indicate that the impending expansion/collapse should not
happen. For more information, see How to Write a Tree-Will-
Expand Listener .
Creating and Setting Up a Tree
Constructor or Method Purpose
JTree(TreeNode) Create a tree. The TreeNode argument specifies
JTree(TreeNode, boolean) the root node, to be managed by the default tree
175
model. The TreeModel argument specifies the
model that provides the data to the table. The
boolean argument specifies how the tree should
JTree(TreeModel) determine whether a node can have children. The
JTree() no-argument version of this constructor is for use in
JTree(Hashtable) builders; it creates a tree that contains some sample
JTree(Object[]) data. If you specify a Hashtable, array of
JTree(Vector) objects, of Vector as an argument, then the
argument is treated as a list of nodes under the root
node (which is not displayed), and a model and tree
nodes are constructed accordingly.
void
setCellRenderer(TreeCellRenderer) Sets the renderer that draws each node.
void setEditable(boolean) The first method sets whether the user can edit tree
void nodes. By default, tree nodes are not editable. The
setCellEditor(TreeCellEditor) second sets which customized editor to use.
Sets whether the tree shows handles for its leftmost
nodes, letting you expand and collapse the nodes.
void setShowsRootHandles(boolean) The default is false. If the tree doesn’t show the root
node, then you should invoke
setShowsRootHandles(true).
Set or get the dragEnabled property, which must
void setDragEnabled(boolean) be true to enable drag handling on this component.
boolean getDragEnabled() The default value is false. See Drag and Drop for
more details. Introduced in 1.4.
Implementing Selection
Method Purpose
void addTreeSelectionListener Registers a listener to detect when the a node
(TreeSelectionListener) is selected or deselected.
void Set or get the model used to control node
setSelectionModel(TreeSelectionModel) selections. You can turn off node selection
TreeSelectionModel completely using
getSelectionModel() setSelectionModel(null).
Set or get the selection mode. The value can
void setSelectionMode(int) be CONTIGUOUS_TREE_SELECTION,
int getSelectionMode() DISCONTIGUOUS_TREE_SELECTION, or
(in TreeSelectionModel) SINGLE_TREE_SELECTION (all defined in
TreeSelectionModel).
Get the object representing the currently
selected node. This is equivalent to invoking
Object getLastSelectedPathComponent() getLastPathComponent on the value
returned by
tree.getSelectionPath().
void setSelectionPath(TreePath) Set or get the path to the currently selected
TreePath getSelectionPath() node.
void setSelectionPaths(TreePath[]) Set or get the paths to the currently selected
TreePath[] getSelectionPaths() nodes.
void setSelectionPath(TreePath) Set or get the path to the currently selected
TreePath getSelectionPath() node.
Showing and Hiding Nodes
176
Method Purpose
Register a listener to detect when the tree nodes have
void addTreeExpansionListener expanded or collapsed, or will be expanded or
(TreeExpansionListener) collapsed, respectively. To veto an impending
void addTreeWillExpandListener expansion or collapse, a
(TreeWillExpandListener) TreeWillExpandListener can throw a
ExpandVetoException.
void expandPath(TreePath)
void collapsePath(TreePath) Expand or collapse the specified tree path.
void setToggleClickCount(int) Set or get the number of mouse clicks before a node
int getToggleClickCount() will expand or close. The default is two. Introduced in
1.3.
TreePath getNextMatch(String, Return the TreePath to the next tree element that
int, Position.Bias) begins with the specific prefix. Introduced in 1.4.
177