Using Swing Components
Using Swing Components
Using Swing Components
http://java.sun.com/docs/books/tutorial/uiswing/components/jcomponent.html
This lesson gives you the background information you need to use the Swing components, and then
describes every Swing component. It assumes that you have successfully compiled and run a
program that uses Swing components, and that you are familiar with basic Swing concepts. These
prerequisites are covered in Getting Started with Swing and Learning Swing with the NetBeans IDE.
Before you get started, you may want to check out these pages (from the Graphical User Interfaces
lesson in the Core trail) which have pictures of all the standard Swing components, from top-level
containers to scroll panes to buttons. To find the section that discusses a particular component, just
click the component's picture.
Discusses how to use the features shared by the JFrame, JDialog, and JApplet classes — content
panes, menu bars, and root panes. It also discusses the containment hierarchy, which refers to the
tree of components contained by a top-level container.
Tells you about the features JComponent provides to its subclasses — which include almost all
Swing components — and gives tips on how to take advantage of these features. This section ends
with API tables describing the commonly used API defined by JComponent and its superclasses,
Container and Component.
Describes the features and API shared by all components that descend from JTextComponent. You
probably do not need to read this section if you are just using text fields (formatted or not) or text
areas.
How to...
Sections on how to use each Swing component, in alphabetical order. We do not expect you to read
these sections in order. Instead, we recommend reading the relevant "How to" sections once you are
ready to start using Swing components in your own programs. For example, if your program needs a
frame, a label, a button, and a color chooser, you should read How to Make Frames, How to Use
Labels, How to Use Buttons, and How to Use Color Choosers.
510
Describes how to vary the font, color, or other formatting of text displayed by Swing components by
using HTML tags.
Using Models
Tells you about the Swing model architecture. This variation on Model-View-Controller (MVC)
means that you can, if you wish, specify how the data and state of a Swing component are stored and
retrieved. The benefits are the ability to share data and state between components, and to greatly
improve the performance of components such as tables that display large amounts of data.
Using Borders
Borders are very handy for drawing lines, titles, and empty space around the edges of components.
(You might have noticed that the examples in this trail use a lot of borders.) This section tells you
how to add a border to any JComponent.
Using Icons
Many Swing components can display icons. Usually, icons are implemented as instances of the
ImageIcon class.
Try these questions and exercises to test what you have learned in this lesson.
511
Note: Although JInternalFrame mimics JFrame, internal frames aren't actually top-level
containers.
Here's a picture of a frame created by an application. The frame contains a green menu bar (with no
menus) and, in the frame's content pane, a large blank, yellow label.
You can find the entire source for this example in TopLevelDemo.java. Although the example uses
a JFrame in a standalone application, the same concepts apply to JApplets and JDialogs.
As the ellipses imply, we left some details out of this diagram. We reveal the missing details a bit
later. Here are the topics this section discusses:
Each program that uses Swing components has at least one top-level container. This top-level
container is the root of a containment hierarchy — the hierarchy that contains all of the Swing
components that appear inside the top-level container.
As a rule, a standalone application with a Swing-based GUI has at least one containment hierarchy
with a JFrame as its root. For example, if an application has one main window and two dialogs, then
the application has three containment hierarchies, and thus three top-level containers. One
containment hierarchy has a JFrame as its root, and each of the other two has a JDialog object as its
root.
512
A Swing-based applet has at least one containment hierarchy, exactly one of which is rooted by a
JApplet object. For example, an applet that brings up a dialog has two containment hierarchies. The
components in the browser window are in a containment hierarchy rooted by a JApplet object. The
dialog has a containment hierarchy rooted by a JDialog object.
Here's the code that the preceding example uses to get a frame's content pane and add the yellow
label to it:
frame.getContentPane().add(yellowLabel, BorderLayout.CENTER);
As the code shows, you find the content pane of a top-level container by calling the
getContentPane method. The default content pane is a simple intermediate container that inherits
from JComponent, and that uses a BorderLayout as its layout manager.
It's easy to customize the content pane — setting the layout manager or adding a border, for example.
However, there is one tiny gotcha. The getContentPane method returns a Container object, not a
JComponent object. This means that if you want to take advantage of the content pane's JComponent
features, you need to either typecast the return value or create your own component to be the content
pane. Our examples generally take the second approach, since it's a little cleaner. Another approach
we sometimes take is to simply add a customized component to the content pane, covering the
content pane completely.
Note that the default layout manager for JPanel is FlowLayout; you'll probably want to change it.
To make a component the content pane, use the top-level container's setContentPane method. For
example:
topLevelContainer.setContentPane(contentPane);
Note: As a convenience, the add method and its variants, remove and setLayout have been
overridden to forward to the contentPane as necessary. This means you can write
frame.add(child);
and the child will be added to the contentPane.
Note that only these three methods do this. This means that getLayout() will not return the layout
set with setLayout().
In theory, all top-level containers can hold a menu bar. In practice, however, menu bars usually
appear only in frames and applets. To add a menu bar to a top-level container, create a JMenuBar
object, populate it with menus, and then call setJMenuBar. The TopLevelDemo adds a menu bar to
its frame with this code:
frame.setJMenuBar(greenMenuBar);
For more information about implementing menus and menu bars, see How to Use Menus.
513
The Root Pane
Each top-level container relies on a reclusive intermediate container called the root pane. The root
pane manages the content pane and the menu bar, along with a couple of other containers. You
generally don't need to know about root panes to use Swing components. However, if you ever need
to intercept mouse clicks or paint over multiple components, you should get acquainted with root
panes.
Here's a list of the components that a root pane provides to a frame (and to every other top-level
container):
We've already told you about the content pane and the optional menu bar. The two other components
that a root pane adds are a layered pane and a glass pane. The layered pane contains the menu bar
and content pane, and enables Z-ordering of other components. The glass pane is often used to
intercept input events occurring over the top-level container, and can also be used to paint over
multiple components.
The JComponent class extends the Container class, which itself extends Component. The
Component class includes everything from providing layout hints to supporting painting and events.
The Container class has support for adding components to the container and laying them out. This
section's API tables summarize the most often used methods of Component and Container, as well
as of JComponent.
JComponent Features
Tool tips
Painting and borders
Application-wide pluggable look and feel
Custom properties
Support for layout
Support for accessibility
514
Support for drag and drop
Double buffering
Key bindings
Tool tips
By specifying a string with the setToolTipText method, you can provide help to
users of a component. When the cursor pauses over the component, the specified
string is displayed in a small window that appears near the component. See How to
Use Tool Tips for more information.
Painting and borders
The setBorder method allows you to specify the border that a component displays
around its edges. To paint the inside of a component, override the paintComponent
method. See How to Use Borders and Performing Custom Painting for details.
Application-wide pluggable look and feel
Behind the scenes, each JComponent object has a corresponding ComponentUI object
that performs all the drawing, event handling, size determination, and so on for that
JComponent. Exactly which ComponentUI object is used depends on the current look
and feel, which you can set using the UIManager.setLookAndFeel method. See How
to Set the Look and Feel for details.
Custom properties
You can associate one or more properties (name/object pairs) with any JComponent.
For example, a layout manager might use properties to associate a constraints object
with each JComponent it manages. You put and get properties using the
putClientProperty and getClientProperty methods. For general information
about properties, see Properties.
Support for layout
Although the Component class provides layout hint methods such as
getPreferredSize and getAlignmentX, it doesn't provide any way to set these
layout hints, short of creating a subclass and overriding the methods. To give you
another way to set layout hints, the JComponent class adds setter methods —
setMinimumSize, setMaximumSize, setAlignmentX, and setAlignmentY. See
Laying Out Components Within a Container for more information.
Support for accessibility
The JComponent class provides API and basic functionality to help assistive
technologies such as screen readers get information from Swing components, For
more information about accessibility, see How to Support Assistive Technologies.
Support for drag and drop
The JComponent class provides API to set a component's transfer handler, which is
the basis for Swing's drag and drop support. See Introduction to DnD for details.
Double buffering
Double buffering smooths on-screen painting. For details, see Performing Custom
Painting.
Key bindings
This feature makes components react when the user presses a key on the keyboard.
For example, in many look and feels when a button has the focus, typing the Space
key is equivalent to a mouse click on the button. The look and feel automatically sets
up the bindings between pressing and releasing the Space key and the resulting effects
on the button. For more information about key bindings, see How to Use Key
Bindings.
515
The JComponent class provides many new methods and inherits many methods from Component and
Container. The following tables summarize the methods we use the most.
516
drop. See Introduction to DnD for more details.
Set the text to display in a tool tip. See How to Use Tool
void setToolTipText(String)
Tips for more information.
Set or get the name of the component. This can be useful
void setName(String)
when you need to associate text with a component that
String getName()
does not display text.
Determine whether the component is showing on screen.
boolean isShowing() This means that the component must be visible, and it
must be in a container that is visible and showing.
void setEnabled(boolean) Set or get whether the component is enabled. An enabled
boolean isEnabled() component can respond to user input and generate events.
Set or get whether the component is visible. Components
void setVisible(boolean)
are initially visible, with the exception of top-level
boolean isVisible()
components.
Handling Events
(see Writing Event Listeners for details)
Method Purpose
Adds or removes the specified hierarchy
listener to receive hierarchy changed events
void addHierarchyListener(hierarchyListener l) from this component when the hierarchy to
void removeHierarchyListener(hierarchyListener l) which this container belongs changes. If
listener l is null, no exception is thrown and
no action is performed.
Add or remove a mouse listener to or from
void addMouseListener(MouseListener) the component. Mouse listeners are notified
void removeMouseListener(MouseListener) when the user uses the mouse to interact with
the listened-to component.
void Add or remove a mouse motion listener to or
addMouseMotionListener(MouseMotionListener) from the component. Mouse motion listeners
void are notified when the user moves the mouse
removeMouseMotionListener(MouseMotionListener) within the listened-to component's bounds.
Add or remove a key listener to or from the
component. Key listeners are notified when
void addKeyListener(KeyListener)
the user types at the keyboard and the
void removeKeyListener(KeyListener)
listened-to component has the keyboard
focus.
Add or remove a component listener to or
void addComponentListener(ComponentListener) from the component. Component listeners are
void removeComponentListener(ComponentListener) notified when the listened-to component is
hidden, shown, moved, or resized.
Determine whether the specified point is
within the component. The argument should
boolean contains(int, int)
be specified in terms of the component's
boolean contains(Point)
coordinate system. The two int arguments
specify x and y coordinates, respectively.
Component getComponentAt(int, int) Return the component that contains the
Component getComponentAt(Point) specified x, y position. The top-most child
517
component is returned in the case where
components overlap. This is determined by
finding the component closest to the index 0
that claims to contain the given point via
Component.contains().
Moves the specified component to the
specified z-order index in the container.
518
Dealing with the Containment Hierarchy
(see Using Top-Level Containers for more information)
Method Purpose
Add the specified component to this
container. The one-argument version of this
method adds the component to the end of
Component add(Component) the container. When present, the int
Component add(Component, int) argument indicates the new component's
void add(Component, Object) position within the container. When
present, the Object argument provides
layout constraints to the current layout
manager.
Remove one of or all of the components
void remove(int)
from this container. When present, the int
void remove(Component)
argument indicates the position within the
void removeAll()
container of the component to remove.
Get the root pane that contains the
JRootPane getRootPane()
component.
Get the topmost container for the
component — a Window, Applet, or null if
Container getTopLevelAncestor()
the component has not been added to any
container.
Container getParent() Get the component's immediate container.
Get the number of components in this
int getComponentCount()
container.
Get the one of or all of the components in
Component getComponent(int)
this container. The int argument indicates
Component[] getComponents()
the position of the component to get.
Returns the z-order index of the component
inside the container. The higher a
Component getComponentZOrder(int) component is in the z-order hierarchy, the
Component[] getComponentZOrder() lower its index. The component with the
lowest z-order index is painted last, above
all other child components.
Laying Out Components
(see Laying Out Components Within a Container for more information)
Method Purpose
Set the component's preferred, maximum,
or minimum size, measured in pixels. The
preferred size indicates the best size for
void setPreferredSize(Dimension)
the component. The component should be
void setMaximumSize(Dimension)
no larger than its maximum size and no
void setMinimumSize(Dimension)
smaller than its minimum size. Be aware
that these are hints only and might be
ignored by certain layout managers.
Dimension getPreferredSize() Get the preferred, maximum, or minimum
Dimension getMaximumSize() size of the component, measured in
519
Dimension getMinimumSize() pixels. Many JComponent classes have
setter and getter methods. For those non-
JComponent subclasses, which do not
have the corresponding setter methods,
you can set a component's preferred,
maximum, or minimum size by creating a
subclass and overriding these methods.
Set the alignment along the x- or y- axis.
These values indicate how the component
would like to be aligned relative to other
components. The value should be a
void setAlignmentX(float) number between 0 and 1 where 0
void setAlignmentY(float) represents alignment along the origin, 1 is
aligned the furthest away from the origin,
and 0.5 is centered, and so on. Be aware
that these are hints only and might be
ignored by certain layout managers.
Get the alignment of the component along
the x- or y- axis. For non-JComponent
float getAlignmentX() subclasses, which do not have the
float getAlignmentY() corresponding setter methods, you can set
a component's alignment by creating a
subclass and overriding these methods.
Set or get the component's layout
void setLayout(LayoutManager) manager. The layout manager is
LayoutManager getLayout() responsible for sizing and positioning the
components within a container.
Set the ComponentOrientation property
void of this container and all the components
applyComponentOrientation(ComponentOrientation) contained within it. See Setting the
void setComponentOrientation(ComponentOrientation) Container's Orientation for more
information.
Getting Size and Position Information
Method Purpose
int getWidth()
Get the current width or height of the component measured in pixels.
int getHeight()
Dimension getSize() Get the component's current size measured in pixels. When using the one-
Dimension argument version of this method, the caller is responsible for creating the
getSize(Dimension) Dimension instance in which the result is returned.
int getX() Get the current x or y coordinate of the component's origin relative to the
int getY() parent's upper left corner measured in pixels.
Get the bounds of the component measured in pixels. The bounds specify
Rectangle getBounds()
the component's width, height, and origin relative to its parent. When
Rectangle
using the one-argument version of this method, the caller is responsible
getBounds(Rectangle)
for creating the Rectangle instance in which the result is returned.
Gets the current location of the component relative to the parent's upper
Point getLocation()
left corner measured in pixels. When using the one-argument version of
Point getLocation(Point)
getLocation method, the caller is responsible for creating the Point
520
instance in which the result is returned.
Point
Returns the position relative to the upper left corner of the screen.
getLocationOnScreen()
Insets getInsets() Get the size of the component's border.
Specifying Absolute Size and Position
(see Doing Without a Layout Manager (Absolute Positioning) for more information)
Method Purpose
void setLocation(int, Set the location of the component, in pixels, relative to the parent's upper
int) left corner. The two int arguments specify x and y, in that order. Use these
void setLocation(Point) methods to position a component when you are not using a layout manager.
void setSize(int, int) Set the size of the component measured in pixels. The two int arguments
void specify width and height, in that order. Use these methods to size a
setSize(Dimension) component when you are not using a layout manager.
void setBounds(int, int, Set the size and location relative to the parent's upper left corner, in pixels,
int, int) of the component. The four int arguments specify x, y, width, and height,
void in that order. Use these methods to position and size a component when
setBounds(Rectangle) you are not using a layout manager.
Swing text components display text and optionally allow the user to edit the text. Programs need text
components for tasks ranging from the straightforward (enter a word and press Enter) to the complex
(display and edit styled text with embedded images in an Asian language).
Swing provides six text components, along with supporting classes and interfaces that meet even the
most complex text requirements. In spite of their different uses and capabilities, all Swing text
components inherit from the same superclass, JTextComponent, which provides a highly-
configurable and powerful foundation for text manipulation.
521
The following picture shows an application called TextSamplerDemo that uses each Swing text
component.
Try this:
1. Click the Launch button to run TextSamplerDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.
2. Type some text in the text field and press Enter. Do the same in the password field.
The label beneath the fields is updated when you press Enter.
3. Try entering valid and invalid dates into the formatted text field. Note that when you
press Enter the label beneath the fields is updated only if the date is valid.
522
4. Select and edit text in the text area and the text pane. Use keyboard bindings, Ctrl-X,
Ctrl-C, and Ctrl-V, to cut, copy, and paste text, respectively.
5. Try to edit the text in the editor pane, which has been made uneditable with a call to
setEditable.
6. Look in the text pane to find an example of an embedded component and an
embedded icon.
The TextSamplerDemo example uses the text components in very basic ways. The following table
tells you more about what you can do with each kind of text component.
This Tutorial provides information about the foundation laid by the JTextComponent class and tells
you how to accomplish some common text-related tasks. Because the JTextComponent class and its
subclasses have too many features to be completely described in this Tutorial, please visit the Swing
and AWT forum at java.net for help and information.
523
A model, known as a document, that manages the component's content.
A view, which displays the component on screen.
A controller, known as an editor kit, that reads and writes text and implements editing
capabilities with actions.
Support for infinite undo and redo.
A pluggable caret and support for caret change listeners and navigation filters.
See the example called TextComponentDemo to explore these capabilities. Although the
TextComponentDemo example contains a customized instance of JTextPane, the capabilities
discussed in this section are inherited by all JTextComponent subclasses.
The upper text component is the customized text pane. The lower text component is an instance of
JTextArea, which serves as a log that reports all changes made to the contents of the text pane. The
status line at the bottom of the window reports either the location of the selection or the position of
the caret, depending on whether text is selected.
Try this:
1. Click the Launch button to run TextComponentDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
2. Use the mouse to select text and place the cursor in the text pane. Information about
the selection and cursor is displayed at the bottom of the window.
3. Enter text by typing on the keyboard. You can move the caret around using the arrow
keys on the keyboard or the four emacs key bindings: Ctrl-B (backward one
character), Ctrl-F (forward one character), Ctrl-N (down one line), and Ctrl-P (up one
line).
4. Open the Edit menu, and use its menu items to edit text in the text pane. Make a
selection in the text area at the bottom of the window. Because the text area is not
524
editable, only some of the Edit menu's commands, like copy-to-clipboard, work. It is
important to note though, that the menu operates on both text components.
5. Use the items in the Style menu to apply different styles to the text in the text pane.
Using the TextComponentDemo example as a reference point, this section covers the following
topics:
All Swing text components support standard editing commands such as cut, copy, paste, and insert
characters. Each editing command is represented and implemented by an Action object. (To learn
more about actions see How to Use Actions.) Actions allow you to associate a command with a GUI
component, such as a menu item or button, and therefore build a GUI around a text component.
You can invoke the getActions method on any text component to receive an array containing all
actions supported by this component. It is also possible to load the array of actions into a HashMap so
your program can retrieve an action by name. Here is the code from the TextComponentDemo
example that takes the actions from the text pane and loads them into a HashMap.
The following code shows how the cut menu item is created and associated with the action of
removing text from the text component.
525
editing and is the superclass for all the editor kits provided by Swing. So its capabilities are available
to all text components unless thay are overridden by a customization.
For efficiency, text components share actions. The Action object returned by
getActionByName(DefaultEditorKit.cutAction) is shared by the uneditable JTextArea at the
bottom of the window. This sharing characteristic has two important ramifications:
Generally, you should not modify Action objects you get from editor kits. If you do,
the changes affect all text components in your program.
Action objects can operate on other text components in the program, sometimes more
than you intended. In this example, even though it is not editable, the JTextArea
shares actions with the JTextPane. (Select some text in the text area, then choose the
cut-to-clipboard menu item. You will hear a beep because the text area is not
editable.) If you do not want to share, instantiate the Action object yourself.
DefaultEditorKit defines a number of useful Action subclasses.
Here is the code that creates the Style menu and puts the Bold menu item in it:
protected JMenu createStyleMenu() {
JMenu menu = new JMenu("Style");
In addition to associating an action with a GUI component, you can also associate an action with a
key stroke by using a text component's input map. Input maps are described in How to Use Key
Bindings.
The text pane in the TextComponentDemo example supports four key bindings not provided by
default.
The following code adds the Ctrl-B key binding to the text pane. The code for adding the other three
bindings listed above is similar.
InputMap inputMap = textPane.getInputMap();
First, the code obtains the text component's input map. Next, it finds a KeyStroke object
representing the Ctrl-B key sequence. Finally, the code binds the key stroke to the Action that
moves the cursor backward.
526
Implementing Undo and Redo
A document notifies interested listeners whenever an undoable edit occurs on the document content.
An important step in implementing undo and redo is to register an undoable edit listener on the
document of the text component. The following code adds an instance of MyUndoableEditListener
to the text pane's document:
doc.addUndoableEditListener(new MyUndoableEditListener());
The undoable edit listener used in our example adds the edit to the undo manager's list:
protected class MyUndoableEditListener
implements UndoableEditListener {
public void undoableEditHappened(UndoableEditEvent e) {
//Remember the edit and update the menus
undo.addEdit(e.getEdit());
undoAction.updateUndoState();
redoAction.updateRedoState();
}
}
Note that this method updates two objects: undoAction and redoAction. These are the action
objects attached to the Undo and Redo menu items, respectively. The next step shows you how to
create the menu items and how to implement the two actions. For general information about
undoable edit listeners and undoable edit events, see How to Write an Undoable Edit Listener.
Note: By default, each undoable edit undoes a single character entry. It is possible with some effort
to group edits so that a series of key strokes is combined into one undoable edit. Grouping edits in
this manner would require you to define a class that intercepts undoable edit events from the
document, combining them if appropriate and forwarding the results to your undoable edit listener.
527
The undo and redo actions are implemented by custom AbstractAction subclasses: UndoAction
and RedoAction, respectively. These classes are inner classes of the example's primary class.
When the user invokes the undo command, the actionPerformed method of the UndoAction class
is called:
Similarly, when the user invokes the redo command, the actionPerformed method of the
RedoAction class is called:
Much of the code in the UndoAction and RedoAction classes is dedicated to enabling and disabling
the actions as appropriate of the current state, and changing the names of the menu items to reflect
the edit to be undone or redone.
Note: The implementation of undo and redo in the TextComponentDemo example was taken from
the NotePad demo that comes with the JDK software. Many programmers will also be able to copy
this implementation of undo/redo without modification.
Like other Swing components, a text component separates its data (known as the model) from its
view of the data. If you are not yet familiar with the model-view split used by Swing components,
refer to Using Models.
A text component's model is known as a document and is an instance of a class that implements the
Document interface. A document provides the following services for a text component:
Contains the text. A document stores the textual content in Element objects, which
can represent any logical text structure, such as paragraphs, or text runs that share
528
styles. We do not describe Element objects here. However, The Swing Connection
has at least one article on the subject.
Provides support for editing the text through the remove and insertString methods.
Notifies document listeners and undoable edit listeners of changes to the text.
Manages Position objects, which track a particular location within the text even as
the text is modified.
Allows you to obtain information about the text, such as its length, and segments of
the text as a string.
The Swing text package contains a subinterface of Document, StyledDocument, that adds support
for marking up the text with styles. One JTextComponent subclass, JTextPane, requires that its
document be a StyledDocument rather than merely a Document.
The javax.swing.text package provides the following hierarchy of document classes, which
implement specialized documents for the various JTextComponent subclasses:
A PlainDocument is the default document for text fields, password fields, and text areas.
PlainDocument provides a basic container for text where all the text is displayed in the same font.
Even though an editor pane is a styled text component, it uses an instance of PlainDocument by
default. The default document for a standard JTextPane is an instance of DefaultStyledDocument
— a container for styled text in no particular format. However, the document instance used by any
particular editor pane or text pane depends on the type of content bound to it. If you use the setPage
method to load text into an editor pane or text pane, the document instance used by the pane might
change. Refer to How to Use Editor Panes and Text Panes for details.
Although you can set the document of a text component, it is usually easier to allow it to set
automatically, and if necessary, use a document filter to change how the text component's data is set.
You can implement certain customizations either by installing a document filter or by replacing a
text component's document with one of your own. For example, the text pane in the
TextComponentDemo example has a document filter that limits the number of characters the text pane
can contain.
529
The TextComponentDemo application has a document filter, DocumentSizeFilter, that limits the
number of characters that the text pane can contain. Here is the code that creates the filter and
attaches it to the text pane's document:
The code for replace is similar. The FilterBypass parameter to the methods defined by the
DocumentFilter class is simply an object that enables the document to be updated in a thread-safe
way.
Because the preceding document filter is concerned with additions to the document's data, it only
overrides the insertString and replace methods. Most document filters would override
DocumentFilter's remove method as well.
You can register two different types of listeners on a document: document listeners and undoable
edit listeners. This subsection describes document listeners. For information about undoable edit
listeners, refer to Implementing Undo and Redo.
A document notifies registered document listeners of changes to the document. Use a document
listener to create a reaction when text is inserted or removed from a document, or when the text style
changes.
The TextComponentDemo program uses a document listener to update the change log whenever a
change is made to the text pane. The following line of code registers an instance of the
MyDocumentListener class as a listener on the text pane's document:
doc.addDocumentListener(new MyDocumentListener());
530
Here is the implementation of the MyDocumentListener class:
protected class MyDocumentListener implements DocumentListener {
public void insertUpdate(DocumentEvent e) {
displayEditInfo(e);
}
public void removeUpdate(DocumentEvent e) {
displayEditInfo(e);
}
public void changedUpdate(DocumentEvent e) {
displayEditInfo(e);
}
private void displayEditInfo(DocumentEvent e) {
Document document = (Document)e.getDocument();
int changeLength = e.getLength();
changeLog.append(e.getType().toString() + ": "
+ changeLength + " character"
+ ((changeLength == 1) ? ". " : "s. ")
+ " Text length = " + document.getLength()
+ "." + newline);
}
}
The listener implements three methods for handling three different types of document events:
insertion, removal, and style changes. StyledDocument instances can fire all three types of events.
PlainDocument instances fire events only for insertion and removal. For general information about
document listeners and document events, see How to Write a Document Listener.
Remember that the document filter for this text pane limits the number of characters allowed in the
document. If you try to add more text than the document filter allows, the document filter blocks the
change and the listener's insertUpdate method is not called. Document listeners are notified of
changes only if the change has already occurred.
You may want to change the document's text within a document listener. However, you should
never modify the contents of a text component from within a document listener. If you do, the
program will likely deadlock. Instead, you can use a formatted text field or provide a document filter.
The TextComponentDemo program uses a caret listener to display the current position of the caret or,
if text is selected, the extent of the selection.
The caret listener class in this example is a JLabel subclass. Here is the code that creates the caret
listener label and makes it a caret listener of the text pane:
531
setText("caret: text position: " + dot +
", view location = [" +
caretCoords.x + ", " + caretCoords.y + "]" +
newline);
} catch (BadLocationException ble) {
setText("caret: text position: " + dot + newline);
}
} else if (dot < mark) {
setText("selection from: " + dot + " to " + mark + newline);
} else {
setText("selection from: " + mark + " to " + dot + newline);
}
}
As you can see, this listener updates its text label to reflect the current state of the caret or selection.
The listener gets the information to display from the caret event object. For general information
about caret listeners and caret events, see How to Write a Caret Listener.
As with document listeners, a caret listener is passive. It reacts to changes in the caret or in the
selection, but does not change the caret or the selection itself. If you want to change the caret or
selection, use a navigation filter or a custom caret.
Implementing a navigation filter is similar to implementing a document filter. First, write a subclass
of NavigationFilter. Then attach an instance of the subclass to a text component with the
setNavigationFilter method.
You might create a custom caret to customize the appearance of a caret. To create a custom caret,
write a class that implements the Caret interface — perhaps by extending the DefaultCaret class.
Then provide an instance of your class as an argument to the setCaret method on a text component.
Text components use an EditorKit to tie the various pieces of the text component together. The
editor kit provides the view factory, document, caret, and actions. An editor kit also reads and writes
documents of a particular format. Although all text components use editor kits, some components
hide theirs. You cannot set or get the editor kit used by a text field or text area. Editor panes and text
panes provide the getEditorKit method to get the current editor kit and the setEditorKit method
to change it.
For all components, the JTextComponent class provides the API for you to indirectly invoke or
customize some editor kit capabilities. For example, JTextComponent provides the read and write
methods, which invoke the editor kit's read and write methods. JTextComponent also provides a
method, getActions, which returns all of the actions supported by a component.
DefaultEditorKit
Reads and writes plain text, and provides a basic set of editing commands. Details
about how the text system treats newlines can be found in the DefaultEditorKit
API documentation. Briefly, the '\n' character is used internally, but the document or
platform line separators are used when writing files. All the other editor kits are
descendants of the DefaultEditorKit class.
StyledEditorKit
532
Reads and writes styled text, and provides a minimal set of actions for styled text.
This class is a subclass of DefaultEditorKit and is the editor kit used by JTextPane
by default.
HTMLEditorKit
Reads, writes, and edits HTML. This is a subclass of StyledEditorKit.
Each of the editor kits listed above has been registered with the JEditorPane class and associated
with the text format that the kit reads, writes, and edits. When a file is loaded into an editor pane, the
pane checks the format of the file against its registered kits. If a registered kit is found that supports
that file format, the pane uses the kit to read the file, display, and edit it. Thus, the editor pane
effectively transforms itself into an editor for that text format. You can extend JEditorPane to
support your own text format by creating an editor kit for it, and then using JEditorPane's
registerEditorKitForContentType to associate your kit with your text format.
The JComponent Class describes the API that text components inherit from JComponent. For
information about the API related to specific text components, see the how-to page for that
component: text field, password field, formatted text field, text area, or editor pane and text pane.
For complete details about the text API, see the API documentation for JTextComponent and for the
various classes and interfaces in the text package.
Setting Attributes
Manipulating the Selection
Converting Positions Between the Model and the View
Text Editing Commands
Classes and Interfaces That Represent Documents
Working With Documents
Manipulating Carets and Selection Highlighters
Reading and Writing Text
Setting Attributes
These methods are defined in the JTextComponent class.
Method Description
void setEditable(boolean) Sets or indicates whether the user can edit the text in the text
boolean isEditable() component.
Sets or gets the dragEnabled property, which must be true to enable
void setDragEnabled(boolean)
drag handling on this component. The default value is false. See
boolean getDragEnabled()
Drag and Drop for more details.
void
Sets or gets the color used to display text when the text component is
setDisabledTextColor(Color)
disabled.
Color getDisabledTextColor()
void setMargin(Insets) Sets or gets the margin between the text and the text component's
533
Insets getMargin() border.
Manipulating the Selection
These methods are defined in the JTextComponent class.
Method Description
String getSelectedText() Gets the currently selected text.
void selectAll()
Selects all text or selects text within a start and end range.
void select(int, int)
void setSelectionStart(int)
void setSelectionEnd(int)
Sets or gets the extent of the current selection by index.
int getSelectionStart()
int getSelectionEnd()
void setSelectedTextColor(Color)
Sets or gets the color of selected text.
Color getSelectedTextColor()
void setSelectionColor(Color)
Sets or gets the background color of selected text.
Color getSelectionColor()
Converting Positions Between the Model and the View
These methods are defined in the JTextComponent class.
Method Description
Converts the specified point in the view coordinate system to a position
int viewToModel(Point)
within the text.
Rectangle Converts the specified position within the text to a rectangle in the view
modelToView(int) coordinate system.
Text Editing Commands
Class or Method Description
void cut()
void copy()
void paste() Cuts, copies, and pastes text using the system clipboard, or replaces the
void selected text with the string specified by an argument, respectively.
replaceSelection(String)
(in JTextComponent)
Provides a text component's view factory, document, caret, and actions, as
EditorKit
well as reading and writing documents of a particular format.
A concrete subclass of EditorKit that provides the basic text editing
DefaultEditorKit
capabilities.
A subclass of Default EditorKit that provides additional editing
StyledEditorKit
capabilities for styled text.
String xxxxAction The names of all the actions supported by the default editor kit. See
(in DefaultEditorKit) Associating Text Actions with Menus and Buttons.
BeepAction
CopyAction
CutAction
DefaultKeyTypedAction Inner classes that implement various text editing commands.
InsertBreakAction
InsertContentAction
InsertTabAction
534
PasteAction
(in DefaultEditorKit)
AlignmentAction
BoldAction
FontFamilyAction
FontSizeAction
ForegroundAction Inner classes that implement various editing commands for styled text.
ItalicAction
StyledTextAction
UnderlineAction
(in StyledEditorKit)
Action[] getActions() Gets the actions supported by this component. This method gets the array
(in JTextComponent) of actions from the editor kit if one is used by the component.
InputMap getInputMap() Gets the input map that binds key strokes to actions. See Associating Text
(in JComponent) Actions with Key Strokes.
void put(KeyStroke, Binds the specified key to the specified action. You generally specify the
Object) action by its name, which for standard editing actions is represented by a
(in InputMap) string constant such as DefaultEditorKit.backwardAction.
Classes and Interfaces That Represent Documents
Interface or Class Description
An interface that defines the API that must be implemented by all
Document
documents.
An abstract superclass implementation of the Document interface. This is
AbstractDocument
the superclass for all documents provided by the Swing text package.
A class that implements the Document interface. This is the default
document for the plain text components (text field, password field, and text
PlainDocument
area). Additionally, this class is used by the editor panes and text panes
when loading plain text or text of an unknown format.
A Document subinterface. Defines the API that must be implemented by
StyledDocument documents that support styled text. JTextPane requires that its document
be of this type.
A class that implements the StyledDocument interface. The default
DefaultStyledDocument
document for JTextPane.
Working With Documents
Class or Method Description
The superclass of all document filters. You
can use a document filter to change what gets
DocumentFilter inserted or removed from a document, without
having to implement a document yourself. See
Implementing a Document Filter.
void setDocumentFilter(DocumentFilter)
Sets the document filter.
(in AbstractDocument)
void setDocument(Document)
Sets or gets the document for a text
Document getDocument()
component.
(in JTextComponent)
Document createDefaultModel() Creates a default PlainDocument model.
535
(in JTextField) Override this method to create a custom
document instead of the default
PlainDocument.
void addDocumentListener(DocumentListener)
Adds or removes a document listener. See
void removeDocumentListener(DocumentListener)
Listening for Changes on a Document.
(in Document)
void
addUndoableEditListener(UndoableEditListener) Adds or removes an undoable edit listener.
void Undoable edit listeners are used in
removeUndoableEditListener(UndoableEditlistener) Implementing Undo and Redo.
(in Document)
int getLength()
Position getStartPosition()
Document methods that return various
Position getEndPosition()
descriptive information about the document.
String getText(int, int)
(in Document)
Object getProperty(Object)
void putProperty(Object, Object)
(in Document) A Document maintains a set of properties that
void setDocumentProperties(Dictionary) you can manipulate with these methods.
Dictionary getDocumentProperties()
(in AbstractDocument)
Manipulating Carets and Selection Highlighters
These methods are defined in the JTextComponent class.
Interface, Class, or Method Description
An interface that defines the API for objects that represent
Caret
an insertion point within documents.
DefaultCaret The default caret used by all text components.
void setCaret(Caret)
Sets or gets the caret object used by a text component.
Caret getCaret()
void setCaretColor(Color)
Sets or gets the color of the caret.
Color getCaretColor()
void setCaretPosition(int)
Sets or gets the current position of the caret within the
void moveCaretPosition(int)
document.
int getCaretPosition()
void addCaretListener(CaretListener)
void Adds or removes a caret listener from a text component.
removeCaretListener(CaretListener)
The superclass for all navigation filters. A navigation filter
NavigationFilter lets you modify caret changes that are about to occur for a
text component.
void
Attaches a navigation filter to a text component.
setNavigationFilter(NavigationFilter)
An interface that defines the API for objects used to
Highlighter
highlight the current selection.
DefaultHighlighter The default highlighter used by all text components.
536
void setHighlighter(Highlighter)
Sets or gets the highlighter used by a text component.
Highlighter getHighlighter()
Reading and Writing Text
Method Description
void read(Reader, Object)
void write(Writer) Reads or writes text.
(in JTextComponent)
void read(Reader, Document, int)
void read(InputStream, Document, int) Reads text from a stream into a document.
(in EditorKit)
void write(Writer, Document, int, int)
void write(OutputStream, Document, int, int) Writes text from a document to a stream.
(in EditorKit)
537
How to Use Trees
Any applet that contains Swing components must be implemented with a subclass of JApplet.
Here's a Swing version of one of the applets that helped make Java famous — an animation applet
that (in its most well known configuration) shows our mascot Duke doing cartwheels:
Note: If you don't see the applet running above, you need to install release 6 of the JDK. You can
find the main source code for this applet in TumbleItem.java.
Because JApplet is a top-level Swing container, each Swing applet has a root pane. The most
noticeable effects of the root pane's presence are support for adding a menu bar and the need to use a
content pane.
As described in Using Top-Level Containers, each top-level container such as a JApplet has a single
content pane. The content pane makes Swing applets different from regular applets in the following
ways:
You add components to a Swing applet's content pane, not directly to the applet.
Adding Components to the Content Pane shows you how.
You set the layout manager on a Swing applet's content pane, not directly on the
applet.
The default layout manager for a Swing applet's content pane is BorderLayout. This
differs from the default layout manager for Applet, which is FlowLayout.
You should not put painting code directly in a JApplet object. See Performing
Custom Painting for examples of how to perform custom painting in applets.
Threads in Applets
Swing components should be created, queried, and manipulated on the event-dispatching thread, but
browsers don't invoke applet "milestone" methods from that thread. For this reason, the milestone
538
methods — init, start, stop, and destroy — should use the SwingUtilities method
invokeAndWait (or, if appropriate, invokeLater) so that code that refers to the Swing components
is executed on the event-dispatching thread. More information about these methods and the event-
dispatching thread is in Concurrency in Swing.
The invokeLater method is not appropriate for this implementation because it allows init to return
before initialization is complete, which can cause applet problems that are difficult to debug.
The init method in TumbleItem is more complex, as the following code shows. Like the first
example, this init method implementation uses SwingUtilities.invokeAndWait to execute the
GUI creation code on the event-dispatching thread. This init method sets up a Swing timer to fire
action events the update the animation. Also, init uses javax.swing.SwingWorker to create a
background task that loads the animation image files, letting the applet present a GUI right away,
without waiting for all resources to be loaded.
539
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}).execute();
}
You can find the applet's source code in TumbleItem.java. To find all the files required for the
applet, see the example index.
The Applet class provides the getImage method for loading images into an applet. The getImage
method creates and returns an Image object that represents the loaded image. Because Swing
components use Icons rather than Images to refer to pictures, Swing applets tend not to use
getImage. Instead Swing applets create instances of ImageIcon — an icon loaded from an image
file. ImageIcon comes with a code-saving benefit: it handles image tracking automatically. Refer to
How to Use Icons for more information.
The animation of Duke doing cartwheels requires 17 different pictures. The applet uses one
ImageIcon per picture and loads them in its init method. Because images can take a long time to
load, the icons are loaded in a separate thread implemented by a SwingWorker object. Here's the
code:
540
}).execute();
}
...
protected ImageIcon loadImage(int imageNum) {
String path = dir + "/T" + imageNum + ".gif";
int MAX_IMAGE_SIZE = 2400; //Change this to the size of
//your biggest image, in bytes.
int count = 0;
BufferedInputStream imgStream = new BufferedInputStream(
this.getClass().getResourceAsStream(path));
if (imgStream != null) {
byte buf[] = new byte[MAX_IMAGE_SIZE];
try {
count = imgStream.read(buf);
imgStream.close();
} catch (java.io.IOException ioe) {
System.err.println("Couldn't read stream from file: " + path);
return null;
}
if (count <= 0) {
System.err.println("Empty file: " + path);
return null;
}
return new ImageIcon(Toolkit.getDefaultToolkit().createImage(buf));
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
The loadImage method loads the image for the specified frame of animation. It uses the
getResourceAsStream method rather than the usual getResource method to get the images. The
resulting code isn't pretty, but getResourceAsStream is more efficient than getResource for
loading images from JAR files into applets that are executed using Java Plug-inTM software. For
further details, see Loading Images Into Applets.
The recommended way to include an applet in an HTML page is using the APPLET tag. Here's the
APPLET tag for the cartwheeling Duke applet:
<applet code="TumbleItem.class"
codebase="examples/"
archive="tumbleClasses.jar, tumbleImages.jar"
width="600" height="95">
<param name="maxwidth" value="120">
<param name="nimgs" value="17">
<param name="offset" value="-57">
<param name="img" value="images/tumble">
The next table lists the interesting methods that JApplet adds to the applet API. They give you
access to features provided by the root pane. Other methods you might use are defined by the
541
Component and Applet classes. See Component Methods for a list of commonly used Component
methods, and Applets for help in using Applet methods.
Method Purpose
void setContentPane(Container) Set or get the applet's content pane. The content pane contains the
Container getContentPane() applet's visible GUI components and should be opaque.
Create, set, or get the applet's root pane. The root pane manages
void setRootPane(JRootPane)
the interior of the applet including the content pane, the glass
JRootPane getRootPane()
pane, and so on.
void setJMenuBar(JMenuBar) Set or get the applet's menu bar to manage a set of menus for the
JMenuBar getJMenuBar() applet.
void setGlassPane(Component) Set or get the applet's glass pane. You can use the glass pane to
Component getGlassPane() intercept mouse events.
void Set or get the applet's layered pane. You can use the applet's
setLayeredPane(JLayeredPane) layered pane to put components on top of or behind other
JLayeredPane getLayeredPane() components.
Applet Example
This table shows examples of Swing applets and where those examples are described.
Example Where Described Notes
TumbleItem This page An animation applet
542
JCheckBox and JRadioButton. Can be
instantiated or subclassed to create two-state
buttons.
Note: If you want to collect a group of buttons into a row or column, then you should check out tool
bars.
First, this section explains the basic button API that AbstractButton defines — and thus all Swing
buttons have in common. Next, it describes the small amount of API that JButton adds to
AbstractButton. After that, this section shows you how to use specialized API to implement check
boxes and radio buttons.
Try this:
1. Click the Launch button to run the Button Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
As the ButtonDemo example shows, a Swing button can display both text and an image. In
ButtonDemo, each button has its text in a different place, relative to its image. The underlined letter
in each button's text shows the mnemonic — the keyboard alternative — for each button. In most
look and feels, the user can click a button by pressing the Alt key and the mnemonic. For example,
Alt-M would click the Middle button in ButtonDemo.
When a button is disabled, the look and feel automatically generates the button's disabled
appearance. However, you could provide an image to be substituted for the normal image. For
example, you could provide gray versions of the images used in the left and right buttons.
How you implement event handling depends on the type of button you use and how you use it.
Generally, you implement an action listener, which is notified every time the user clicks the button.
For check boxes you usually use an item listener, which is notified when the check box is selected or
deselected.
543
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.
Ordinary buttons — JButton objects — have just a bit more functionality than the AbstractButton
class provides: You can make a JButton be the default button.
544
At most one button in a top-level container can be the default button. The default button typically has
a highlighted appearance and acts clicked whenever the top-level container has the keyboard focus
and the user presses the Return or Enter key. Here is a picture of a dialog, implemented in the
ListDialog example, in which the Set button is the default button:
You set the default button by invoking the setDefaultButton method on a top-level container's root
pane. Here is the code that sets up the default button for the ListDialog example:
The JCheckBox class provides support for check box buttons. You can also put check boxes in
menus, using the JCheckBoxMenuItem class. Because JCheckBox and JCheckBoxMenuItem inherit
from AbstractButton, Swing check boxes have all the usual button characteristics, as discussed
earlier in this section. For example, you can specify images to be used in check boxes.
Check boxes are similar to radio buttons but their selection model is different, by convention. Any
number of check boxes in a group — none, some, or all — can be selected. A group of radio buttons,
on the other hand, can have only one button selected.
Here is a picture of an application that uses four check boxes to customize a cartoon:
Try this:
545
1. Click the Launch button to run the CheckBox Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
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.
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...
546
...
updatePicture();
}
Radio buttons are groups of buttons in which, by convention, only one button at a time can be
selected. The Swing release supports radio buttons with the JRadioButton and ButtonGroup
classes. To put a radio button in a menu, use the JRadioButtonMenuItem class. Other ways of
displaying one-of-many choices are combo boxes and lists. Radio buttons look similar to check
boxes, but, by convention, check boxes place no limits on how many items can be selected at a time.
Because JRadioButton inherits from AbstractButton, Swing radio buttons have all the usual
button characteristics, as discussed earlier in this section. For example, you can specify the image
displayed in a radio button.
Here is a picture of an application that uses five radio buttons to let you choose which kind of pet is
displayed:
Try this:
1. Click the Launch button to run the RadioButton Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
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
547
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.
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.
548
The following tables list the commonly used button-related API. Other methods you might call, such
as setFont and setForeground, are listed in the API tables in The JComponent Class.
549
setHorizontalAlignment(int) AbstractButton class allows any one of the following values for
void setVerticalAlignment(int) horizontal alignment: RIGHT, LEFT, CENTER (the default), LEADING,
int getHorizontalAlignment() and TRAILING. For vertical alignment: TOP, CENTER (the default),
int getVerticalAlignment() and BOTTOM.
void Set or get where the button's text should be placed, relative to the
setHorizontalTextPosition(int)
button's image. The AbstractButton class allows any one of the
void
following values for horizontal position: LEFT, CENTER, RIGHT,
setVerticalTextPosition(int)
LEADING, and TRAILING (the default). For vertical position: TOP,
int getHorizontalTextPosition()
CENTER (the default), and BOTTOM.
int getVerticalTextPosition()
void setMargin(Insets) Set or get the number of pixels between the button's border and its
Insets getMargin() contents.
void setFocusPainted(boolean) Set or get whether the button should look different when it has the
boolean isFocusPainted() focus.
void
setBorderPainted(boolean) Set or get whether the border of the button should be painted.
boolean isBorderPainted()
void setIconTextGap(int) Set or get the amount of space between the text and the icon
int getIconTextGap() displayed in this button.
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 accepts a character
void setMnemonic(int)
argument; however, the Swing team recommends that you use
char getMnemonic()
an int argument instead, specifying a KeyEvent.VK_X
constant.
void Set or get a hint as to which character in the text should be
setDisplayedMnemonicIndex(int) decorated to represent the mnemonic. Note that not all look and
int getDisplayedMnemonicIndex() feels may support this.
void setActionCommand(String)
Set or get the name of the action performed by the button.
String getActionCommand()
void
addActionListener(ActionListener) Add or remove an object that listens for action events fired by
ActionListener the button.
removeActionListener()
void addItemListener(ItemListener) Add or remove an object that listens for item events fired by
ItemListener removeItemListener() the button.
void setSelected(boolean) Set or get whether the button is selected. Makes sense only for
boolean isSelected() buttons that have on/off state, such as check boxes.
Programmatically perform a "click". The optional argument
void doClick()
specifies the amount of time (in milliseconds) that the button
void doClick(int)
should look pressed.
void Set or get the amount of time (in milliseconds) required
setMultiClickThreshhold(long) between mouse press events for the button to generate
long getMultiClickThreshhold() corresponding action events.
Check Box Constructors
550
Constructor Purpose
JCheckBox(Action)
JCheckBox(String) Create a JCheckBox instance. The string argument specifies the
JCheckBox(String, boolean) text, if any, that the check box should display. Similarly, the Icon
JCheckBox(Icon) argument specifies the image that should be used instead of the
JCheckBox(Icon, boolean) look and feel's default check box image. Specifying the boolean
JCheckBox(String, Icon) argument as true initializes the check box to be selected. If the
JCheckBox(String, Icon, boolean argument is absent or false, then the check box is initially
boolean) unselected.
JCheckBox()
JCheckBoxMenuItem(Action)
JCheckBoxMenuItem(String)
JCheckBoxMenuItem(String,
boolean) Create a JCheckBoxMenuItem instance. The arguments are
JCheckBoxMenuItem(Icon) interpreted in the same way as the arguments to the JCheckBox
JCheckBoxMenuItem(String, constructors, except that any specified icon is shown in addition to
Icon) the normal check box icon.
JCheckBoxMenuItem(String,
Icon, boolean)
JCheckBoxMenuItem()
Radio Button Constructors
Constructor Purpose
JRadioButton(Action)
Create a JRadioButton instance. The string argument specifies
JRadioButton(String)
the text, if any, that the radio button should display. Similarly, the
JRadioButton(String, boolean)
Icon argument specifies the image that should be used instead of
JRadioButton(Icon)
the look and feel's default radio button image. Specifying the
JRadioButton(Icon, boolean)
boolean argument as true initializes the radio button to be
JRadioButton(String, Icon)
selected, subject to the approval of the ButtonGroup object. If the
JRadioButton(String, Icon,
boolean) boolean argument is absent or false, then the radio button is
JRadioButton() initially unselected.
JRadioButtonMenuItem(Action)
JRadioButtonMenuItem(String) Create a JRadioButtonMenuItem instance. The arguments are
JRadioButtonMenuItem(Icon) interpreted in the same way as the arguments to the JRadioButton
JRadioButtonMenuItem(String, constructors, except that any specified icon is shown in addition to
Icon) the normal radio button icon.
JRadioButtonMenuItem()
Toggle Button Constructors
Constructor Purpose
JToggleButton(Action) Create a JToggleButton instance, which is similar to a JButton, but
JToggleButton(String)
with two states. Normally, you use a JRadioButton or JCheckBox
JToggleButton(String,
instead of directly instantiating JToggleButton, but JToggleButton can
boolean)
be useful when you do not want the typical radio button or check box
JToggleButton(Icon)
appearance. The string argument specifies the text, if any, that the toggle
JToggleButton(Icon,
button should display. Similarly, the Icon argument specifies the image
boolean)
that should be used. Specifying the boolean argument as true initializes
JToggleButton(String,
the toggle button to be selected. If the boolean argument is absent or
Icon)
false, then the toggle button is initially unselected.
JToggleButton(String,
551
Icon, boolean)
JToggleButton()
Commonly Used Button Group Constructors/Methods
Constructor or Method Purpose
ButtonGroup() Create a ButtonGroup instance.
void add(AbstractButton)
void Add a button to the group, or remove a button from the group.
remove(AbstractButton)
public ButtonGroup Get the ButtonGroup, if any, that controls a button. For example:
getGroup() ButtonGroup group =
(in DefaultButtonModel) ((DefaultButtonModel)button.getModel()).getGroup();
public ButtonGroup Clears the state of selected buttons in the ButtonGroup. None of the
clearSelection() buttons in the ButtonGroup are selected .
The following examples use buttons. Also see Examples that Use Tool Bars, which lists programs
that add JButton objects to JToolBars.
Example Where Described Notes
How to Use the Uses mnemonics and icons. Specifies the button text
ButtonDemo Common Button position, relative to the button icon. Uses action
API commands.
ButtonHtmlDemo
Using HTML in A version of ButtonDemo that uses HTML formatting in
Swing Components its buttons.
ListDialog
How to Use JButton Implements a dialog with two buttons, one of which is the
Features default button.
Has "Show it" buttons whose behavior is tied to the state
How to Make
DialogDemo of radio buttons. Uses sizable, though anonymous, inner
Dialogs
classes to implement the action listeners.
ProgressBarDemo
How to Monitor Implements a button's action listener with a named inner
Progress class.
CheckBoxDemo
How to Use Check Uses check box buttons to determine which of 16 images
Boxes it should display.
Uses check box menu items to set the state of the
ActionDemo How to Use Actions
program.
RadioButtonDemo
How to Use Radio Uses radio buttons to determine which of five images it
Buttons should display.
DialogDemo
How to Make Contains several sets of radio buttons, which it uses to
Dialogs determine which dialog to bring up.
Contains radio button menu items and check box menu
MenuDemo How to Use Menus
items.
ColorChooserDemo2
How to Use Color The crayons in CrayonPanel are implemented as toggle
Choosers buttons.
How to Use Scroll
ScrollDemo The cm button is a toggle button.
Panes
552
How to Use the ButtonGroup Component
The ButtonGroup component manages the selected/unselected state for a set of buttons. For the
group, the ButtonGroup instance guarantees that only one button can be selected at a time.
You can use ButtonGroup with any set of objects that inherit from AbstractButton. Typically a
button group contains instances of JRadioButton, JRadioButtonMenuItem, or JToggleButton. It
would not make sense to put an instance of JButton or JMenuItem in a button group because
JButton and JMenuItem do not implement the select/deselect button state.
In general, you will typically follow these steps to write code that uses a ButtonGroup component.
1. Subclass JFrame
2. Call ContextPane together with a layout manager
3. Declare and configure a set of radio buttons or toggle buttons
4. Instantiate a ButtonGroup object
5. Call the add method on that buttongroup object in order to add each button to the group.
For details and a code example, see How to Use Radio Buttons. It shows how to use a ButtonGroup
component to group a set of RadioButtons set into a JPanel.
ButtonGroup Examples
The following demonstration application uses the ButtonGroup component to group radio buttons
displaying on a Window.
553
Buttons should display.
Here is a picture of an application that uses a color chooser to set the text color in a banner:
Try this:
Click the Launch button to run the ColorChooser Demo using Java™ Web
Start (download JDK 6). Alternatively, to compile and run the example
yourself, consult the example index.
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
554
preview panel. The three tabs in the tabbed pane select chooser panels. The preview panel below the
tabbed pane displays the currently selected color.
Here is the code from the example that creates a JColorChooser instance and adds it to a container:
A color chooser uses an instance of ColorSelectionModel to contain and manage the current
selection. The color selection model fires a change event whenever the user changes the color in the
color chooser. The example program registers a change listener with the color selection model so that
it can update the banner at the top of the window. The following code registers and implements the
change listener:
tcc.getSelectionModel().addChangeListener(this);
. . .
public void stateChanged(ChangeEvent e) {
Color newColor = tcc.getColor();
banner.setForeground(newColor);
}
See How to Write a Change Listener for general information about change listeners and change
events.
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.
Now let's turn our attention to ColorChooserDemo2, a modified version of the previous demo
program that uses more of the JColorChooser API.
555
Try this:
Click the Launch button to run the ColorChooser Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
This program customizes the banner text color chooser in these ways:
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 background color.
The JColorChooser class provides two class methods to make it easy to use a color chooser in a
dialog. ColorChooserDemo2 uses one of these methods, showDialog, to display the background
556
color chooser when the user clicks the Show Color Chooser... button. Here is the single line of code
from the example that brings up the background color chooser in a dialog:
Color newColor = JColorChooser.showDialog(
ColorChooserDemo2.this,
"Choose Background Color",
banner.getBackground());
The first argument is the parent for the dialog, the second is the dialog 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 is the code from the example
that updates the banner 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.
By default, the color chooser displays a preview panel. ColorChooserDemo2 removes the text color
chooser's preview panel with this line of code:
tcc.setPreviewPanel(new JPanel());
This effectively removes the preview panel because a plain JPanel has no size and no default view.
To set the preview panel back to the default, use null as the argument to setPreviewPanel.
To provide a custom preview panel, you also use setPreviewPanel. The component you pass into
the method should inherit from JComponent, specify a reasonable size, and provide a customized
view of the current color. To get notified when the user changes the color in the color chooser, the
preview panel must register as a change listener on the color chooser's color selection model as
described previously.
You can extend the default color chooser by adding chooser panels of your own design with
addChooserPanel, or you can limit it by removing chooser panels with removeChooserPanel.
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 is the call to
setChooserPanels from that example:
557
//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.
void buildChooser()
Creates the GUI that comprises the chooser panel. The example creates four toggle
buttons — one for each crayon — and adds them to the chooser panel.
void updateChooser()
This method is called whenever the chooser panel is displayed. The implementation
of this method selects the toggle button that represents the currently selected color.
public void updateChooser() {
Color color = getColorFromModel();
if (Color.red.equals(color)) {
redCrayon.setSelected(true);
} else if (Color.yellow.equals(color)) {
yellowCrayon.setSelected(true);
} else if (Color.green.equals(color)) {
greenCrayon.setSelected(true);
} else if (Color.blue.equals(color)) {
blueCrayon.setSelected(true);
}
}
String getDisplayName()
Returns the display name of the chooser panel. The name is used on the tab for the
chooser panel. Here is the example getDisplayName method:
public String getDisplayName() {
return "Crayons";
}
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 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 implementation of this method returns
null.
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:
558
Creating and Displaying the Color Chooser
Method or Constructor Purpose
Create a color chooser. The default constructor creates a
JColorChooser() color chooser with an initial color of Color.white. Use the
JColorChooser(Color) second constructor to 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
Color showDialog(Component, String, Component argument is the parent of the dialog, the String
Color) argument specifies the dialog title, and the Color argument
specifies the chooser's initial color.
Create a dialog for the specified color chooser. As with
JDialog createDialog(Component, showDialog, the Component argument is the parent of the
String, dialog and the String argument specifies the dialog title.
boolean, JColorChooser, The other arguments are as follows: the boolean specifies
ActionListener, whether the dialog is modal, the JColorChooser is the color
ActionListener) 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
void setPreviewPanel(JComponent)
panel, use new JPanel() as an argument.
JComponent getPreviewPanel()
To specify the default preview panel, use
null.
void setChooserPanels(AbstractColorChooserPanel[]) Set or get the chooser panels in the color
AbstractColorChooserPanel[] getChooserPanels() chooser.
void addChooserPanel(AbstractColorChooserPanel)
Add a chooser panel to the color chooser or
AbstractColorChooserPanel
remove a chooser panel from it.
removeChooserPanel(AbstractColorChooserPanel)
Set or get the dragEnabled property,
void setDragEnabled(boolean) which must be true to enable drag handling
boolean getDragEnabled() on this component. The default value is
false. See Drag and Drop for more details.
Setting or Getting the Current Color
Method Purpose
Set or get the currently selected color. The three integer
void setColor(Color) version of the setColor method interprets the three
void setColor(int, int, int) integers together as an RGB color. The single integer
void setColor(int) version of the setColor method divides the integer into
Color getColor() four 8-bit bytes and interprets the integer as an RGB
color as follows:
void Set or get the selection model for the color chooser. This
setSelectionModel(ColorSelectionModel) object contains the current selection and fires change
559
ColorSelectionModel events to registered listeners whenever the selection
getSelectionModel() changes.
This table shows the examples that use JColorChooser and where those examples are described.
Where
Example Notes
Described
ColorChooserDemo This section Uses a standard color chooser.
Uses one customized color chooser and one standard color
ColorChooserDemo2 This section
chooser in a dialog created with showDialog.
Shows how to use a color chooser as a custom cell editor
How to Use
TableDialogEditDemo in a table. The color chooser used by this example is
Tables
created with createDialog.
Uses a color chooser that is not in a dialog; demonstrates
Introduction to
BasicDnD default drag-and-drop capabilities of Swing components,
DnD
including color choosers.
Introduction to Uses a color chooser without a dialog in a demonstration
DragColorDemo
DnD of importing Color data.
Introduction to Similar to DragColorDemo, but demonstrates importing
DragColorTextFieldDemo
DnD both Color and text data.
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
560
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:
The application shown here uses an uneditable combo box for choosing a pet picture:
Try this:
1. Click the Launch button to run the ComboBox Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
2. Choose an animal name from the combo box to view its picture.
3. Compare the operation and GUI of this program to one that uses radio buttons:
run RadioButtonDemo (it requires release 6). You might want to compare the
source code as well: ComboBoxDemo.java vs. RadioButtonDemo.java.
The following code, taken from ComboBoxDemo.java, creates an uneditable combo box and sets it
up:
String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
561
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.
Something else to watch out for — even for uneditable combo boxes — is ensuring that your custom
model fires list data events when the combo box's data or state changes. Even immutable combo box
models, whose data never changes, must fire a list data event (a CONTENTS_CHANGED event) when the
selection changes. One way to get the list data event firing code for free is to make your combo box
model a subclass of AbstractListModel.
Here's the code from ComboBoxDemo.java that registers and implements an action listener on the
combo box:
public class ComboBoxDemo ... implements ActionListener {
. . .
petList.addActionListener(this) {
. . .
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
String petName = (String)cb.getSelectedItem();
updateLabel(petName);
}
. . .
}
This action listener gets the newly selected item from the combo box, uses it to compute the name of
an image file, and updates a label to display the image. The combo box fires an action event when
the user selects an item from the combo box's menu. See How to Write an Action Listener, for
general information about implementing action listeners.
Combo boxes also generate item events, which are fired when any of the items' selection state
changes. Only one item at a time can be selected in a combo box, so when the user makes a new
selection the previously selected item becomes unselected. Thus two item events are fired each time
the user selects a different item from the menu. If the user chooses the same item, no item events are
562
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.
Here's a picture of a demo application that uses an editable combo box to enter a pattern with which
to format dates.
Try this:
1. Click the Launch button to run the ComboBox2 Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
2. Enter a new pattern by choosing one from the combo box's menu. The
program reformats the current date and time.
3. Enter a new pattern by typing one in and pressing Enter. Again the program
reformats the current date and time.
The following code, taken from ComboBoxDemo2.java, creates and sets up the combo box:
String[] patternExamples = {
"dd MMMMM yyyy",
"dd.MM.yy",
"MM/dd/yy",
"yyyy.MM.dd G 'at' hh:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"H:mm:ss:SSS",
"K:mm a,z",
563
"yyyy.MMMMM.dd GGG hh:mm aaa"
};
. . .
JComboBox patternList = new JComboBox(patternExamples);
patternList.setEditable(true);
patternList.addActionListener(this);
This code is very similar to the previous example, but warrants a few words of explanation. The bold
line of code explicitly turns on editing to allow the user to type values in. This is necessary because,
by default, a combo box is not editable. This particular example allows editing on the combo box
because its menu does not provide all possible date formatting patterns, just shortcuts to frequently
used patterns.
An editable combo box fires an action event when the user chooses an item from the menu and when
the user types Enter. Note that the menu remains unchanged when the user enters a value into the
combo box. If you want, you can easily write an action listener that adds a new item to the combo
box's menu each time the user types in a unique value.
See Internationalization to learn more about formatting dates and other types of data.
A combo box uses a renderer to display each item in its menu. If the combo box is uneditable, it also
uses the renderer to display the currently selected item. An editable combo box, on the other hand,
uses an editor to display the selected item. A renderer for a combo box must implement the
ListCellRenderer interface. A combo box's editor must implement ComboBoxEditor. This section
shows how to provide a custom renderer for an uneditable combo box.
The default renderer knows how to render strings and icons. If you put other objects in a combo box,
the default renderer calls the toString method to provide a string to display. You can customize the
way a combo box renders itself and its items by implementing your own ListCellRenderer.
Here's a picture of an application that uses a combo box with a custom renderer:
Click the Launch button to run the CustomComboBox Demo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The full source code for this example is in CustomComboBoxDemo.java. To get the image files it
requires, consult the example index.
564
The following statements from the example create an instance of ComboBoxRenderer (a custom
class) and set up the instance as the combo box's renderer:
The last line sets the combo box's maximum row count, which determines the number of items
visible when the menu is displayed. If the number of items in the combo box is larger than its
maximum row count, the menu has a scroll bar. The icons are pretty big for a menu, so our code
limits the number of rows to 3. Here's the implementation of ComboBoxRenderer, a renderer that
puts an icon and text side-by-side:
/*
* 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;
565
}
. . .
}
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.
JList list — a list object used behind the scenes to display the items. The example
uses this object's colors to set up foreground and background colors.
Object value — the object to render. An Integer in this example.
int index — the index of the object to render.
boolean isSelected — indicates whether the object to render is selected. Used by
the example to determine which colors to use.
boolean cellHasFocus — indicates whether the object to render has the focus.
Note that combo boxes and lists use the same type of renderer — ListCellRenderer. You can save
yourself some time by sharing renderers between combo boxes and lists, if it makes sense for your
program.
The following tables list the commonly used JComboBox constructors and methods. Other methods
you are most likely to invoke on a JComboBox object are those it inherits from its superclasses, such
as setPreferredSize. See The JComponent API for tables of commonly used inherited methods.
The API for using combo boxes falls into two categories:
566
void removeItemAt(int) methods require that the combo box's data model be an instance of
void removeItem(Object) MutableComboBoxModel.
int getItemCount() Get the number of items in the combo box's menu.
void
Set or get the data model that provides the items in the combo box's
setModel(ComboBoxModel)
menu.
ComboBoxModel getModel()
void setAction(Action) Set or get the Action associated with the combo box. For further
Action getAction() information, see How to Use Actions.
Customizing the Combo Box's Operation
Method or Constructor Purpose
Add an action listener to the combo box. The listener's
void actionPerformed method is called when the user selects an
addActionListener(ActionListener) item from the combo box's menu or, in an editable combo box,
when the user presses Enter.
Add an item listener to the combo box. The listener's
void addItemListener(ItemListener) itemStateChanged method is called 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 box.
boolean isEditable()
Set or get the object responsible for painting the selected item
void setRenderer(ListCellRenderer) in the combo box. The renderer is used only when the combo
ListCellRenderer getRenderer() box is uneditable. If the combo box is editable, the editor is
used to paint the selected item instead.
Set or get the object responsible for painting and editing the
void setEditor(ComboBoxEditor) selected item in the combo box. The editor is used only when
ComboBoxEditor getEditor() the combo box is editable. If the combo box is uneditable, the
renderer is used to paint the selected item instead.
This table shows the examples that use JComboBox and where those examples are described.
Example Where Described Notes
ComboBoxDemo This section Uses an uneditable combo box.
ComboBoxDemo2 This section Uses an editable combo box.
CustomComboBoxDemo This section
Provides a custom renderer for a
combo box.
TableRenderDemo
How to Use Tables (Using a Combo Shows how to use a combo box as a
Box as an Editor) table cell editor.
567
For convenience, several Swing component classes can directly instantiate and display dialogs. To
create simple, standard dialogs, you use the JOptionPane class. The ProgressMonitor class can put
up a dialog that shows the progress of an operation. Two other classes, JColorChooser and
JFileChooser, also supply standard dialogs. To bring up a print dialog, you can use the Printing
API. To create a custom dialog, use the JDialog class directly.
The code for simple dialogs can be minimal. For example, here is an informational dialog:
An Overview of Dialogs
The DialogDemo Example
JOptionPane Features
Creating and Showing Simple Dialogs
Customizing Button Text
Getting the User's Input from a Dialog
Stopping Automatic Dialog Closing
The Dialog API
Examples that Use Dialogs
An Overview of Dialogs
Every dialog is dependent on a Frame component. When that Frame is destroyed, so are its
dependent Dialogs. When the frame is iconified, its dependent Dialogs also disappear from the
screen. When the frame is deiconified, its dependent Dialogs return to the screen. A swing JDialog
class inherits this behavior from the AWT Dialog class.
A Dialog can be modal. When a modal Dialog is visible, it blocks user input to all other windows in
the program. JOptionPane creates JDialogs that are modal. To create a non-modal Dialog, you must
use the JDialog class directly.
Starting with JDK6, you can modify Dialog window modality behavior using the new Modality API.
See The New Modality API for details.
The JDialog class is a subclass of the AWT java.awt.Dialog class. It adds a root pane container
and support for a default close operation to the Dialog object . These are the same features that
JFrame has, and using JDialog directly is very similar to using JFrame. If you're going to use
JDialog directly, then you should understand the material in Using Top-Level Containers and How
to Make Frames, especially Responding to Window-Closing Events.
568
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.
Try this::
1. Click the Launch button to run the Dialog Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
JOptionPane Features
Using JOptionPane, you can quickly create and customize several different kinds of dialogs.
JOptionPane provides support for laying out standard dialogs, providing icons, specifying the dialog
569
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.
When you create a JOptionPane, look-and-feel-specific code adds components to the JOptionPane
and determines the layout of those components.
JOptionPane's icon support lets you easily specify which icon the dialog displays. You can use a
custom icon, no icon at all, or any one of four standard JOptionPane icons (question, information,
warning, and error). Each look and feel has its own versions of the four standard icons. The
following figure shows the icons used in the Java (and Windows) look and feel.
For most simple modal dialogs, you create and show the dialog using one of JOptionPane's
showXxxDialog methods. If your dialog should be an internal frame, then add Internal after show
— for example, showMessageDialog changes to showInternalMessageDialog. If you need to
control the dialog window-closing behavior or if you do not want the dialog to be modal, then you
should directly instantiate JOptionPane and add it to a JDialog instance. Then invoke
setVisible(true) on the JDialog to make it appear.
The two most useful showXxxDialog methods are showMessageDialog and showOptionDialog.
The showMessageDialog method displays a simple, one-button dialog. The showOptionDialog
method displays a customized dialog — it can display a variety of buttons with customized button
text, and can contain a standard text message or a collection of components.
The other two showXxxDialog methods are used less often. The showConfirmDialog method asks
the user to confirm something, but presents standard button text (Yes/No or the localized equivalent,
for example) rather than button text customized to the user situation (Start/Cancel, for example). A
fourth method, showInputDialog, is designed to display a modal dialog that gets a string from the
user, using either a text field, an uneditable combo box or a list.
showMessageDialog
Displays a modal dialog with one button, which is labeled "OK" (or the localized
equivalent). You can easily specify the message, icon, and title that the dialog
displays. Here are some examples of using showMessageDialog:
570
//default title and icon
JOptionPane.showMessageDialog(frame,
"Eggs are not supposed to be green.",
"Message");
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.
571
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[2]);
JOptionPane (constructor)
Creates a JOptionPane with the specified buttons, icons, message, title, and so on.
You must then add the option pane to a JDialog, register a property-change listener
on the option pane, and show the dialog. See Stopping Automatic Dialog Closing for
details.
The JOptionPane constructors do not include this argument. Instead, you specify the
parent frame when you create the JDialog that contains the JOptionPane, and you
use the JDialog setLocationRelativeTo method to set the dialog position.
Object message
This required argument specifies what the dialog should display in its main area.
Generally, you specify a string, which results in the dialog displaying a label with the
specified text. You can split the message over several lines by putting newline (\n)
characters inside the message string. For example:
"Complete the sentence:\n \"Green eggs and...\""
String title
The title of the dialog.
int optionType
572
Specifies the set of buttons that appear at the bottom of the dialog. Choose from one
of the following standard sets: DEFAULT_OPTION, YES_NO_OPTION,
YES_NO_CANCEL_OPTION, OK_CANCEL_OPTION.
int messageType
This argument determines the icon displayed in the dialog. Choose from one of the
following values: PLAIN_MESSAGE (no icon), ERROR_MESSAGE,
INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE.
Icon icon
The icon to display in the dialog.
Object[] options
Generally used to specify the string displayed by each button at the bottom of the
dialog. See Customizing Button Text in a Standard Dialog for more information. Can
also be used to specify icons to be displayed by the buttons or non-button components
to be added to the button row.
Object initialValue
Specifies the default value to be selected.
You can either let the option pane display its default icon or specify the icon using the message type
or icon argument. By default, an option pane created with showMessageDialog displays the
information icon, one created with showConfirmDialog or showInputDialog displays the question
icon, and one created with a JOptionPane constructor displays no icon. To specify that the dialog
display a standard icon or no icon, specify the message type corresponding to the icon you desire. To
specify a custom icon, use the icon argument. The icon argument takes precedence over the message
type; as long as the icon argument has a non-null value, the dialog displays the specified icon.
When you use JOptionPane to create a dialog, you can either use the standard button text (which
might vary by look and feel and locale) or specify different text. By default, the option pane type
determines how many buttons appear. For example, YES_NO_OPTION dialogs have two buttons, and
YES_NO_CANCEL_OPTION dialogs have three buttons.
The following code, taken from DialogDemo.java, creates two Yes/No dialogs. The first dialog is
implemented with showConfirmDialog, which uses the look-and-feel wording for the two buttons.
The second dialog uses showOptionDialog so it can customize the wording. With the exception of
wording changes, the dialogs are identical.
573
As the previous code snippets showed, the showMessageDialog, showConfirmDialog, and
showOptionDialog methods return an integer indicating the user's choice. The values for this integer
are YES_OPTION, NO_OPTION, CANCEL_OPTION, OK_OPTION, and CLOSED_OPTION. Except for
CLOSED_OPTION, each option corresponds to the button the user pressed. When CLOSED_OPTION is
returned, it indicates that the user closed the dialog window explicitly, rather than by choosing a
button inside the option pane.
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.
The only form of showXxxDialog that does not return an integer is showInputDialog, which returns
an Object instead. This Object is generally a String reflecting the user's choice. Here is an
example of using showInputDialog to create a dialog that lets the user choose one of three strings:
574
Because the user can type anything into the text field, you might want to check the returned value
and ask the user to try again if it is invalid. Another approach is to create a custom dialog that
validates the user-entered data before it returns. See CustomDialog.java for an example of
validating data.
If you're designing a custom dialog, you need to design your dialog's API so that you can query the
dialog about what the user chose. For example, CustomDialog has a getValidatedText method
that returns the text the user entered.
By default, when the user clicks a JOptionPane-created button, the dialog closes. But what if you
want to check the user's answer before closing the dialog? In this case, you must implement your
own property change listener so that when the user clicks a button, the dialog does not automatically
close.
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 do not care to be notified when the user closes the window explicitly,
then ignore the bold code.
if (dialog.isVisible()
&& (e.getSource() == optionPane)
&& (prop.equals(JOptionPane.VALUE_PROPERTY))) {
//If you were going to check something
//before closing the window, you'd do
//it here.
dialog.setVisible(false);
575
}
}
});
dialog.pack();
dialog.setVisible(true);
The following tables list the commonly used JOptionPane and JDialog constructors and methods.
Other methods you're likely to call are defined by the Dialog, Window and Component classes and
include pack, setSize, and setVisible.
576
showConfirmDialog(Component,
Object, String, int, int, Icon)
static String showInputDialog(Object)
Show a modal dialog that prompts the user for input. The
static String
single-argument version specifies just the message, with
showInputDialog(Component, Object)
the parent component assumed to be null. The arguments
static String
for the other versions specify (in order) the parent
showInputDialog(Component, Object,
component, message, title, message type, icon, options,
String, int)
and initial value for the dialog. See Creating and Showing
static String
Simple Dialogs for a discussion of the arguments and their
showInputDialog(Component, Object,
effects.
String, int, Icon, Object[], Object)
static void
showInternalMessageDialog(...)
Implement a standard dialog as an internal frame. See the
static void showInternalOptionDialog(...)
JOptionPane API documentation for the exact list of
static void
arguments.
showInternalConfirmDialog(...)
static String showInternalInputDialog(...)
Methods for Using JOptionPanes Directly
Method or Constructor Purpose
JOptionPane()
JOptionPane(Object)
JOptionPane(Object, int)
Creates a JOptionPane instance. See Creating and
JOptionPane(Object, int, int)
Showing Simple Dialogs for a discussion of the
JOptionPane(Object, int, int, Icon)
arguments and their effects.
JOptionPane(Object, int, int, Icon, Object[])
JOptionPane(Object, int, int, Icon, Object[],
Object)
static Frame
Handy JOptionPane class methods that find the
getFrameForComponent(Component)
frame or desktop pane, respectively, that the specified
static JDesktopPane
component is in.
getDesktopPaneForComponent(Component)
Determines where line breaks will be automatically
inserted in the option pane text. (The default is
Integer.MAX_VALUE.) To use this method, you must
create a JOptionPane subclass. For example, the
following code results in an option pane with one
word per line, due to the fact that each word in the
int getMaxCharactersPerLineCount() string is 5 characters or less:
JOptionPane op = new JOptionPane("This is
the text.") {
public int
getMaxCharactersPerLineCount() {
return 5;
}
};
577
JDialog(Dialog, boolean) the dialog depends on. Make the boolean
JDialog(Dialog, String) argument true to specify a modal dialog, false
JDialog(Dialog, String, boolean) or absent to specify a non-modal dialog. You can
JDialog(Dialog, String, boolean, also specify the title of the dialog, using a string
GraphicsConfiguration) argument.
JDialog(Frame)
JDialog(Frame, boolean)
JDialog(Frame, String)
JDialog(Frame, String, boolean)
JDialog(Frame, String, boolean,
GraphicsConfiguration)
JDialog(Window owner)
JDialog(Window owner, Dialog.ModalityType
modalityType)
JDialog(Window owner, String title)
JDialog(Window owner, String title,
Dialog.ModalityType modalityType)
JDialog(Window owner, String title,
Dialog.ModalityType modalityType,
GraphicsConfiguration gc)
Get and set the content pane, which is usually the
void setContentPane(Container) container of all the dialog's components. See
Container getContentPane() Using Top-Level Containers for more
information.
Get and set what happens when the user tries to
close the dialog. Possible values:
void setDefaultCloseOperation(int)
DISPOSE_ON_CLOSE, DO_NOTHING_ON_CLOSE,
int getDefaultCloseOperation()
HIDE_ON_CLOSE (the default). See Responding to
Window-Closing Events for more information.
void setLocationRelativeTo(Component) Centers the dialog over the specified component.
Set or get a hint as to whether the dialog's window
decorations (such as borders, or widgets to close
static void the window) should be provided by the current
setDefaultLookAndFeelDecorated(boolean) look and feel. Otherwise the dialog's decorations
static boolean isDefaultLookAndFeelDecorated() will be provided by the current window manager.
See Specifying Window Decorations for more
information.
This table lists examples that use JOptionPane or JDialog. To find other examples that use dialogs,
see the example lists for progress bars, color choosers, and file choosers.
Where
Example Notes
Described
DialogDemo,
CustomDialog
This section Creates many kinds of dialogs, using JOptionPane and JDialog.
Brings up a confirmation dialog when the user selects the Quit menu
Framework —
item.
ListDialog How to Use Implements a modal dialog containing a scrolling list and two
578
BoxLayout buttons. Does not use JOptionPane, except for the utility method
getFrameForComponent.
You can see an editor pane and a text pane in use by running TextSamplerDemo. Here is a picture of
the TextSamplerDemo example.
Click the Launch button to run TextSamplerDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
The TextSamplerDemo example barely begins to demonstrate the capabilities of editor panes and
text panes. However, the top right editor pane illustrates a handy, easy-to-use feature: it displays
uneditable help information loaded from a URL. The text pane at the lower right demonstrates that
you can easily embed images and even components directly into text panes.
Note: If you need a fully-fledged help system, take a look at the JavaHelpTM system.
The Swing text API is powerful and immense, and we could devote an entire book just to using
editor panes and text panes. This section introduces their capabilities, offers hints on which one you
might want to use, and points to other sources of information.
579
Using an Editor Pane to Display Text From a URL
Editor Panes vs. Text Panes
An Example of Using a Text Pane
The Editor Pane and Text Pane API
Examples That Use Editor Panes and Text Panes
One task that you can accomplish without knowing anything about the Swing text system is
displaying text from a URL. Here is the code from TextSamplerDemo.java that creates an
uneditable editor pane that displays text formatted with HTML tags.
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
java.net.URL helpURL = TextSamplerDemo.class.getResource(
"TextSamplerDemoHelp.html");
if (helpURL != null) {
try {
editorPane.setPage(helpURL);
} catch (IOException e) {
System.err.println("Attempted to read a bad URL: " + helpURL);
}
} else {
System.err.println("Couldn't find file: TextSamplerDemoHelp.html");
}
The setPage method opens the resource pointed to by the URL and figures out the format of the text
(which is HTML in the example). If the text format is known, the editor pane initializes itself with
the text found at the URL. A standard editor pane can understand plain text, HTML, and RTF. Note
that the page might be loaded asynchronously, which keeps the GUI responsive but means that you
should not count on the data being completely loaded after the call to setPage returns.
In order to use editor panes and text panes, you need to understand the text system, which is
described in Text Component Features. Several facts about editor panes and text panes are scattered
throughout that section. Here we list the facts again and provide a bit more detail. The information
here should help you understand the differences between editor panes and text panes, and when to
use which.
An editor pane or a text pane can easily be loaded with text from a URL using the
setPage method. The JEditorPane class also provides constructors that let you
initialize an editor pane from a URL. The JTextPane class has no such constructors.
See Using an Editor Pane to Display Text From a URL for an example that uses this
feature to load an uneditable editor pane with HTML-formatted text.
580
Be aware that the document and editor kit might change when using the setPage
method. For example, if an editor pane contains plain text (the default), and you load
it with HTML, the document will change to an HTMLDocument instance and the editor
kit will change to an HTMLEditorKit instance. If your program uses the setPage
method, make sure you adjust your code for possible changes to the pane's document
and editor kit instances (re-register document listeners on the new document, and so
on).
Editor panes, by default, know how to read, write, and edit plain, HTML, and RTF
text. Text panes inherit this capability but impose certain limitations. A text pane
insists that its document implement the StyledDocument interface. HTMLDocument
and RTFDocument are both StyledDocuments so HTML and RTF work as expected
within a text pane. If you load a text pane with plain text though, the text pane's
document is not a PlainDocument as you might expect, but a
DefaultStyledDocument.
To support a custom text format, implement an editor kit that can read, write, and edit
text of that format. Then call the registerEditorKitForContentType method to
register your kit with the JEditorPane class. By registering an editor kit in this way,
all editor panes and text panes in your program will be able to read, write, and edit the
new format. However, if the new editor kit is not a StyledEditorKit, text panes will
not support the new format.
As mentioned previously, a text pane requires its document to implement the
StyledDocument interface. The Swing text package provides a default
implementation of this interface, DefaultStyledDocument, which is the document
that text panes use by default. A text pane also requires that its editor kit be an
instance of a StyledEditorKit (or a subclass). Be aware that the read and write
methods for StyleEditorKit work with plain text.
Through their styled document and styled editor kit, text panes provide support for
named styles and logical styles. The JTextPane class itself contains many methods
for working with styles that simply call methods in its document or editor kit.
Through the API provided in the JTextPane class, you can embed images and
components in a text pane. You can embed images in an editor pane, too, but only by
including the images in an HTML or RTF file.
Here is the code from the TextSamplerDemo example that creates and initializes a text pane.
String[] initString =
{ /* ... fill array with initial text ... */ };
String[] initStyles =
{ /* ... fill array with names of styles ... */ };
581
}
Briefly, this code hard-codes the initial text into an array and creates and hard-codes several styles —
objects that represent different paragraph and character formats — into another array. Next, the code
loops over the arrays, inserts the text into the text pane, and specifies the style to use for the inserted
text.
Although this is an interesting example that concisely demonstrates several features of JTextPane,
"real-world" programs aren't likely to initialize a text pane this way. Instead, a program would use a
text pane to save a document which would then be used to initialize the text pane.
This section lists some of the API related to text and editor panes. Many of the most useful methods
for JEditorPane and its subclass JTextPane are inherited from the JTextComponent class. You can
find the API tables for JTextComponent in The Text Component API. Also see The JComponent
Class, which describes the API inherited from JComponent.
JEditorPane API for Displaying Text from a URL
Method or Constructor Description
JEditorPane(URL)
Creates an editor pane loaded with the text at the specified URL.
JEditorPane(String)
setPage(URL)
Loads an editor pane (or text pane) with the text at the specified URL.
setPage(String)
URL getPage() Gets the URL for the editor pane's (or text pane's) current page.
JTextPane API
Method or Constructor Description
JTextPane() Creates a text pane. The optional argument specifies the text
JTextPane(StyledDocument) pane's model.
StyledDocument getStyledDocument
Gets or sets the text pane's model.
setStyledDocument(StyledDocument)
To begin using text, you might want to run these programs and examine their code to find something
similar to what you want to do.
Where
Example Notes
Described
TextSamplerDemo Using Text Uses each Swing text component.
Components
TextComponentDemo Text Provides a customized text pane. Illustrates many text
Component component features, such as undo and redo, document filters,
Features document listeners, caret change listeners, and how to
associate editing actions with menus and key strokes.
TreeDemo How to Use Uses an editor pane to display help loaded from an HTML file.
Trees
Note: If you intend to distribute your program as an unsigned Java™ 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
can be found in the Java Web Start lesson.
Click the Launch button to run JWSFileChooserDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
When working with the JWSFileChooserDemo example, be careful not to lose files that you need.
Whenever you click the save button and select an existing file, this demo brings up the File Exists
dialog box with a request to replace the file. Accepting the request overwrites the file.
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 Basic I/O for information on how to read and write
files.
The JFileChooser API makes it easy to bring up open and save dialogs. The type of 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 a standard open dialog in the Java look
and feel:
583
Here is a picture of an application called FileChooserDemo that brings up an open dialog and a save
dialog.
Try this:
584
The argument to the showOpenDialog method specifies the parent component for the dialog. The
parent component affects the position of the dialog and the frame that the dialog depends on. For
example, the Java look and feel places the dialog directly over the parent component. If the parent
component is in a frame, then the dialog is dependent on that frame. This dialog disappears when the
frame is minimized and reappears when the frame is maximized.
By default, a file chooser that has not been shown before displays all files in the user's home
directory. You can specify the file chooser's initial directory by using one of JFileChooser's other
constructors, or you can set the directory with the setCurrentDirectory method.
The call to showOpenDialog appears in the actionPerformed method of the Open a File button's
action listener:
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
//This is where a real application would open the file.
log.append("Opening: " + file.getName() + "." + newline);
} else {
log.append("Open command cancelled by user." + newline);
}
} ...
}
The showXxxDialog methods return an integer that indicates whether the user selected a file.
Depending on how you use a file chooser, it is often sufficient to check whether the return value is
APPROVE_OPTION and then not to change any other value. To get the chosen file (or directory, if you
set up the file chooser to allow directory selections), call the getSelectedFile method on the file
chooser. This method returns an instance of File.
The example obtains 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 obtain 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 by using one of the reader or writer classes
provided by the Java platform. See Basic I/O for information about using readers and writers to read
and write data to the file system.
The example program uses the same instance of the JFileChooser class to display a standard save
dialog. This time the program calls showSaveDialog:
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.
585
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);
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 will discuss the following topics:
Let us look at FileChooserDemo2 example, a modified version of the previous demo program that
uses more of the JFileChooser API. This example uses a file chooser that has been customized in
several ways. Like the original example, the user invokes a file chooser with the push of a button.
Here is a picture of the file chooser:
586
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 example index for links to all the files required by this example.
As you have seen, the JFileChooser class provides the showOpenDialog method for displaying an
open dialog and the showSaveDialog method for displaying a save dialog.
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 is the code from
FileChooserDemo2 that brings up the file chooser dialog for the Attach task:
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.
By default, a file chooser displays all of the files and directories that it detects, except for hidden
files. A program can apply one or more file filters to a file chooser so that the chooser shows only
some files. The file chooser calls the filter's accept method for each file to determine whether it
587
should be displayed. A file filter accepts or rejects a file based on criteria such as file type, size,
ownership, and so on. Filters affect the list of files displayed by the file chooser. The user can enter
the name of any file even if it is not displayed.
JFileChooser supports three different kinds of filtering. The filters are checked in the order listed
here. For example, an application-controlled filter sees only those files accepted by the built-in
filtering.
Built-in filtering
Filtering is set up through specific method calls on a file chooser. Currently the only
built-in filter available is for hidden files, such as those whose names begin with
period (.) on UNIX systems. By default, hidden files are not shown. Call
setFileHidingEnabled(false) to show hidden files.
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 the setFileFilter
method. The installed filter is displayed on the list of user-choosable filters. 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 files 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
enables the user to 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;
}
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.
588
The preceding code sample uses the getExtension method and several string
constants from Utils.java, shown here:
/*
* Get the extension of a file.
*/
public static String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
In the Java look and feel, the chooser's list shows each file's name and displays a small icon that
represents whether the file is a true file or a directory. You can customize this file view by creating a
custom subclass of FileView and using an instance of the class as an argument to the setFileView
method. The example uses an instance of a custom class, implemented in ImageFileView.java, as
the file chooser's file view.
fc.setFileView(new ImageFileView());
The ImageFileView class shows a different icon for each type of image accepted by the image filter
described previously.
The ImageFileView class overrides the five abstract methods defined in the FileView as follows.
String getTypeDescription(File f)
Returns a description of the file type. Here is ImageFileView's implementation of this
method:
public String getTypeDescription(File f) {
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";
}
589
}
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. 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.
The customized file chooser in FileChooserDemo2 has an accessory component. If the currently
selected item is a PNG, JPEG, TIFF, or GIF image, the accessory component displays a thumbnail
sketch 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 such
as checkboxes that toggle between features.
The example calls the setAccessory method to establish an instance of the ImagePreview class,
implemented in ImagePreview.java, as the chooser's accessory component:
fc.setAccessory(new ImagePreview(fc));
Any object that inherits from the JComponent class can be an accessory component. The component
should have a preferred size that looks good in the file chooser.
590
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:
The API for using file choosers falls into these categories:
591
int CANCEL_OPTION if the user cancelled it. Another possible return
showSaveDialog(Component) value is ERROR_OPTION, which means an unanticipated error
int showDialog(Component, occurred.
String)
Selecting Files and Directories
Method Purpose
void setSelectedFile(File) Sets or obtains the currently selected file or (if directory
File getSelectedFile() selection has been enabled) directory.
void setSelectedFiles(File[]) Sets or obtains the currently selected files if the file chooser
File[] getSelectedFiles() is set to allow multiple selection.
void setFileSelectionMode(int) Sets or obtains the file selection mode. Acceptable values are
void getFileSelectionMode() FILES_ONLY (the default), DIRECTORIES_ONLY, and
boolean FILES_AND_DIRECTORIES.
isDirectorySelectionEnabled() Interprets whether directories or files are selectable according
boolean isFileSelectionEnabled() to the current selection mode.
void
Sets or interprets whether multiple files can be selected at
setMultiSelectionEnabled(boolean)
once. By default, a user can choose only one file.
boolean isMultiSelectionEnabled()
void Sets or obtains whether the AcceptAll file filter is used as an
setAcceptAllFileFilterUsed(boolean) allowable choice in the choosable filter list; the default value
boolean isAcceptAllFileFilterUsed() is true.
Given a parent component, creates and returns a new dialog
Dialog createDialog(Component) that contains this file chooser, is dependent on the parent's
frame, and is centered over the parent.
Navigating the File Chooser's List
Method Purpose
void ensureFileIsVisible(File) Scrolls the file chooser's list such that the indicated file is visible.
void
Sets or obtains the directory whose files are displayed in the file
setCurrentDirectory(File)
chooser's list.
File getCurrentDirectory()
void
Changes the list to display the current directory's parent.
changeToParentDirectory()
void
Checks the file system and updates the chooser's list.
rescanCurrentDirectory()
void
Sets or obtains the property that determines whether automatic drag
setDragEnabled(boolean)
handling is enabled. See Drag and Drop for more details.
boolean getDragEnabled()
Customizing the File Chooser
Method Purpose
void
setAccessory(javax.swing.JComponent) Sets or obtains the file chooser's accessory component.
JComponent getAccessory()
void setFileFilter(FileFilter)
Sets or obtains the file chooser's primary file filter.
FileFilter getFileFilter()
void setFileView(FileView) Sets or obtains the chooser's file view.
592
FileView getFileView()
FileFilter[] getChoosableFileFilters()
void addChoosableFileFilter(FileFilter)
boolean Sets, obtains, or modifies the list of user-choosable file
removeChoosableFileFilter(FileFilter) filters.
void resetChoosableFileFilters()
FileFilter getAcceptAllFileFilter()
void setFileHidingEnabled(boolean)
Sets or obtains whether hidden files are displayed.
boolean isFileHidingEnabled()
void Sets or obtains the property that indicates whether the
setControlButtonsAreShown(boolean) Approve and Cancel buttons are shown in the file
boolean getControlButtonsAreShown() chooser. This property is true by default.
This table shows the examples that use file choosers and points to where those examples are
described.
Where
Example Notes
Described
FileChooserDemo This section Displays an open dialog and a save dialog.
FileChooserDemo2 This section
Uses a file chooser with custom filtering, a custom file
view, and an accessory component.
DragFileDemo
Introduction to Uses a file chooser directly, without a dialog, and
DnD demonstrates dragging files.
JWSFileChooserDemo This section Uses the JNLP API to open and save files.
Using the formatters that Swing provides, you can set up formatted text fields to type dates and
numbers in localized formats. Another kind of formatter enables you to use a character mask to
specify the set of characters that can be typed at each position in the field. For example, you can
specify a mask for typing phone numbers in a particular format, such as (XX) X-XX-XX-XX-XX.
If the possible values of a formatted text field have an obvious order, use a spinner instead. A spinner
uses a formatted text field by default, but adds two buttons that enable the user to choose a value in a
sequence.
Another alternative or adjunct to using a formatted text field is installing an input verifier on the
field. A component's input verifier is called when the component nearly loses the keyboard focus.
The input verifier enables you to check whether the value of the component is valid and optionally
change it or stop the focus from being transferred.
593
This GUI uses formatted text fields to display numbers in four different formats.
Try this:
2. Experiment with different loan amounts, annual percentage rates (APRs), and
loan lengths.
Note that as long as the text you type is valid, the Month Payment field is
updated when you press Enter or move the focus out of the field that you are
editing.
3. Type invalid text such as "abcd" in the Loan Amount field and then press
Enter.
The Month Payment field remains the same. When you move the focus from
the Loan Amount field, the text reverts to the field's last valid value.
4. Type marginally valid text such as "2000abcd" in the Loan Amount field and
press Enter.
The Monthly Payment field is updated, though the Loan Amount field still
displays 2000abcd. When you move the focus from the Loan Amount field,
the text it displays is updated to a neatly formatted version of its value, for
example, "2,000".
You can find the entire code for this program in FormattedTextFieldDemo.java. This code creates
the first field.
amountField = new JFormattedTextField(amountFormat);
amountField.setValue(new Double(amount));
amountField.setColumns(10);
amountField.addPropertyChangeListener("value", this);
...
amountFormat = NumberFormat.getNumberInstance();
The constructor used to create the amountField object takes a java.text.Format argument. The
Format object is used by the field's formatter to translate the field's value to text and the text to the
field's value.
The remaining code sets up the amountField object. The setValue method sets the field's value
property to a floating-point number represented as a Double object. The setColumns method,
inherited from the JTextField class, hints about the preferred size of the field. The call to the
594
addPropertyChangeListener method registers a listener for the value property of the field, so the
program can update the Monthly Payment field whenever the user changes the loan amount.
This section does not explain the API inherited from the JTextField class. That API is described in
How to Use Text Fields.
The following code creates and initializes the remaining three fields in the
FormattedTextFieldDemo example.
...
percentFormat = NumberFormat.getNumberInstance();
percentFormat.setMinimumFractionDigits(2);
paymentFormat = NumberFormat.getCurrencyInstance();
The code for setting up the rateField object is almost identical to the code listed previously for
other fields. The only difference is that the format is slightly different, thanks to the code
percentFormat.setMinimumFractionDigits(2).
The code that creates the numPeriodsField object does not explicitly set a format or formatter.
Instead, it sets the value to an Integer and enables the field to use the default formatter for Integer
objects. The code did not do this in the previous two fields because the default formatter is not being
used for Double objects. The result was not what was needed. How to specify formats and formatters
is covered later in this section.
The payment field is different from the other fields because it is uneditable, uses a different color for
its text, and does not have a property change listener. Otherwise, it is identical to the other fields. We
could have chosen to use a text field or label instead. Whatever the component, we could still use the
paymentFormat method to parse the payment amount into the text to be displayed.
595
Setting and Getting the Field's Value
A formatted text field's text and its value are two different properties, and the value often lags
behind the text.
The text property is defined by the JTextField class. This property always reflects what the field
displays. The value property, defined by the JFormattedTextField class, might not reflect the latest
text displayed in the field. While the user is typing, the text property changes, but the value property
does not change until the changes are committed.
To be more precise, the value of a formatted text field can be set by using either the setValue
method or the commitEdit method. The setValue method sets the value to the specified argument.
The argument can technically be any Object, but the formatter needs to be able to convert it into a
string. Otherwise, the text field does not display any substantive information.
The commitEdit method sets the value to whatever object the formatter determines is represented by
the field's text. The commitEdit method is automatically called when either of the following
happens:
When the user presses Enter while the field has the focus.
By default, when the field loses the focus, for example, when the user presses the Tab
key to change the focus to another component. You can use the
setFocusLostBehavior method to specify a different outcome when the field loses
the focus.
Note: Some formatters might update the value constantly, rendering the loss of focus meaningless, as
the value is always the same as what the text specifies.
When you set the value of a formatted text field, the field's text is updated to reflect the value.
Exactly how the value is represented as text depends on the field's formatter.
Note that although the JFormattedTextField class inherits the setText method from the
JTextField class, you do not usually call the setText method on a formatted text field. If you do,
the field's display changes accordingly but the value is not updated (unless the field's formatter
updates it constantly).
To obtain a formatted text field's current value, use the getValue method. If necessary, you can
ensure that the value reflects the text by calling the commitEdit method before getValue. Because
the getValue method returns an Object, you need to cast it to the type used for your field's value.
For example:
To detect changes in a formatted text field's value, you can register a property change listener on the
formatted text field to listen for changes to the "value" property. The property change listener is
taken from the FormattedTextFieldDemo example:
596
//The property change listener is registered on each
//field using code like this:
// someField.addPropertyChangeListener("value", this);
Specifying Formats
The Format class provides a way to format locale-sensitive information such as dates and numbers.
Formatters that descend from the InternationalFormatter class, such as the DateFormatter and
NumberFormatter classes, use Format objects to translate between the field's text and value. You
can obtain a Format object by calling one of the factory methods in the DateFormat or
NumberFormat classes, or by using one of the SimpleDateFormat constructors.
Note: A third commonly used formatter class, MaskFormatter, does not descend from the
InternationalFormatter class and does not use formats. The MaskFormatter is discussed in
Using MaskFormatter.
You can customize certain format aspects when you create the Format object, and others through a
format-specific API. For example, DecimalFormat objects, which inherit from NumberFormat and
are often returned by its factory methods, can be customized by using the
setMaximumFractionDigits and setNegativePrefix methods. For information about using
Format objects, see the Formatting lesson of the Internationalization trail.
The easiest way to associate a customized format with a formatted text field is to create the field by
using the JFormattedTextField constructor that takes a Format as an argument. You can see this
association in the previous code examples that create amountField and rateField objects.
Using MaskFormatter
The MaskFormatter class implements a formatter that specifies exactly which characters are valid in
each position of the field's text. For example, the following code creates a MaskFormatter that lets
the user to type a five-digit zip code:
zipField = new JFormattedTextField(
createFormatter("#####"));
...
protected MaskFormatter createFormatter(String s) {
MaskFormatter formatter = null;
try {
formatter = new MaskFormatter(s);
} catch (java.text.ParseException exc) {
System.err.println("formatter is bad: " + exc.getMessage());
597
System.exit(-1);
}
return formatter;
}
You can try out the results of the preceding code by running TextInputDemo. Click the Launch
button to run TextInputDemo using Java™ Web Start (download JDK 6). Alternatively, to compile
and run the example yourself, consult the example index.
The following table shows the characters that you can use in the formatting mask:
Character Description
# Any valid number (Character.isDigit).
'
Escape character, used to escape any of the special formatting characters.
(single quote)
U Any character (Character.isLetter). All lowercase letters are mapped to uppercase.
L Any character (Character.isLetter). All uppercase letters are mapped to lowercase.
A Any character or number (Character.isLetter or Character.isDigit).
? Any character (Character.isLetter).
* Anything.
H Any hex character (0-9, a-f or A-F).
When specifying formatters, keep in mind that each formatter object can be used by at most one
formatted text field at a time. Each field should have at least one formatter associated with it, of
which exactly one is used at any time.
You can specify the formatters to be used by a formatted text field in several ways:
598
the field's value as a guide. If the value is a Date, the formatter is a DateFormatter.
If the value is a Number, the formatter is a NumberFormatter. Other types result in an
instance of DefaultFormatter.
Make the formatted text field use a formatter factory that returns customized
formatter objects.
This is the most flexible approach. It is useful when you want to associate more than
one formatter with a field or add a new kind of formatter to be used for multiple
fields. An example of the former use is a field that interprets the user typing in a
certain way but displays the value (when the user is not typing) in another way. An
example of the latter use is several fields with custom class values, for example,
PhoneNumber. You can set up the fields to use a formatter factory that returns
specialized formatters for phone numbers.
You can set a field's formatter factory either by creating the field using a constructor that takes a
formatter factory argument, or by calling the setFormatterFactory method on the field. To create a
formatter factory, you can often use an instance of DefaultFormatterFactory class. A
DefaultFormatterFactory object enables you to specify the formatters returned when a value is
being edited, is not being edited, or has a null value.
The following figures show an application based on the FormattedTextFieldDemo example that
uses formatter factories to set multiple editors for the Loan Amount and APR fields. While the user
is editing the Loan Amount, the $ character is not used so that the user is not forced to type it.
Similarly, while the user is editing the APR field, the % character is not required.
Click the Launch button to run FormatterFactoryDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
The following code that creates the formatters and sets them up by using instances of the
DefaultFormatterFactory class:
599
Number number = (Number)o;
if (number != null) {
double d = number.doubleValue() * 100.0;
number = new Double(d);
}
return super.valueToString(number);
}
public Object stringToValue(String s)
throws ParseException {
Number number = (Number)super.stringToValue(s);
if (number != null) {
double d = number.doubleValue() / 100.0;
number = new Double(d);
}
return number;
}
};
rateField = new JFormattedTextField(
new DefaultFormatterFactory(
new NumberFormatter(percentDisplayFormat),
new NumberFormatter(percentDisplayFormat),
percentEditFormatter));
...
amountDisplayFormat = NumberFormat.getCurrencyInstance();
amountDisplayFormat.setMinimumFractionDigits(0);
amountEditFormat = NumberFormat.getNumberInstance();
percentDisplayFormat = NumberFormat.getPercentInstance();
percentDisplayFormat.setMinimumFractionDigits(2);
percentEditFormat = NumberFormat.getNumberInstance();
percentEditFormat.setMinimumFractionDigits(2);
The boldface code highlights the calls to DefaultFormatterFactory constructors. The first
argument to the constructor specifies the default formatter to use for the formatted text field. The
second argument specifies the display formatter, which is used when the field does not have the
focus. The third argument specifies the edit formatter, which is used when the field has the focus.
The code does not use a fourth argument, but if it did, the fourth argument would specify the null
formatter, which is used when the field's value is null. Because no null formatter is specified, the
default formatter is used when the value is null.
The code customizes the formatter that uses percentEditFormat by creating a subclass of the
NumberFormatter class. This subclass overrides the valueToString and stringToValue methods
of NumberFormatter so that they convert the displayed number to the value actually used in
calculations, and convert the value to a number. Specifically, the displayed number is 100 times the
actual value. The reason is that the percent format used by the display formatter automatically
displays the text as 100 times the value, so the corresponding editor formatter must display the text at
the same value. The FormattedTextFieldDemo example does not need to take care of this
conversion because this demo uses only one format for both display and editing.
You can find the code for the entire program in FormatterFactoryDemo.java.
The following tables list some of the commonly used APIs for using formatted text fields.
600
Classes Related to Formatted Text Fields
Class or Interface Purpose
Subclass of JTextField that supports formatting
JFormattedTextField
arbitrary values.
The superclass of all formatters for
JFormattedTextField. A formatter enforces
editing policies and navigation policies, handles
JFormattedTextField.AbstractFormatter
string-to-object conversions, and manipulates the
JFormattedTextField as necessary to enforce the
desired policy.
The superclass of all formatter factories. Each
JFormattedTextField uses a formatter factory to
JFormattedTextField.AbstractFormatterFactory
obtain the formatter that best corresponds to the text
field's state.
The formatter factory normally used. Provides
DefaultFormatterFactory formatters based on details such as the passed-in
parameters and focus state.
Subclass of
JFormattedTextField.AbstractFormatter that
DefaultFormatter
formats arbitrary objects by using the toString
method.
Subclass of DefaultFormatter that formats and
edits strings using a specified character mask. (For
MaskFormatter
example, seven-digit phone numbers can be
specified by using "###-####".)
Subclass of DefaultFormatter that uses an
InternationalFormatter instance of java.text.Format to handle
conversion to and from a String.
Subclass of InternationalFormatter that
NumberFormatter supports number formats by using an instance of
NumberFormat.
Subclass of InternationalFormatter that
DateFormatter supports date formats by using an instance of
DateFormat.
JFormattedTextField Methods
Method or Constructor Purpose
Creates a new formatted text field. The Object
argument, if present, specifies the initial value of
JFormattedTextField() the field and causes an appropriate formatter
JFormattedTextField(Object) factory to be created. The Format or
JFormattedTextField(Format) AbstractFormatter argument specifies the
JFormattedTextField(AbstractFormatter) format or formatter to be used for the field, and
JFormattedTextField(AbstractFormatterFactory) causes an appropriate formatter factory to be
JFormattedTextField(AbstractFormatterFactory, created. The AbstractFormatterFactory
Object) argument specifies the formatter factory to be
used, which determines which formatters are used
for the field.
601
Sets or obtains the value of the formatted text
field. You must cast the return type based on how
void setValue(Object) the JFormattedTextField has been configured. If
Object getValue() the formatter has not been set yet, calling
setValue sets the formatter to one returned by the
field's formatter factory.
Sets the object that determines the formatters used
void
for the formatted text field. The object is often an
setFormatterFactory(AbstractFormatterFactory)
instance of the DefaultFormatterFactory class.
Obtains the formatter of the formatted text field.
AbstractFormatter getFormatter() The formatter is often an instance of the
DefaultFormatter class.
Specifies the outcome of a field losing the focus.
Possible values are defined in
JFormattedTextField as COMMIT_OR_REVERT
void setFocusLostBehavior(int)
(the default), COMMIT (commit if valid, otherwise
leave everything the same), PERSIST (do nothing),
and REVERT (change the text to reflect the value).
Sets the value to the object represented by the
field's text, as determined by the field's formatter.
void commitEdit()
If the text is invalid, the value remains the same
and a ParseException is thrown.
Returns true if the formatter considers the current
boolean isEditValid() text to be valid, as determined by the field's
formatter.
DefaultFormatter Options
Method Purpose
void
Sets or obtains values when edits are pushed back to the
setCommitsOnValidEdit(boolean)
JFormattedTextField. If true, commitEdit is called after
boolean
every valid edit. This property is false by default.
getCommitsOnValidEdit()
Sets or obtains the behavior when inserting characters. If true,
new characters overwrite existing characters in the model as they
void setOverwriteMode(boolean) are inserted. The default value of this property is true in
boolean getOverwriteMode() DefaultFormatter (and thus in MaskFormatter) and false in
InternationalFormatter (and thus in DateFormatter and
NumberFormatter).
Sets or interprets whether the value being edited is allowed to be
invalid for a length of time. It is often convenient to enable the
void setAllowsInvalid(boolean) user to type invalid values until the commitEdit method is
boolean getAllowsInvalid() attempted. DefaultFormatter initializes this property to true.
Of the standard Swing formatters, only MaskFormatter sets this
property to false.
This table lists examples that use formatted text fields and points to where those examples are
described.
602
Example Where Described Notes
FormattedTextFieldDemo This section Uses four formatted text fields.
SpinnerDemo How to Use Spinners Customizes the appearance of the formatted text
fields used by two spinners.
SliderDemo3 How to Use Sliders Pairs a formatted text field with a slider to
enable an integer value to be edited.
Converter Using Models Each ConversionPanel pairs a formatted text
field with a slider.
TextInputDemo This section Shows how to use text fields, spinners, and
formatted text fields together, and demonstrates
how to use MaskFormatter. Includes code for
selecting the text of the field that has just
received the focus.
FormatterFactoryDemo This section A variation on FormattedTextFieldDemo that
uses formatter factories to specify multiple
formatters for two formatted text fields.
RegexFormatter Regular Expression A regular expression formatter that includes
Based AbstractFormatter source code and information about how it was
(in The Swing implemented.
Connection
A frame, implemented as an instance of the JFrame class, is a window that has decorations such as a
border, a title, and supports button components that close or iconify the window. Applications with a
GUI usually include at least one frame. Applets sometimes use frames, as well.
To make a window that is dependent on another window — disappearing when the other window is
iconified, for example — use a dialog instead of frame.. To make a window that appears within
another window, use an internal frame.
603
Here is a picture of the extremely plain window created by the FrameDemo demonstration
application. You can find the source code in FrameDemo.java. You can run FrameDemo ( download
JDK 6).
The following FrameDemo code shows how to create and set up a frame.
//1. Create the frame.
JFrame frame = new JFrame("FrameDemo");
1. The first line of code creates a frame using a constructor that lets you set the frame
title. The other frequently used JFrame constructor is the no-argument constructor.
2. Next the code specifies what happens when your user closes the frame. The
EXIT_ON_CLOSE operation exits the program when your user closes the frame. This
behavior is appropriate for this program because the program has only one frame, and
closing the frame makes the program useless.
3. The next bit of code adds a blank label to the frame content pane. If you're not already
familiar with content panes and how to add components to them, please read Adding
Components to the Content Pane.
For frames that have menus, you'd typically add the menu bar to the frame here using
the setJMenuBar method. See How to Use Menus for details.
4. The pack method sizes the frame so that all its contents are at or above their preferred
sizes. An alternative to pack is to establish a frame size explicitly by calling setSize
or setBounds (which also sets the frame location). In general, using pack is
preferable to calling setSize, since pack leaves the frame layout manager in charge
of the frame size, and layout managers are good at adjusting to platform dependencies
and other factors that affect component size.
604
This example does not set the frame location, but it is easy to do so using either the
setLocationRelativeTo or setLocation method. For example, the following code
centers a frame onscreen:
frame.setLocationRelativeTo(null);
5. Calling setVisible(true) makes the frame appear onscreen. Sometimes you might
see the show method used instead. The two usages are equivalent, but we use
setVisible(true) for consistency's sake.
By default, window decorations are supplied by the native window system. However, you can
request that the look-and-feel provide the decorations for a frame. You can also specify that the
frame have no window decorations at all, a feature that can be used on its own, or to provide your
own decorations, or with full-screen exclusive mode.
Besides specifying who provides the window decorations, you can also specify which icon is used to
represent the window. Exactly how this icon is used depends on the window system or look and feel
that provides the window decorations. If the window system supports minimization, then the icon is
used to represent the minimized window. Most window systems or look and feels also display the
icon in the window decorations. A typical icon size is 16x16 pixels, but some window systems use
other sizes.
The following snapshots show three frames that are identical except for their window decorations.
As you can tell by the appearance of the button in each frame, all three use the Java look and feel.
The first uses decorations provided by the window system, which happen to be Microsoft Windows,
but could as easily be any other system running the Java platform.The second and third use window
decorations provided by the Java look and feel. The third frame uses Java look and feel window
decorations, but has a custom icon.
Window decorations provided by the Window decorations provided by the Custom icon; window decorations
look and feel window system provided by the look and feel
Here is an example of creating a frame with a custom icon and with window decorations provided by
the look and feel:
As the preceding code snippet implies, you must invoke the setDefaultLookAndFeelDecorated
method before creating the frame whose decorations you wish to affect. The value you set with
605
setDefaultLookAndFeelDecorated is used for all subsequently created JFrames. You can switch
back to using window system decorations by invoking
JFrame.setDefaultLookAndFeelDecorated(false). Some look and feels might not support
window decorations; in this case, the window system decorations are used.
The full source code for the application that creates the frames pictured above is in
FrameDemo2.java. Besides showing how to choose window decorations, FrameDemo2 also shows
how to disable all window decorations and gives an example of positioning windows. It includes two
methods that create the Image objects used as icons — one is loaded from a file, and the other is
painted from scratch.
Try this::
1. Click the Launch button to run the Frame Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
By default, when the user closes a frame onscreen, the frame is hidden. Although invisible, the frame
still exists and the program can make it visible again. If you want different behavior, then you need
to either register a window listener that handles window-closing events, or you need to specify
default close behavior using the setDefaultCloseOperation method. You can even do both.
The argument to setDefaultCloseOperation must be one of the following values, the first three of
which are defined in the WindowConstants interface (implemented by JFrame, JInternalPane, and
JDialog):
DO_NOTHING_ON_CLOSE
Do not do anything when the user requests that the window close. Instead, the
program should probably use a window listener that performs some other action in its
windowClosing method.
HIDE_ON_CLOSE (the default for JDialog and JFrame)
Hide the window when the user closes it. This removes the window from the screen
but leaves it displayable.
606
DISPOSE_ON_CLOSE (the default for JInternalFrame)
Hide and dispose of the window when the user closes it. This removes the window
from the screen and frees up any resources used by it.
EXIT_ON_CLOSE (defined in the JFrame class)
Exit the application, using System.exit(0). This is recommended for applications
only. If used within an applet, a SecurityException may be thrown.
Note: DISPOSE_ON_CLOSE can have results similar to EXIT_ON_CLOSE if only one window is
onscreen. More precisely, when the last displayable window within the Java virtual machine (VM) is
disposed of, the VM may terminate. See AWT Threading Issues for details.
The default close operation is executed after any window listeners handle the window-closing event.
So, for example, assume that you specify that the default close operation is to dispose of a frame.
You also implement a window listener that tests whether the frame is the last one visible and, if so,
saves some data and exits the application. Under these conditions, when the user closes a frame, the
window listener will be called first. If it does not exit the application, then the default close operation
— disposing of the frame — will then be performed.
For more information about handling window-closing events, see How to Write Window Listeners.
Besides handling window-closing events, window listeners can also react to other window state
changes, such as iconification and activation.
The following tables list the commonly used JFrame constructors and methods. Other methods you
might want to call are defined by the java.awt.Frame, java.awt.Window, and
java.awt.Component classes, from which JFrame descends.
Because each JFrame object has a root pane, frames have support for interposing input and painting
behavior in front of the frame children, placing children on different "layers", and for Swing menu
bars. These topics are introduced in Using Top-Level Containers and explained in detail in How to
Use Root Panes.
607
DISPOSE_ON_CLOSE
EXIT_ON_CLOSE
608
Container getContentPane() visible GUI components within the frame.
JRootPane createRootPane() Create, set, or get the frame root pane. The root pane manages the
void setRootPane(JRootPane) interior of the frame including the content pane, the glass pane,
JRootPane getRootPane() and so on.
void setJMenuBar(JMenuBar) Set or get the frame menu bar to manage a set of menus for the
JMenuBar getJMenuBar() frame.
void setGlassPane(Component) Set or get the frame glass pane. You can use the glass pane to
Component getGlassPane() intercept mouse events or paint on top of your program GUI.
void
Set or get the frame layered pane. You can use the frame layered
setLayeredPane(JLayeredPane)
pane to put components on top of or behind other components.
JLayeredPane getLayeredPane()
All of the standalone applications in this trail use JFrame. The following table lists a few and tells
you where each is discussed.
Example Where Described Notes
The Example
FrameDemo Displays a basic frame with one component.
Explained
Specifying Window
FrameDemo2 Lets you create frames with various window decorations.
Decorations
A study in creating and destroying windows, in
Framework —
implementing a menu bar, and in exiting an application.
LayeredPaneDemo
How to Use Layered Illustrates how to use a layered pane (but not the frame
Panes layered pane).
GlassPaneDemo The Glass Pane Illustrates the use of a frame glass pane.
MenuDemo How to Use Menus Shows how to put a JMenuBar in a JFrame.
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 is not 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:
609
Try this:
1. Click the Launch button to run InternalFrameDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
2. Create new internal frames using the Create item in the Document menu.
Each internal frame comes up 30 pixels lower and to the right of the place
where the previous internal frame first appeared. This functionality is
implemented in the MyInternalFrame class, which is the custom subclass of
JInternalFrame.
The following code, taken from InternalFrameDemo.java, creates the desktop and internal frames
in the previous example.
public MyInternalFrame() {
super("Document #" + (++openFrameCount),
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable
610
//...Create the GUI and put it in the window...
//...Then set the window size or call pack...
...
//Set the window's location.
setLocation(xOffset*openFrameCount, yOffset*openFrameCount);
}
The code for using internal frames is similar in many ways to the code for using regular Swing
frames. Because internal frames have root panes, setting up the GUI for a JInternalFrame is very
similar to setting up the GUI for a JFrame. JInternalFrame also provides other API, such as pack,
that makes it similar to JFrame.
Note: Just as for a regular frame, you must invoke setVisible(true) or show() on an internal
frame to display it. The internal frame does not appear until you explicitly make it visible.
Internal frames are not windows or top-level containers, however, which makes them different from
frames. For example, you must add an internal frame to a container (usually a JDesktopPane); an
internal frame cannot be the root of a containment hierarchy. Also, internal frames do not generate
window events. Instead, the user actions that would cause a frame to fire window events cause an
internal frame to fire internal frame events.
Because internal frames are implemented with platform-independent code, they add some features
that frames cannot give you. One such feature is that internal frames give you more control over their
state and capabilities than frames do. You can programatically iconify or maximize an internal
frame. You can also specify what icon goes in the internal frame's title bar. You can even specify
whether the internal frame has the window decorations to support resizing, iconifying, closing, and
maximizing.
Another feature is that internal frames are designed to work within desktop panes. The
JInternalFrame API contains methods such as moveToFront that work only if the internal frame's
container is a layered pane such as a JDesktopPane.
If you have built any programs using JFrame and the other Swing components, then you already
know a lot about how to use internal frames. The following list summarizes the rules for using
internal frames. For additional information, see How to Make Frames and The JComponent Class.
You must set the size of the internal frame.
If you do not set the size of the internal frame, it will have zero size and thus never be
visible. You can set the size using one of the following methods: setSize, pack, or
setBounds.
As a rule, you should set the location of the internal frame.
If you do not set the location of the internal frame, it will come up at 0,0 (the upper
left of its container). You can use the setLocation or setBounds method to specify
the upper left point of the internal frame, relative to its container.
To add components to an internal frame, you add them to the internal frame's content pane.
This is exactly like the JFrame situation. See Adding Components to the Content Pane
for details.
611
Dialogs that are internal frames should be implemented using JOptionPane or
JInternalFrame, not JDialog.
To create a simple dialog, you can use the JOptionPane showInternalXxxDialog
methods, as described in How to Make Dialogs.
You must add an internal frame to a container.
If you do not add the internal frame to a container (usually a JDesktopPane), the
internal frame will not appear.
You need to call show or setVisible on internal frames.
Internal frames are invisible by default. You must invoke setVisible(true) or
show() to make them visible.
Internal frames fire internal frame events, not window events.
Handling internal frame events is almost identical to handling window events. See
How to Write an Internal Frame Listener for more information.
Performance tip: When a desktop has many internal frames, the user might notice that moving
them seems slow. Outline dragging is one way to avoid this problem. With outline dragging, only the
outline of the internal frame is painted at the current mouse position while the internal frame's being
dragged. The internal frame's innards are not repainted at a new position until dragging stops. The
default behavior (called live dragging) is to reposition and repaint some or all of internal frame
continuously while it is being moved; this can be slow if the desktop has many internal frames.
Use the JDesktopPane method setDragMode* to specify outline dragging. For example:
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
The following tables list the commonly used JInternalFrame constructors and methods, as well as
a few methods that JDesktopPane provides. Besides the API listed in this section, JInternalFrame
inherits useful API from its superclasses, JComponent, Component, and Container. See The
JComponent Class for lists of methods from those classes.
Like JInternalFrame, JDesktopPane descends from JComponent, and thus provides the methods
described in The JComponent Class. Because JDesktopPane extends JLayeredPane, it also supports
the methods described in The Layered Pane API.
The API for using internal frames falls into these categories:
612
JInternalFrame(String, boolean, boolean) specify whether the internal frame should contain
JInternalFrame(String, boolean, boolean, decorations allowing the user to resize, close,
boolean) maximize, and iconify the internal frame (specified
JInternalFrame(String, boolean, boolean, in that order). The default value for each boolean
boolean, boolean) argument is false, which means that the operation
is not allowed.
static int
showInternalConfirmDialog(Component,
Object)
static String
showInternalInputDialog(Component, Object)
Create a JInternalFrame that simulates a dialog.
static Object
See How to Make Dialogs for details.
showInternalMessageDialog(Component,
Object)
static int
showInternalOptionDialog(Component,
Object, String, int, int, Icon, Object[], Object)
Adding Components to the Internal Frame
Method Purpose
Set or get the internal frame's content pane, which generally
void setContentPane(Container)
contains all of the internal frame's GUI, with the exception of the
Container getContentPane()
menu bar and window decorations.
void setJMenuBar(JMenuBar)
Set or get the internal frame's menu bar.
JMenuBar getJMenuBar()
void
setLayeredPane(JLayeredPane) Set or get the internal frame's layered pane.
JLayeredPane getLayeredPane()
Specifying the Internal Frame's Visibility, Size, and Location
Method Purpose
Make the internal frame visible (if true) or invisible (if false). You
void setVisible(boolean) should invoke setVisible(true) on each JInternalFrame before
adding it to its container. (Inherited from Component).
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 Component).
void setLocation(int, int)
void
setBounds(Rectangle) Explicitly set the size and location of the internal frame. (Inherited from
void setBounds(int, int, Component).
int, int)
void setSize(Dimension)
Explicitly set the size of the internal frame. (Inherited from Component).
void setSize(int, int)
Performing Window Operations on the Internal Frame
Method Purpose
Set or get what the internal frame does when
void setDefaultCloseOperation(int)
the user attempts to "close" the internal
int getDefaultCloseOperation()
frame. The default value is
613
DISPOSE_ON_CLOSE. Other possible values
are DO_NOTHING_ON_CLOSE and
HIDE_ON_CLOSE See Responding to Window-
Closing Events for details.
void Add or remove an internal frame listener
addInternalFrameListener(InternalFrameListener) (JInternalFrame's equivalent of a window
void listener). See How to Write an Internal Frame
removeInternalFrameListener(InternalFrameListener) Listener for more information.
If the internal frame's parent is a layered pane
void moveToFront() such as a desktop pane, moves the internal
void moveToBack() frame to the front or back (respectively) of its
layer.
Set or get whether the internal frame is
currently closed. The argument to setClosed
void setClosed(boolean) must be true. When reopening a closed
boolean isClosed() internal frame, you make it visible and add it
to a container (usually the desktop pane you
originally added it to).
void setIcon(boolean) Iconify or deiconify the internal frame, or
boolean isIcon() determine whether it is currently iconified.
void setMaximum(boolean) Maximize or restore the internal frame, or
boolean isMaximum() determine whether it is maximized.
Set or get whether the internal frame is the
void setSelected(boolean)
currently "selected" (activated) internal
boolean isSelected()
frame.
Controlling Window Decorations and Capabilities
Method Purpose
void setFrameIcon(Icon) Set or get the icon displayed in the title bar of the internal frame
Icon getFrameIcon() (usually in the top-left corner).
void setClosable(boolean)
Set or get whether the user can close the internal frame.
boolean isClosable()
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)
Set or get whether the internal frame can be resized.
boolean isResizable()
void setTitle(String)
Set or get the window title.
String getTitle()
Using the JDesktopPane API
Constructor or Method Purpose
JDesktopPane() Creates a new instance of JDesktopPane.
JInternalFrame[]
Returns all JInternalFrame objects that the desktop contains.
getAllFrames()
614
Returns all JInternalFrame objects that the desktop contains that are
JInternalFrame[]
in the specified layer. See How to Use Layered Panes for information
getAllFramesInLayer(int)
about layers.
Set or get the drag mode used for internal frames in this desktop. The
void setDragMode(int) integer can be either JDesktopPane.LIVE_DRAG_MODE or
int getDragMode() JDesktopPane.OUTLINE_DRAG_MODE. The default for the Java look
and feel is live-drag mode.
The following examples use internal frames. Because internal frames are similar to regular frames,
you should also look at Examples that Use Frames.
Example Where Described Notes
Implements an internal frame that appears at an
MyInternalFrame This page.
offset to the previously created internal frame.
Lets you create internal frames (instances of
InternalFrameDemo This page. MyInternalFrame) that go into the application's
JDesktopPane.
How to Write an Demonstrates listening for internal frame events.
InternalFrameEventDemo Internal Frame Also demonstrates positioning internal frames
Listener within a desktop pane.
With the JLabel class, you can display unselectable text and images. If you need to create a
component that displays a string, an image, or both, you can do so by using or extending JLabel. If
the component is interactive and has a certain state, use a button instead of a label.
By specifying HTML code in a label's text, you can give the label various characteristics such as
multiple lines, multiple fonts or multiple colors. If the label uses just a single color or font, you can
avoid the overhead of HTML processing by using the setForeground or setFont method instead.
See Using HTML in Swing Components for details.
Note that labels are not opaque by default. If you need to paint the label's background, it is
recommended that you turn its opacity property to "true". The following code snippet shows how to
do this.
label.setOpaque(true);
The following picture introduces an application that displays three labels. The window is divided
into three rows of equal height; the label in each row is as wide as possible.
615
Try this:
1. Click the Launch button to run LabelDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the
example index.
2. Resize the window so you can see how the labels' contents are placed within
the labels' drawing area.
All the label contents have default vertical alignment — that is, the label
contents are centered vertically in the label's drawing area. The top label,
which contains both an image and text, has horizontal center alignment. The
second label, which contains just text, has left (leading) alignment, which is
the default for text-only labels in left-to-right languages. The third label,
which contains just an image, has horizontal center alignment, which is the
default for image-only labels.
Below is the code from LabelDemo.java that creates the labels in the previous example.
Often, a label describes another component. When this occurs, you can improve your program's
accessibility by using the setLabelFor method to identify the component that the label describes.
For example:
amountLabel.setLabelFor(amountField);
The preceding code, taken from the FormattedTextFieldDemo example discussed in How to Use
Formatted Text Fields, lets assistive technologies know that the label (amountLabel) provides
616
information about the formatted text field (amountField). For more information about assistive
technologies, see How to Support Assistive Technologies.
The following tables list the commonly used JLabel constructors and methods. Other methods you
are likely to call are defined by the Component and JComponent classes. They include setFont,
setForeground, setBorder, setOpaque, and setBackground. See The JComponent Class for
details. The API for using labels falls into three categories:
Note: In the following API, do not confuse label alignment with 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.
Label alignment simply determines where, inside the label's painting area, the label's contents are
positioned. Typically, the label's painting area is exactly the size needed to paint on the label and
thus label alignment is irrelevant. For more information about X and Y alignment, see How to Use
BoxLayout.
617
character in the text). Not all types of look and feel may support
this feature.
Sets or gets the image displayed by the label when it is disabled.
void setDisabledIcon(Icon)
If you do not specify a disabled image, then the look and feel
Icon getDisabledIcon()
creates one by manipulating the default image.
Fine Tuning the Label's Appearance
Method Purpose
void Sets or gets the area on the label where its contents should be
setHorizontalAlignment(int) placed. The SwingConstants interface defines five possible values
void setVerticalAlignment(int) for horizontal alignment: LEFT, CENTER (the default for image-only
int getHorizontalAlignment() labels), RIGHT, LEADING (the default for text-only labels), TRAILING.
int getVerticalAlignment() For vertical alignment: TOP, CENTER (the default), and BOTTOM.
void
setHorizontalTextPosition(int) Sets or gets the location where the label's text will be placed,
void relative to the label's image. The SwingConstants interface defines
setVerticalTextPosition(int) five possible values for horizontal position: LEADING, LEFT, CENTER,
int RIGHT, and TRAILING (the default). For vertical position: TOP,
getHorizontalTextPosition() CENTER (the default), and BOTTOM.
int getVerticalTextPosition()
void setIconTextGap(int) Sets or gets the number of pixels between the label's text and its
int getIconTextGap() image.
Supporting Accessibility
Method Purpose
void setLabelFor(Component)
Sets or gets which component the label describes.
Component getLabelFor()
The following table lists some of the many examples that use labels.
Example Where Described Notes
Shows how to specify horizontal and vertical
LabelDemo This section alignment as well as how to align a label's text and
image.
HtmlDemo
Using HTML in Lets you experiment with specifying HTML text for
Swing Components a label.
Demonstrates possible alignment problems when
Fixing Alignment
BoxAlignmentDemo using a label in a vertical box layout. Shows how to
Problems
solve the problem.
DialogDemo
How to Use Uses a changeable label to display instructions and
Dialogs provide feedback.
How to Use Split
Displays an image using a label inside of a scroll
SplitPaneDemo Panes and How to
pane.
Use Lists
SliderDemo2 How to Use Sliders Uses JLabel to provide labels for a slider.
Implements a label subclass, ColorRenderer, to
TableDialogEditDemo How to Use Tables
display colors in table cells.
618
How to Use
FormattedTextFieldDemo Formatted Text
Has four rows, each containing a label and the
formatted text field it describes.
Fields
TextComponentDemo has an inner class
TextComponentDemo
Text Component (CaretListenerLabel) that extends JLabel to
Features provide a label that listens for events, updating itself
based on the events.
ColorChooserDemo
How to Use Color Uses an opaque label to display the currently chosen
Choosers color against a fixed-color background.
A layered pane is a Swing container that provides a third dimension for positioning components:
depth, also known as Z order. When adding a component to a layered pane, you specify its depth as
an integer. The higher the number, closer the component is to the "top" position within the container.
If components overlap, the "closer" components are drawn on top of components at a lower depth.
The relationship between components at the same depth is determined by their positions within the
depth.
Note: The AWT Container has an API that allows you to manipulate component Z order. For more
information, see the AWT Focus Specification.
Every Swing container that has a root pane — such as JFrame, JApplet, JDialog, or
JInternalFrame — automatically has a layered pane. Most programs do not explicitly use the root
pane's layered pane, so this section will not discuss it. You can find information about it in The Root
Pane, which provides an overview, and The Layered Pane, which has further details. This section
tells you how to create your own layered pane and use it anywhere you can use a regular Swing
container.
Swing provides two layered pane classes. The first, JLayeredPane, is the class that root panes use
and is the class used by the example in this section. The second, JDesktopPane, is a JLayeredPane
subclass that is specialized for the task of holding internal frames. For examples of using
JDesktopPane, see How to Use Internal Frames.
Here is a picture of an application that creates a layered pane and places overlapping, colored labels
at different depths:
619
Try this::
1. Click the Launch button to run the LayeredPane Demo using Java™ Web
Start (download JDK 6). Alternatively, to compile and run the example
yourself, consult the example index.
2. Move the mouse around in the lower part of the window. The image of Duke
drags behind the green and red labels, but in front of the other three labels.
3. Use the combo box at the top of the window to change Duke's depth. Use the
check box to set whether Duke is in the top position — position 0 — within
the current depth.
Here is the code from LayeredPaneDemo.java that creates the layered pane:
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300, 310));
layeredPane.setBorder(BorderFactory.createTitledBorder(
"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. Although we do not show the code
here, the example adds the layered pane to the frame's content pane.
620
As we will 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 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 within that depth can change dynamically.
Here is the code from the sample program that adds the colored labels to the layered pane:
for (int i = 0; i < ...number of labels...; i++) {
JLabel label = createColoredLabel(...);
layeredPane.add(label, new Integer(i));
...
}
You can find the implementation of the createColoredLabel method in the source code for the
program. It just creates an opaque JLabel initialized with a background color, a border, some text,
and a size.
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 iteration variable to specify depths. The actual values do not 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.
As you can see from the example program, if components overlap, components at a higher depth are
on top of components at a lower depth. To change a component depth dynamically, use the
setLayer method. In the example, the user can change Duke's layer by making a selection from the
combo box. Here is the actionPerformed method of the action listener registered on the combo
box:
621
setLayer that takes only the component and the new depth. That method puts the component at the
bottom position in its depth.
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 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.
The following code creates the label that displays Duke's image, and then adds the label to the
layered pane.
final ImageIcon icon = createImageIcon("images/dukeWaveRed.gif");
...
dukeLabel = new JLabel(icon);
...
dukeLabel.setBounds(15, 225,
icon.getIconWidth(),
icon.getIconHeight());
...
layeredPane.add(dukeLabel, new Integer(2), 0);
This code uses the three-argument version of the add method. The third argument specifies the Duke
label position within its depth, which determines the component's relationship with other components
at the same depth.
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.
622
Laying Out Components in a Layered Pane
By default a layered pane has no layout manager. This means that you typically have to write the
code that positions and sizes the components you put in a layered pane.
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 is 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. You can run
LayeredPaneDemo2 ( download JDK 6). If you want to compile the example, consult the example
index for a list of all necessary files.
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 following tables list the commonly used JLayeredPane constructors and methods. Other
methods you are most likely to invoke on a JLayeredPane object are those it inherits from its
superclasses, such as setBorder, setPreferredSize, and so on. See The JComponent API for
tables of commonly used inherited methods.
The API for using layered pane falls into these categories:
623
Creating or Getting a Layered Pane
Layering Components
Setting Component's Intra-Layer Positions
Layering Components
Method Purpose
Add the specified component to the layered pane. The second
argument, when present, is an Integer that indicates the layer.
The third argument, when present, indicates the component's
void add(Component)
position within its layer. If you use the one-argument version of
void add(Component, Object)
this method, the component is added to layer 0. If you use the
void add(Component, Object, int)
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 indicates
void setLayer(Component, int, the layer. The third argument, when present, indicates the
int) component's position within its layer.
int getLayer(Component)
Get the layer for the specified component.
int getLayer(JComponent)
Get the number of components in the specified layer. The value
int
returned by this method can be useful for computing position
getComponentCountInLayer(int)
values.
Component[]
Get an array of all the components in the specified layer.
getComponentsInLayer(int)
int highestLayer()
Compute the highest or lowest layer currently in use.
int lowestLayer()
Setting Components' Intra-Layer Positions
Method Purpose
void setPosition(Component, int)
Set or get the position for the specified component within its layer.
int getPosition(Component)
void moveToFront(Component)
Move the specified component to the front or back of its layer.
void moveToBack(Component)
This table shows the examples that use JLayeredPane and where those examples are described.
Example Where Described Notes
Illustrates layers and intra-layer positions of a
LayeredPaneDemo This section
JLayeredPane.
LayeredPaneDemo2 This section Uses a layout manager to help lay out the components
624
in a layered pane.
RootLayeredPaneDemo The Layered Pane
A version of LayeredPaneDemo modified to use the
root pane's layered pane.
How to Use Internal
InternalFrameDemo Uses a JDesktopFrame to manage internal frames.
Frames
In addition to lists, the following Swing components present multiple selectable items to the user:
combo boxes, menus, tables, and groups of check boxes or radio buttons. To display hierarchical
data, use a tree.
The following figures shows two applications that use lists. This section uses these examples as a
basis for the discussions that follow.
ListDialog ListDemo
(used by ListDialogRunner)
Try this:
1. Click the Launch button to run ListDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the
example index.
3. To bring up the ListDialog, click the Pick a new name... button in the window
titled Name That Baby.
The resulting dialog is a ListDialog instance that has been customized to have
the title Name Chooser.
625
4. In ListDemo, try adding (hiring) and removing (firing) a few items.
Creating a Model
Initializing a List
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
Creating a Model
DefaultListModel — everything is pretty much taken care of for you. The examples in
this page use DefaultListModel.
AbstractListModel — you manage the data and invoke the "fire" methods. For this
approach, you must subclass AbstractListModel and implement the getSize and
getElementAt methods inherited from the ListModel interface.
ListModel — you manage everything.
Initializing a List
Here is the code from ListDialog.java that creates and sets up its list:
list = new JList(data); //data has type Object[]
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
...
JScrollPane listScroller = new JScrollPane(list);
listScroller.setPreferredSize(new Dimension(250, 80));
The code passes an array to the list's constructor. The array is filled with strings that were passed in
from another object. In our example, the strings happen to be boys' names.
Other JList constructors let you initialize a list from a Vector 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.
The call to setSelectionMode specifies how many items the user can select, and whether they must
be contiguous; the next section tells you more about selection modes.
The call to setLayoutOrientation lets the list display its data in multiple columns. The value
JList.HORIZONTAL_WRAP specifies that the list should display its items from left to right before
wrapping to a new row. Another possible value is JList.VERTICAL_WRAP, which specifies that the
data be displayed from top to bottom (as usual) before wrapping to a new column. The following
figures show these two wrapping possibilities, together with the default, JList.VERTICAL.
626
HORIZONTAL_WRAP VERTICAL_WRAP VERTICAL
A list uses an instance of ListSelectionModel to manage its selection. By default, a list selection
model allows any combination of items to be selected at a time. You can specify a different selection
mode by calling the setSelectionMode method on the list. For example, both ListDialog and
ListDemo set the selection mode to SINGLE_SELECTION (a constant defined by
ListSelectionModel) so that only one item in the list can be selected. The following table describes
the three list selection modes:
Mode Description
SINGLE_SELECTION Only one item can be selected at a time. When the user selects an
item, any previously selected item is deselected first.
SINGLE_INTERVAL_SELECTION Multiple, contiguous items can be selected. When the user begins
a new selection range, any previously selected items are
deselected first.
MULTIPLE_INTERVAL_SELECTION The default. Any combination of items can be selected. The user
must explicitly deselect items.
No matter which selection mode your list uses, the list fires list selection events whenever the
selection changes. You can process these events by adding a list selection listener to the list with the
addListSelectionListener method. A list selection listener must implement one method:
valueChanged. Here is the valueChanged method for the listener in ListDemo:
if (list.getSelectedIndex() == -1) {
//No selection, disable fire button.
fireButton.setEnabled(false);
627
} else {
//Selection, enable the fire button.
fireButton.setEnabled(true);
}
}
}
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 does something 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. ListSelectionDemo is 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.
The ListDemo example that we showed previously features a list whose contents can change. You
can find the source code for ListDemo in ListDemo.java. Here is the ListDemo code that creates a
mutable list model object, puts the initial items in it, and uses the list model to create a list:
listModel = new DefaultListModel();
listModel.addElement("Debbie Scott");
listModel.addElement("Scott Hommel");
listModel.addElement("Alan Sommerer");
The following code snippet shows the actionPerformed method for the action listener registered on
the Fire button. 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 if it is not.
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
}
628
Here is the actionPerformed method for the action listener shared by the Hire button and the text
field:
listModel.insertElementAt(employeeName.getText(), index);
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.
A list uses an object called a cell renderer to display each of its items. The default cell renderer
knows how to display strings and icons and it displays Objects by invoking toString. If you want
to change the way the default renderer display icons or strings, or if you want behavior different than
what is provided by toString, you can implement a custom cell renderer. Take these steps to
provide a custom cell renderer for a list:
We do not provide an example of a list with a custom cell renderer, but we do have an example of a
combo box with a custom renderer — and combo boxes use the same type of renderer as lists. See
the example described in Providing a Custom Renderer.
629
The List API
The following tables list the commonly used JList constructors and methods. Other methods you
are most likely to invoke on a JList object are those such as setPreferredSize that its
superclasses provide. See The JComponent API for tables of commonly used inherited methods.
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 do not 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:
630
void Scroll so that the specified index is visible within the viewport that this
ensureIndexIsVisible(int) list is in.
Managing the List's Selection
Method Purpose
void Register to receive notification of selection
addListSelectionListener(ListSelectionListener) changes.
Set the current selection as indicated. Use
void setSelectedIndex(int)
setSelectionMode to set what ranges of
void setSelectedIndices(int[])
selections are acceptable. The boolean argument
void setSelectedValue(Object, boolean)
specifies whether the list should attempt to scroll
void setSelectionInterval(int, int)
itself so that the selected item is visible.
int getAnchorSelectionIndex()
int getLeadSelectionIndex()
int getSelectedIndex()
int getMinSelectionIndex() Get information about the current selection as
int getMaxSelectionIndex() indicated.
int[] getSelectedIndices()
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()
Set or get whether any items are selected.
boolean isSelectionEmpty()
boolean isSelectedIndex(int) Determine whether the specified index is selected.
Managing List Data
Class or Method Purpose
Given the starting index, search through the list for an item that
starts with the specified string and return that index (or -1 if the
string is not found). The third argument, which specifies the search
direction, can be either Position.Bias.Forward or
int getNextMatch(String, int,
Position.Bias.Backward. For example, if you have a 6-item list,
javax.swing.text.Position.Bias) getNextMatch("Matisse", 5,
javax.swing.text.Position.Bias.Forward) searches for the
string "Matisse" in the item at index 5, then (if necessary) at index
0, index 1, and so on.
void setDragEnabled(boolean) Set or get the property that determines whether automatic drag
boolean getDragEnabled() handling is enabled. See Drag and Drop for more details.
This table shows the examples that use JList and where those examples are described.
Where
Example Notes
Described
SplitPaneDemo How to Use Contains a single-selection, immutable list.
631
Split Panes
ListDemo This section Demonstrates how to add and remove items from a list at
runtime.
ListDialog This section, Implements a modal dialog with a single-selection list.
How to Use
BoxLayout
ListDataEventDemo How to Write a Demonstrates listening for list data events on a list model.
List Data
Listener
ListSelectionDemo How to Write a Contains a list and a table that share the same selection
List Selection model. You can dynamically choose the selection mode.
Listener
SharedModelDemo Using Models Modifies ListSelectionDemo so that the list and table share
the same data model.
CustomComboBoxDemo Providing a Shows how to provide a custom renderer for a combo box.
Custom Because lists and combo boxes use the same type of
Renderer renderer, you can use what you learn there an apply it to
lists. In fact, a list and a combo box can share a renderer.
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 many menu-related components: a menu bar, menus, menu items, radio
button menu items, check box menu items, and separators. As you can see, a menu item can have
either an image or text, or both. You can also specify other properties, such as font and color.
632
The rest of this section teaches you about the menu components and tells you how to use various
menu features:
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
633
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. Other required files are listed in the
example index.
Try this:
Click the Launch button to run the MenuLook Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
Because this code has no event handling, the menus do nothing useful except to look as 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.
//Where the GUI is created:
JMenuBar menuBar;
JMenu menu, submenu;
JMenuItem menuItem;
JRadioButtonMenuItem rbMenuItem;
JCheckBoxMenuItem cbMenuItem;
634
group.add(rbMenuItem);
menu.add(rbMenuItem);
//a submenu
menu.addSeparator();
submenu = new JMenu("A submenu");
submenu.setMnemonic(KeyEvent.VK_S);
...
frame.setJMenuBar(theJMenuBar);
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.
Note: Menu items, like other components, can be in at most one container. If you try to add a menu
item to a second menu, the menu item will be removed from the first menu before being added to the
second. For a way of implementing multiple components that do the same thing, see How to Use
Actions.
Other methods in the preceding code include setAccelerator and setMnemonic, which are
discussed a little later in Enabling Keyboard Operation. The setAccessibleDescription method is
discussed in How to Support Assistive Technologies.
635
To detect when the user chooses a JMenuItem, you can listen for action events (just as you would for
a JButton). To detect when the user chooses a JRadioButtonMenuItem, you can listen for either
action events or item events, as described in How to Use Radio Buttons. For JCheckBoxMenuItems,
you generally listen for item events, as described in How to Use Check Boxes.
Try this:
Click the Launch button to run the Menu Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
636
For examples of handling action and item events, see the button, radio button, and check box
sections, as well as the list of examples at the end of this section.
Menus support two kinds of keyboard alternatives: mnemonics and accelerators. Mnemonics offer a
way to use the keyboard to navigate the menu hierarchy, increasing the accessibility of programs.
Accelerators, on the other hand, offer keyboard shortcuts to bypass navigating the menu hierarchy.
Mnemonics are for all users; accelerators are for power users.
A mnemonic is a key that makes an already visible menu item be chosen. For example, in MenuDemo
the first menu has the mnemonic A, and its second menu item has the mnemonic B. This means that,
when you run MenuDemo with the Java look and feel, pressing the Alt and A keys makes the first
menu appear. While the first menu is visible, pressing the B key (with or without Alt) makes the
second menu item be chosen. A menu item generally displays its mnemonic by underlining the first
occurrence of the mnemonic character in the menu item's text, as the following snapshot shows.
An accelerator is a key combination that causes a menu item to be chosen, whether or not it's visible.
For example, pressing the Alt and 2 keys in MenuDemo makes the first item in the first menu's
submenu be chosen, without bringing up any menus. Only leaf menu items — menus that don't bring
up other menus — can have accelerators. The following snapshot shows how the Java look and feel
displays a menu item that has an accelerator.
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:
Note: Because popup menus, unlike regular menus, aren't always contained by a component,
accelerators in popup menu items don't work unless the popup menu is visible.
637
To bring up a popup menu ( JPopupMenu), you must register a mouse listener on each component
that the popup menu should be associated with. The mouse listener must detect user requests that the
popup menu be brought up.
The exact gesture that should bring up a popup menu varies by look and feel. In Microsoft Windows,
the user by convention brings up a popup menu by releasing the right mouse button while the cursor
is over a component that is popup-enabled. In the Java look and feel, the customary trigger is either
pressing the right mouse button (for a popup that goes away when the button is released) or clicking
it (for a popup that stays up).
Try this:
Click the Launch button to run the PopupMenu Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
Popup menus have a few interesting implementation details. One is that every menu has an
associated popup menu. When the menu is activated, it uses its associated popup menu to show its
menu items.
638
Another detail is that a popup menu itself uses another component to implement the window
containing the menu items. Depending on the circumstances under which the popup menu is
displayed, the popup menu might implement its "window" using a lightweight component (such as a
JPanel), a "mediumweight" component (such as a Panel), or a heavyweight window (something
that inherits from Window).
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.
Because menus are made up of ordinary Swing components, you can easily customize them. For
example, you can add any lightweight component to a JMenu or JMenuBar. And because JMenuBar
uses BoxLayout, you can customize a menu bar's layout just by adding invisible components to it.
Here is an example of adding a glue component to a menu bar, so that the last menu is at the right
edge of the menu bar:
Try this:
Click the Launch button to run the MenuGlue Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
Another way of changing the look of menus is to change the layout managers used to control them.
For example, you can change a menu bar's layout manager from the default left-to-right BoxLayout
to something such as GridLayout.
Try this:
639
Click the Launch button to run the MenuLayout Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
The following tables list the commonly used menu constructors and methods. The API for using
menus falls into these categories:
640
menu item was to use menu's add(Action) method to create the menu item
and add it to the menu. As of 1.3, that method is no longer recommended.
You can instead associate a menu item with an Action using the setAction
method.
void addSeparator() Adds a separator to the current end of the menu.
JMenuItem
Inserts a menu item or separator into the menu at the specified position. The
insert(JMenuItem, int)
first menu item is at position 0, the second at position 1, and so on. The
void insert(String, int)
JMenuItem and String arguments are treated the same as in the
void
corresponding add methods.
insertSeparator(int)
void
remove(JMenuItem) Removes the specified item(s) from the menu. If the argument is an integer,
void remove(int) then it specifies the position of the menu item to be removed.
void removeAll()
Creating, Populating, and Controlling Popup Menus
Constructor or Method Purpose
Creates a popup menu. The optional string argument
JPopupMenu()
specifies the title that a look and feel might display as part
JPopupMenu(String)
of the popup window.
Adds a menu item to the current end of the popup menu. If
the argument is a string, then the menu automatically
creates a JMenuItem object that displays the specified text.
JMenuItem add(JMenuItem) Version Note: Before 1.3, the only way to associate an
JMenuItem add(String) Action with an item in a popup menu was to use the popup
menu's add(Action) method to create the menu item and
add it to the popup menu. As of 1.3, that method is no
longer recommended. You can instead associate a menu
item with an 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 menu item is at position 0, the second at position
void insert(Component, int)
1, and so on. The Component argument specifies the menu
item to add.
Removes the specified item(s) from the menu. If the
void remove(int)
argument is an integer, then it specifies the position of the
void removeAll()
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 heavyweight components in your Swing program, as
static void
described in Bringing Up a Popup Menu. (This is one of
setLightWeightPopupEnabled(boolean)
several reasons to avoid using heavyweight components.)
As a workaround, invoke
JPopupMenu.setLightWeightPopupEnabled(false).
Display the popup menu at the specified x,y position
void show(Component, int, int) (specified in that order by the integer arguments) in the
coordinate system of the specified component.
641
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
JMenuItem() should display. The integer argument specifies the keyboard
JMenuItem(String) mnemonic to use. You can specify any of the relevant VK
JMenuItem(Icon) constants defined in the KeyEvent class. For example, to
JMenuItem(String, Icon) specify the A key, use KeyEvent.VK_A.
JMenuItem(String, int)
JMenuItem(Action) The constructor with the 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()
JCheckBoxMenuItem(String)
Creates a menu item that looks and acts like a check box. The
JCheckBoxMenuItem(Icon)
string argument, if any, specifies the text that the menu item
JCheckBoxMenuItem(String, Icon)
should display. If you specify true for the boolean argument,
JCheckBoxMenuItem(String,
then the menu item is initially selected (checked). Otherwise,
boolean)
the menu item is initially unselected.
JCheckBoxMenuItem(String, Icon,
boolean)
JRadioButtonMenuItem()
JRadioButtonMenuItem(String)
JRadioButtonMenuItem(Icon)
JRadioButtonMenuItem(String, Creates a menu item that looks and acts like a radio button.
Icon) The string argument, if any, specifies the text that the menu
JRadioButtonMenuItem(String, item should display. If you specify true for the boolean
boolean) argument, then the menu item is initially selected. Otherwise,
JRadioButtonMenuItem(Icon, the menu item is initially unselected.
boolean)
JRadioButtonMenuItem(String,
Icon, boolean)
void setState(boolean)
boolean getState() Set or get the selection state of a check box menu item.
(in JCheckBoxMenuItem)
If the argument is true, enable the menu item. Otherwise,
void setEnabled(boolean)
disable the menu item.
Set the mnemonic that enables keyboard navigation to the
void setMnemonic(int) menu or menu item. Use one of the VK constants defined in
the KeyEvent class.
void setAccelerator(KeyStroke) Set the accelerator that activates the menu item.
void setActionCommand(String) Set the name of the action performed by the menu item.
void
Add an event listener to the menu item. See Handling Events
addActionListener(ActionListener)
from Menu Items for details.
void addItemListener(ItemListener)
Set the Action associated with the menu item. See How to
void setAction(Action)
Use Actions for details.
642
Many of the preceding methods are inherited from AbstractButton. See The Button API for
information about other useful methods that AbstractButton provides.
643
borders to them and otherwise customize their painting. Details can be found in Performing Custom
Painting.
In many types of look and feel, panels are opaque by default. Opaque panels work well as content
panes and can help with painting efficiently, as described in Using Top-Level Containers. You can
change a panel's transparency by invoking the setOpaque method. A transparent panel draws no
background, so that any components underneath show through.
An Example
The following picture shows a colored version of the Converter application, which is discussed in
more detail in Using Models.
One JPanel instance — colored red in the preceding snapshot — serves as a content
pane for the application's frame. This content pane uses a top-to-bottom BoxLayout 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 custom JPanel subclass named ConversionPanel — colored
cyan — are used to contain components and coordinate communication between
components. These ConversionPanel panels also have titled borders, which describe
their contents and enclose the contents with a line. Each ConversionPanel panel uses
a left-to-right BoxLayout object to lay out its contents.
In each ConversionPanel, a JPanel instance — colored magenta — is used to
ensure the proper size and position of the combo box. Each of these JPanel instances
uses a top-to-bottom BoxLayout object (helped by an invisible space-filling
component) to lay out the combo box.
In each ConversionPanel, an instance of an unnamed JPanel subclass — colored
blue — groups two components (a text field and a slider) and restricts their size. Each
of these JPanel instances uses a top-to-bottom BoxLayout object to lay out its
contents.
644
As the Converter example demonstrates, 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.
Like other containers, a panel uses a layout manager to position and size its components. By default,
a panel's layout manager is an instance of FlowLayout, which places the panel's contents in a row.
You can easily make a panel use any other layout manager by invoking the setLayout method or by
specifying a layout manager when creating the panel. The latter approach is preferable for
performance reasons, since it avoids the unnecessary creation of a FlowLayout object.
Here is an example of how to set the layout manager when creating the panel.
This approach does not work with BoxLayout, since the BoxLayout constructor requires a pre-
existing container. Here is an example that uses BoxLayout.
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 SpringLayout, you will 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 an argument specifying the added
component's position within the panel. For example:
aBorderPanel.add(aComponent, BorderLayout.CENTER);
aBorderPanel.add(anotherComponent, BorderLayout.PAGE_END);
With GridBagLayout you can use either add method, but you must somehow specify grid bag
constraints for each component.
For information about choosing and using the standard layout managers, see Using Layout
Managers.
645
The Panel API
The API in the JPanel class itself is minimal. The methods you are most likely to invoke on a
JPanel object are those it inherits from its superclasses — JComponent, Container, and Component.
The following tables list the API you are most likely to use, with the exception of methods related to
borders and layout hints. For more information about the API that all JComponent objects can use,
see The JComponent Class.
Creating a JPanel
Managing a Container's Components
Setting or Getting the Layout Manager
Creating a JPanel
Constructor Purpose
Creates a panel. The LayoutManager parameter provides a layout manager
JPanel()
for the new panel. By default, a panel uses a FlowLayout to lay out its
JPanel(LayoutManager)
components.
Managing a Container's Components
Method Purpose
void add(Component)
Adds the specified component to the panel. When present, the int
void add(Component, int)
parameter is the index of the component within the container. By default,
void add(Component,
the first component added is at index 0, the second is at index 1, and so
Object)
on. The Object parameter is layout manager dependent and typically
void add(Component,
provides information to the layout manager regarding positioning and
Object, int)
other layout constraints for the added component. The String parameter
void add(String,
is similar to the Object parameter.
Component)
int getComponentCount() Gets the number of components in this panel.
Component
getComponent(int)
Component
getComponentAt(int, int) Gets 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) Removes the specified component(s).
void removeAll()
Setting or Getting the Layout Manager
Method Purpose
void Sets or gets the layout manager for this panel. The layout manager is
setLayout(LayoutManager) responsible for positioning the panel's components within the panel's
LayoutManager getLayout() bounds according to some philosophy.
646
Examples That Use Panels
Many examples contained in this lesson use JPanel objects. The following table lists a few.
Where
Example Notes
Described
Uses five panels, four of which use BoxLayout and one of which
Converter This section uses GridLayout. The panels use borders and, as necessary, size
and alignment hints to affect layout.
ListDemo
How to Use Uses a panel, with its default FlowLayout manager, to center three
Lists components in a row.
ToolBarDemo
How to Use Uses a panel as a content pane. The panel contains three
Tool Bars components, laid out by BorderLayout.
BorderDemo
How to Use Contains many panels that have various kinds of borders. Several
Borders panels use BoxLayout.
How to Use
BoxLayoutDemo Illustrates the use of a panel with Swing's BoxLayout manager.
BoxLayout
Here is a picture of a demo that opens a small window and prompts the user to type in a password.
Click the Launch button to run PasswordDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
The password is "bugaboo". You can find the entire code for this program in PasswordDemo.java.
Here is the code that creates and sets up the password field:
passwordField = new JPasswordField(10);
passwordField.setActionCommand(OK);
passwordField.addActionListener(this);
The argument passed into the JPasswordField constructor indicates the preferred size of the field,
which is at least 10 columns wide in this case. By default a password field displays a dot for each
character typed. If you want to change the echo character, call the setEchoChar method. The code
then adds an action listener to the password field, which checks the value typed in by the user. Here
is the implementation of the action listener's actionPerformed method:
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
647
if (OK.equals(cmd)) { //Process the password.
char[] input = passwordField.getPassword();
if (isPasswordCorrect(input)) {
JOptionPane.showMessageDialog(controllingFrame,
"Success! You typed the right password.");
} else {
JOptionPane.showMessageDialog(controllingFrame,
"Invalid password. Try again.",
"Error Message",
JOptionPane.ERROR_MESSAGE);
}
passwordField.selectAll();
resetFocus();
} else ...//handle the Help button...
}
Security note: Although the JPasswordField class inherits the getText method, you should use the
getPassword method instead. Not only is getText less secure, but in the future it might return the
visible string (for example, "******") instead of the typed string.
To further enhance security, once you are finished with the character array returned by the
getPassword method, you should set each of its elements to zero. The preceding code snippet shows
how to do this.
A program that uses a password field typically validates the password before completing any actions
that require the password. This program calls a private method, isPasswordCorrect, that compares
the value returned by the getPassword method to a value stored in a character array. Here is its
code:
if (input.length != correctPassword.length) {
isCorrect = false;
} else {
isCorrect = Arrays.equals (input, correctPassword);
}
return isCorrect;
}
The following tables list the commonly used JPasswordField constructors and methods. For
information on the API that password fields inherit, see How to Use Text Fields.
648
Commonly Used JPasswordField Constructors and Methods
Constructor or Method Purpose
JPasswordField()
Creates a password field. When present, the int argument
JPasswordField(String)
specifies the desired width in columns. The String
JPasswordField(String, int)
argument contains the field's initial text. The Document
JPasswordField(int)
argument provides a custom model for the field.
JPasswordField(Document, String, int)
char[] getPassword() Returns the password as an array of characters.
void setEchoChar(char) Sets or gets the echo character which is displayed instead
char getEchoChar() of the actual characters typed by the user.
void addActionListener(ActionListener)
void
Adds or removes an action listener.
removeActionListener(ActionListener)
(defined in JTextField)
void selectAll()
Selects all characters in the password field.
(defined in JTextComponent)
PasswordDemo is the Tutorial's only example that uses a JPasswordField object. However, the
Tutorial has many examples that use JTextField objects, whose API is inherited by
JPasswordField. See Examples That Use Text Fields for further information.
A spinner is a compound component with three subcomponents: two small buttons and an editor.
The editor can be any JComponent, but by default it is implemented as a panel that contains a
formatted text field. The spinner's possible and current values are managed by its model.
Here is a picture of an application named SpinnerDemo that has three spinners used to specify dates:
649
The code for the main class can be found in SpinnerDemo.java. The Month spinner displays
the name of the first month in the user's locale. The possible values for this spinner are
specified using an array of strings. The Year spinner displays one value of a range of
integers, initialized to the current year. The Another Date spinner displays one value in a
range of Date objects (initially the current date) in a custom format that shows just a month
and year.
Try this:
1. Click the Launch button to run SpinnerDemo using Java™ Web Start (download JDK
6). Alternatively, to compile and run the example yourself, consult the example index.
2. With the Month spinner, use the arrow buttons or keys to cycle forward and
backward through the possible values.
Note that the lowest value is the first month of the year (for example, January) and
the highest is the last (for example, December). The exact values depend on your
locale. Also note that the values do not cycle — you cannot use the up arrow button
or key to go from December directly to January — because the standard spinner
models do not support cycling.
3. Type in a valid month name for your locale — for example, July.
Note that the spinner automatically completes the month name.
4. Moving on to the Year spinner, try typing a year over 100 years ago — for example,
1800 — and then click on another spinner or press the Tab key to move the focus
out of the spinner.
Because this program restricts the spinner's model to numbers within 100 years of
the current year, 1800 is invalid. When the focus moves out of the spinner, the
displayed text changes back to the last valid value.
5. Moving to the Another Date spinner, use the arrow buttons or keys to change the
date.
Note that by default the first part of the date — in this case, the month number —
changes. You can change which part of the date changes either by clicking the
mouse or using the arrow keys to move to another part of the date.
To create a spinner, first create its model and then pass the model into the JSpinner constructor. For
example:
650
Using Standard Spinner Models and Editors
When you set the spinner's model, the spinner's editor is automatically set. The Swing API provides
an editor class corresponding to each of the three model classes listed above. These classes —
JSpinner.ListEditor, JSpinner.NumberEditor, and JSpinner.DateEditor — are all subclasses of the
JSpinner.DefaultEditor class that feature editable formatted text fields. If you use a model that does
not have an editor associated with it, the editor is by default a JSpinner.DefaultEditor instance
with a non-editable formatted text field.
To change the formatting used in a standard spinner editor, you can create and set the editor yourself.
The JSpinner.NumberEditor and JSpinner.DateEditor classes have constructors that allow you
to create an editor that formats its data in a particular way. For example, the following code sets up
the Another Date spinner so that instead of using the default date format, which is long and includes
the time, it shows just a month and year in a compact way.
651
Note: You can play with date formats by running ComboBoxDemo2 example. Click the Launch
button to run ComboBoxDemo2 using Java™ Web Start (download JDK 6). Alternatively, to
compile and run the example yourself, consult the example index.
For information about format strings, see the Formatting lesson of the Internationalization
trail. For tables of number format characters, see the Getting Started section.
To change the formatting when using a default editor, you can obtain the editor's formatted text field
and invoke methods on it. You can call those methods using the getTextField method defined in
the JSpinner.DefaultEditor class. Note that the Swing-provided editors are not formatted text
fields. Instead, they are the JPanel instances that contain a formatted text field. Here is an example
of getting and invoking methods on the editor's formatted text field:
If the existing spinner models or editors do not meet your needs, you can create your own.
The easiest route to creating a custom spinner model is to create a subclass of an existing
AbstractSpinnerModel subclass that already does most of what you need. An alternative is to
implement your own class by extending AbstractSpinnerModel class, which implements the event
notifications required for all spinner models.
The following subclass of SpinnerListModel implements a spinner model that cycles through an
array of objects. It also lets you specify a second spinner model that will be updated whenever the
cycle begins again. For example, if the array of objects is a list of months, the linked model could be
for a spinner that displays the year. When the month flips over from December to January the year is
incremented. Similarly, when the month flips back from January to December the year is
decremented.
652
firstValue = values[0];
lastValue = values[values.length - 1];
}
The CyclingSpinnerListModel model is used for the Month spinner in the SpinnerDemo2
example, an example that is almost identical to the SpinnerDemo. Click the Launch button to run
SpinnerDemo2 using Java™ Web Start (download JDK 6). Alternatively, to compile and run the
example yourself, consult the example index.
As we mentioned before, if you implement a spinner model that does not descend from
SpinnerListModel, SpinnerNumberModel, or SpinnerDateModel, then the spinner's default editor
is a non-editable instance of JSpinner.DefaultEditor. As you have already seen, you can set a
spinner's editor by invoking the setEditor method on the spinner after the spinner's model property
has been set. An alternative to using setEditor is to create a subclass of the JSpinner class and
override its createEditor method so that it returns a particular kind of editor whenever the spinner
model is of a certain type.
In theory at least, you can use any JComponent instance as an editor. Possibilities include using a
subclass of a standard component such as JLabel, or a component you have implemented from
scratch, or a subclass of JSpinner.DefaultEditor. The only requirements are that the editor must
be updated to reflect changes in the spinner's value, and it must have a reasonable preferred size. The
editor should generally also set its tool tip text to whatever tool tip text has been specified for the
spinner. An example of implementing an editor is provided in the next section.
You can detect that a spinner's value has changed by registering a change listener on
either the spinner or its model. Here is an example of implementing such a change listener.
653
This example is from SpinnerDemo3, which is based on SpinnerDemo and uses a change
listener to change the color of some text to match the value of the Another Date spinner.
Click the Launch button to run SpinnerDemo3 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
654
class GrayEditor extends JLabel
implements ChangeListener {
public GrayEditor(JSpinner spinner) {
setOpaque(true);
...
//Get info from the model.
GrayModel myModel = (GrayModel)(spinner.getModel());
setBackground(myModel.getColor());
spinner.addChangeListener(this);
...
updateToolTipText(spinner);
}
The following tables list some of the commonly used API for using spinners. If you need to deal
directly with the editor's formatted text field, you should also see The FormattedTextField API.
Other methods you might use are listed in the API tables in The JComponent Class.
655
A subclass of AbstractSpinnerModel whose values are defined by
SpinnerListModel
an array or a List.
A subclass of AbstractSpinnerModel that supports sequences of
SpinnerDateModel
Date instances.
A subclass of AbstractSpinnerModel that supports sequences of
SpinnerNumberModel
numbers.
Implements an uneditable component that displays the spinner's
JSpinner.DefaultEditor value. Subclasses of this class are generally more specialized (and
editable).
A subclass of JSpinner.DefaultEditor whose values are defined
JSpinner.ListEditor
by an array or a List.
A subclass of JSpinner.DefaultEditor that supports sequences of
JSpinner.DateEditor
Date instances.
A subclass of JSpinner.DefaultEditor that supports sequences of
JSpinner.NumberEditor
numbers.
Useful JSpinner Constructors and Methods
Constructor or Method Purpose
Creates a new JSpinner. The no-argument constructor
creates a spinner with an integer SpinnerNumberModel with
JSpinner()
an initial value of 0 and no minimum or maximum limits.
JSpinner(SpinnerModel)
The optional parameter on the second constructor allows
you to specify your own SpinnerModel.
void
Sets or gets the currently displayed element of the
setValue(java.lang.Object)
sequence.
Object getValue()
Object getNextValue() Gets the object in the sequence that comes before or after
Object getPreviousValue() the object returned by the getValue method.
SpinnerModel getModel()
Gets or sets the spinner's model.
void setModel(SpinnerModel)
JComponent getEditor() Gets or sets the spinner's editor, which is often an object of
void setEditor(JComponent) type JSpinner.DefaultEditor.
Called by the JSpinner constructors to create the spinner's
protected JComponent
editor. Override this method to associate an editor with a
createEditor(SpinnerModel)
particular type of model.
Useful Editor Constructors and Methods
Constructor or Method Purpose
Creates a JSpinner.NumberEditor instance that
displays and allows editing of the number value of the
JSpinner.NumberEditor(JSpinner, specified spinner. The string argument specifies the
String) format to use to display the number. See the API
documentation for DecimalFormat for information about
decimal format strings.
Creates a JSpinner.DateEditor instance that displays
JSpinner.DateEditor(JSpinner,
and allows editing of the Date value of the specified
String)
spinner. The string argument specifies the format to use
656
to display the date. See the API documentation for
SimpleDateFormat for information about date format
strings.
JFormattedTextField
getTextField() Gets the formatted text field that provides the main GUI
(defined in for this editor.
JSpinner.DefaultEditor)
SpinnerListModel Methods
Method Purpose
void setList(List)
Sets or gets the List that defines the sequence for this model.
List getList()
SpinnerDateModel Methods
Method Purpose
void setValue(Object)
Date getDate() Sets or gets the current Date for this sequence.
Object getValue()
void
Sets or gets the first Date in this sequence. Use null to specify that
setStart(Comparable)
the spinner has no lower limit.
Comparable getStart()
void
Sets or gets the last Date in this sequence. Use null to specify that
setEnd(Comparable)
the spinner has no upper limit.
Comparable getEnd()
Sets or gets the size of the date value increment used by the
getNextValue and getPreviousValue methods. This property is not
used when the user explicitly increases or decreases the value;
void instead, the selected part of the formatted text field is incremented
setCalendarField(int) or decremented. The specified parameter must be one of the
int getCalendarField() following constants, defined in Calendar: ERA, YEAR, MONTH,
WEEK_OF_YEAR, 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)
Sets or gets the current value for this sequence.
Number getNumber()
void
Sets or gets the upper bound for numbers in this sequence. If
setMaximum(Comparable)
the maximum is null, there is no upper bound.
Comparable getMaximum()
void
Sets or gets the lower bound for numbers in this sequence. If
setMinimum(Comparable)
the minimum is null, there is no lower bound.
Comparable getMinimum()
void setStepSize(Number) Sets or gets the increment used by getNextValue and
Number getStepSize() getPreviousValue methods.
657
Examples That Use Spinners
This table lists examples that use spinners and points to where those examples are
described.
Where
Example Notes
Described
Uses all three standard spinner model classes. Contains the
SpinnerDemo This section code to use a custom spinner model, but the code is turned off
by default.
SpinnerDemo2 This section
A SpinnerDemo subclass that uses the custom spinner model
for its Months spinner.
SpinnerDemo3 This section
Based on SpinnerDemo, this application shows how to listen
for changes in a spinner's value.
SpinnerDemo4 This section
Implements a custom model and a custom editor for a spinner
that displays shades of gray.
If you want to create a split pane with an arbitrary number of components, you should check out
Hans Muller's article, MultiSplitPane: Splitting Without Nesting.
Instead of adding the components of interest directly to a split pane, you often put each component
into a scroll pane. You then put the scroll panes into the split pane. This allows the user to view any
part of a component of interest, without requiring the component to take up a lot of screen space or
adapt to displaying itself in varying amounts of screen space.
Here's a picture of an application that uses a split pane to display a list and an image side by side:
Try this:
658
1. Click the Launch button to run the SplitPaneDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
2. Drag the dimpled line that divides the list and the image to the left or right.
Try to drag the divider all the way to the window's edge.
3. Click the tiny arrows on the divider to hide/expand the left or right component.
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.
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 and feel, they are turned
off by default. (Note that not all look and feels support this.) The example turned them on using the
setOneTouchExpandable method.
The range of a split pane's divider is determined in part by the minimum sizes of the components
within the split pane. See Positioning the Divider and Restricting its Range for details.
A program can set a split pane's two components dynamically with these four methods:
659
setLeftComponent
setRightComponent
setTopComponent
setBottomComponent
You can use any of these methods at any time regardless of the split pane's current split direction.
Calls to setLeftComponent and setTopComponent are equivalent and set the specified component
in the top or left position, depending on the split pane's current split orientation. Similarly, calls to
setRightComponent and setBottomComponent are equivalent. These methods replace whatever
component is already in that position with the new one.
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.
To make your split pane work well, you often need to set the minimum sizes of components in the
split pane, as well as the preferred size of either the split pane or its contained components. Choosing
which sizes you should set is an art that requires understanding how a split pane's preferred size and
divider location are determined. Before we get into details, let's take another look at SplitPaneDemo.
Or, if you're in a hurry, you can skip to the list of rules.
Try this:
1. Click the Launch button to run the SplitPaneDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.
Because the size of the demo's frame is set using the pack method, the split pane is at
its preferred size, which SplitPaneDemo happens to set explicitly. The divider is
automatically placed so that the left component is at its preferred width and all
remaining space goes to the right component.
660
the minimum size of the components contained by the split pane. SplitPaneDemo sets
the minimum size of these contained components explicitly.
5. Make the window wider, and then drag the divider as far as it will go to the right.
The divider goes only as far as the right component's minimum size allows. If you
drag the divider to the left, you'll see that it also respects the left component's
minimum size.
Now that you've seen the default behavior of split panes, we can tell you what's happening behind
the scenes and how you can affect it. In this discussion, when we refer to a component's preferred or
minimum size, we often mean the preferred or minimum width of the component if the split pane is
horizontal, or its preferred or minimum height if the split pane is vertical.
By default, a split pane's preferred size and divider location are initialized so that the two
components in the split pane are at their preferred sizes. If the split pane isn't displayed at this
preferred size and the program hasn't set the divider's location explicitly, then the initial position of
the divider (and thus the sizes of the two components) depends on a split pane property called the
resize weight. If the split pane is initially at its preferred size or bigger, then the contained
components start out at their preferred sizes, before adjusting for the resize weight. If the split pane is
initially too small to display both components at their preferred sizes, then they start out at their
minimum sizes, before adjusting for the resize weight.
A split pane's resize weight has a value between 0.0 and 1.0 and determines how space is distributed
between the two contained components when the split pane's size is set — whether programmatically
or by the user resizing the split pane (enlarging its containing window, for example). The resize
weight of a split pane is 0.0 by default, indicating that the left or top component's size is fixed, and
the right or bottom component adjusts its size to fit the remaining space. Setting the resize weight to
0.5 splits any extra or missing space evenly between the two components. Setting the resize weight
to 1.0 makes the right or bottom component's size remain fixed. The resize weight has no effect,
however, when the user drags the divider.
The user can drag the divider to any position as long as neither contained component goes below its
minimum size. If the divider has one-touch buttons, the user can use them to make the divider move
completely to one side or the other — no matter what the minimum sizes of the components are.
Now that you know the factors that affect a split pane's size and divider location, here are some rules
for making them work well:
To ensure that the divider can be dragged when the split pane is at its preferred size,
make sure the minimum size of one or both contained components is smaller than the
contained component's preferred size. You can set the minimum size of a component
either by invoking setMinimumSize on it or by overriding its getMinimumSize
method. For example, if you want the user to be able to drag the divider all the way to
both sides:
Dimension minimumSize = new Dimension(0, 0);
leftComponent.setMinimumSize(minimumSize);
rightComponent.setMinimumSize(minimumSize);
To guarantee that both contained components appear, make sure that either the split
pane is initially at or above its preferred size, or the minimum sizes of the contained
components are greater than zero.
661
This should usually happen if the splitpane is given its preferred size, which depends
upon the layout manager containing the split pane. Another option is to explicitly set a
preferred size on the split pane that is larger than the size of the contained
components.
If you want the bottom or right component to stay the same size and the top or left
component to be flexible when the split pane gets bigger, set the resize weight to 1.0.
You can do this by invoking setResizeWeight:
splitPane.setResizeWeight(1.0);
If you want both halves of the split pane to share in the split pane's extra or removed
space, set the resize weight to 0.5:
splitPane.setResizeWeight(0.5);
Make sure each component contained by a split pane has a reasonable preferred size.
If the component is a panel that uses a layout manager, you can generally just use the
value it returns. If the component is a scroll pane, you have a few choices. You can
invoke the setPreferredSize method on the scroll pane, invoke the appropriate
method on the component in the scroll pane (such as the setVisibleRowCount
method for JList or JTree).
Make sure each component contained by a split pane can display itself reasonably in
varying amounts of space. For example, panels that contain multiple components
should use layout managers that use extra space in a reasonable way.
If you want to set the size of contained components to something other than their
preferred sizes, use the setDividerLocation method. For example, to make the left
component 150 pixels wide:
splitPane.setDividerLocation(150 + splitPane.getInsets().left);
Although the split pane does its best to honor the initial divider location (150 in this
case), once the user drags the divider it may no longer be possible to drag to the
programmatically specified size.
splitPane.setDividerLocation(splitPane.getSize().width
- splitPane.getInsets().right
- splitPane.getDividerSize()
- 150);
If the split pane is already visible, you can set the divider location as a percentage of
the split pane. For example, to make 25% of the space go to left/top:
splitPane.setDividerLocation(0.25);
Note that this is implemented in terms of the current size and is therefore really ony
useful if the split pane is visible.
To lay out the split pane as if it just came up, likely repositioning the divider in the
process, invoke resetToPreferredSizes() on the split pane.
662
Note: Just changing the contained components' preferred sizes — even if you
invoke revalidate afterwards — is not enough to cause the split pane to lay
itself out again. You must invoke resetToPreferredSizes as well.
The following snapshot shows an example named SplitPaneDividerDemo that demonstrates split
pane component sizes and divider placement.
Like SplitPaneDemo, SplitPaneDividerDemo features a horizontal split pane with one-touch buttons.
SplitPaneDividerDemo has the following additional features:
Try this:
1. Click the Launch button to run the SplitPaneDividerDemo using Java™ Web
Start (download JDK 6). Alternatively, to compile and run the example
yourself, consult the example index.
Because the size of the demo's frame is set using the pack method, the split
pane is at its preferred size, which by default is just big enough for the
SizeDisplayers to be at their preferred sizes. The preferred size of each
SizeDisplayer is indicated by a red rectangle. The divider is automatically
placed so that both components are at their preferred widths.
663
2. Make the window wider.
Because the split pane's resize weight is 0.5, the extra space is divided evenly
between the left and right components. The divider moves accordingly.
3. Make the window as narrow as possible.
Assuming the window uses the Java look and feel-provided decorations, it will
not let you size the window smaller than the split pane's minimum size, which
is determined by the minimum size of the SizeDisplayers it contains. The
minimum size of each SizeDisplayer is indicated by a bright blue rectangle.
4. Make the window a bit wider, and then drag the divider as far as it will go to
the right.
The divider goes only as far as the right component's minimum size allows.
5. After making sure the split pane is smaller than its preferred size, click the
Reset button.
Note that the two SizeDisplayers are displayed at the different sizes, even
though when the application came up they had equal sizes. The reason is that
although their preferred sizes are equal, their minimum sizes are not. Because
the split pane cannot display them at their preferred sizes or larger, it lays
them out using their minimum sizes. The leftover space is divided equally
between the components, since the split pane's resize weight is 0.5.
6. Widen the split pane so that it is large enough for both SizeDisplayers to be
shown at their preferred sizes, and then click the Reset button.
The divider is placed in the middle again, so that both components are the
same size.
public SplitPaneDividerDemo() {
super(new BorderLayout());
icon = createImageIcon("images/Dog.gif");
SizeDisplayer sd2 = new SizeDisplayer("right", icon);
sd2.setMinimumSize(new Dimension(60,60));
sd2.setFont(font);
add(splitPane, BorderLayout.CENTER);
add(createControlPanel(), BorderLayout.PAGE_END);
}
...
}
664
The code is fairly self explanatory, except perhaps for the call to setContinuousLayout. Setting the
continuousLayout property to true makes the split pane's contents be painted continuously while the
user is moving the divider. Continuous layout is not on, by default, because it can have a negative
performance impact. However, it makes sense to use it in this demo, when having the split pane's
components as up-to-date as possible can improve the user experience.
Here's a picture of a program that achieves a three-way split by nesting one split pane inside of
another:
If the top portion of the split pane looks familiar to you, it is 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.
Click the Launch button to run the SplitPaneDemo2 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
Here's the interesting part of the code, which you can find in SplitPaneDemo2.java:
...
//Create a regular old label
label = new JLabel("Click on an image name in the list.",
JLabel.CENTER);
665
The following tables list the commonly used JSplitPane constructors and methods. Other methods
you are most likely to invoke on a JSplitPane object are those such as setPreferredSize that its
superclasses provide. See The JComponent API for tables of commonly used inherited methods.
666
Add the component to the split pane. You can add only two
components to a split pane. The first component added is the
void add(Component) 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 divider
setDividerLocation(double)
location, you can specify the new location as a percentage
void setDividerLocation(int)
(double) or a pixel location (int).
int getDividerLocation()
Move the divider such that both components are at their preferred
void resetToPreferredSizes() 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. These
getMaximumDividerLocation()
are set implicitly by setting the minimum sizes of the split pane's
int
two components.
getMinimumDividerLocation()
Set or get the resize weight for the split pane, a value between 0.0
void setResizeWeight(float) (the default) and 1.0. See Positioning the Divider and Restricting
float getResizeWeight() Its Range for an explanation of and examples of using the resize
weight.
This table shows some examples that use JSplitPane and where those examples are described.
Where
Example Notes
Described
This page and
SplitPaneDemo How to Use Shows a split pane with a horizontal split.
Lists
SplitPaneDividerDemo This page
Demonstrates how component size information and resize
weight are used to position the divider.
Puts a split pane within a split pane to create a three-way
SplitPaneDemo2 This page
split.
Uses a split pane with a vertical split to separate a tree (in a
How to Use
TreeDemo scroll pane) from an editor pane (in a scroll pane). Does not
Trees
use the one-touch expandable feature.
Text
Uses a split pane with a vertical split to separate a text pane
TextComponentDemo Component
and a text area, both in scroll panes.
Features
Uses a split pane with a vertical split and resize weight of
Text
0.5 to separate a text pane and an editor pane, both in scroll
TextSamplerDemo Component
panes. The split pane is in the right half of a container that
Features
has a fairly complicated layout. Layout managers such as
667
GridLayout and BorderLayout are used, along with the
split pane's resize weight, to ensure that the components in
scroll panes share all extra space.
Uses a split pane with a vertical split to separate an upper
How to Write a pane, containing a list and a table (both in scroll panes),
ListSelectionDemo List Selection from a lower pane that contains a combo box above a scroll
Listener pane. The lower pane uses a border layout to keep the
combo box small and the scroll pane greedy for space.
The following picture introduces an application called TabbedPaneDemo that has a tabbed pane with
four tabs.
Try this:
1. Click the Launch button to run TabbedPaneDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
668
Click the arrow to view one of the hidden tabs.
Note that clicking the arrow only reveals hidden tabs. It does not select a new tab.
As the TabbedPaneDemo example shows, a tab can have a tool tip and a mnemonic, and it can display
both text and an image.
Tab Placement
The default tab placement is set to the TOP location, as shown above. You can change the
tab placement to LEFT, RIGHT, TOP or BOTTOM by using the setTabPlacement method.
1. Using a mouse. To switch to a specific tab, the user clicks it with the mouse.
2. Using keyboard arrows. When the JTabbedPane object has the focus, the keyboard
arrows can be used to switch from tab to tab.
3. Using key mnemonics. The setMnemonicAt method allows the user to switch to a
specific tab using the keyboard. For example, setMnemonicAt(3, KeyEvent.VK_4)
669
makes '4' the mnemonic for the fourth tab (which is at index 3, since the indices start
with 0); pressing Alt-4 makes the fourth tab's component appear. Often, a mnemonic
uses a character in the tab's title that is then automatically underlined.
In the TabbedPaneDemo example, the fourth panel has a preferred width and height that are larger
than those of the other panels. Thus, the preferred size of the tabbed pane is just big enough to
display the fourth panel at its preferred size. Every panel gets exactly the same amount of space —
410 pixels wide and 50 high, assuming the tabbed pane is at its preferred size. If you do not
understand how preferred size is used, please refer to How Layout Management Works.
The TabComponentsDemo example introduces a tabbed pane whose tabs contain real
components. The use of custom components brings new features such as buttons, combo
boxes, labels and other components to tabs, and allows more complex user interaction.
Try this:
1. Click the Launch button to run TabComponentsDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
670
4. Put the cursor over one of the widgets with a little cross.
The cross turns magenta and gets enclosed in a square. A tool tip associated with
the close button appears.
Click the cross with the left mouse button to close the tab.
5. Restore the tabs that have been removed by choosing the Reset JTabbedPane item
from the Options menu.
6. Note that tabs with custom components are displayed on top of original tabbed pane
tabs.
To view the tabs underneath, open the Options menu and clear the Use
TabComponents checkbox.
7. Display the tabs with components by selecting the Use TabComponents checkbox
again.
8. Close all tabs. Now the tabbed pane is empty.
To Remove Tabs
The following code from ButtonTabComponent.java removes a tab from the tabbed pane.
Note that event-handling code is necessary. Since each tab contains a real JButton object,
you must attach an ActionListener to the close button. As the user clicks the button, the
actionPerformed method determines the index of the tab it belongs to and removes the
corresponding tab.
public void actionPerformed(ActionEvent e) {
int i = pane.indexOfTabComponent(ButtonTabComponent.this);
if (i != -1) {
pane.remove(i);
}
}
The following tables list the commonly used JTabbedPane constructors and methods. The
API for using tabbed panes falls into the following categories:
671
Method or Constructor Purpose
Creates 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
JTabbedPane() (defined in the SwingConstants interface, which JTabbedPane
JTabbedPane(int)
implements): TOP, BOTTOM, LEFT, RIGHT. The second optional
JTabbedPane(int, int)
argument specifies the tab layout policy. You can specify one of
these policies (defined in JTabbedPane): WRAP_TAB_LAYOUT or
SCROLL_TAB_LAYOUT.
addTab(String, Icon,
Adds 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,
fourth argument, if present, specifies the tool tip text for the tab.
Component)
Sets or gets the policy that the tabbed pane uses in laying out
void
tabs when all tabs do not fit within a single run. Possible values
setTabLayoutPolicy(int)
are WRAP_TAB_LAYOUT and SCROLL_TAB_LAYOUT. The default policy
int getTabLayoutPolicy()
is WRAP_TAB_LAYOUT.
Sets or gets the location where the tabs appear relative to the
void setTabPlacement(int)
content. Possible values (defined in SwingConstants, which is
int getTabPlacement()
implemented by JTabbedPane) are TOP, BOTTOM, LEFT, and RIGHT.
Inserting, Removing, Finding, and Selecting Tabs
Method Purpose
Inserts a tab at the specified index, where the first
insertTab(String, Icon, Component,
tab is at index 0. The arguments are the same as
String, int)
for addTab.
remove(Component) Removes the tab corresponding to the specified
removeTabAt(int) component or index.
removeAll() Removes all tabs.
int indexOfComponent(Component)
Returns the index of the tab that has the specified
int indexOfTab(String)
component, title, or icon.
int indexOfTab(Icon)
void setSelectedIndex(int) Selects the tab that has the specified component
void or index. Selecting a tab has the effect of
setSelectedComponent(Component) displaying its associated component.
int getSelectedIndex() Returns the index or component for the selected
Component getSelectedComponent() tab.
Changing Tab Appearance
Method Purpose
void setComponentAt(int,
Sets or gets which component is associated with the
Component)
tab at the specified index. The first tab is at index 0.
Component getComponentAt(int)
void setTitleAt(int, String)
Sets or gets the title of the tab at the specified index.
String getTitleAt(int)
void setIconAt(int, Icon) Sets or gets the icon displayed by the tab at the
672
Icon getIconAt(int) specified index.
void setDisabledIconAt(int, Icon)
Icon getDisabledIconAt(int)
Sets or gets the background or foreground color used
by the tab at the specified index. By default, a tab
void setBackgroundAt(int, Color)
uses the tabbed pane's background and foreground
Color getBackgroundAt(int)
colors. For example, if the tabbed pane's foreground
void setForegroundAt(int, Color)
is black, then each tab's title is black except for any
Color getForegroundAt(int)
tabs for which you specify another color using
setForegroundAt.
void setEnabledAt(int, boolean) Sets or gets the enabled state of the tab at the
boolean isEnabledAt(int) specified index.
void setMnemonicAt(int, int) Sets or gets the keyboard mnemonic for accessing the
int getMnemonicAt(int) specified tab.
void Sets or gets which character should be decorated to
setDisplayedMnemonicIndexAt(int, represent the mnemonic. This is useful when the
int) mnemonic character appears multiple times in the
int tab's title and you don't want the first occurrence
getDisplayedMnemonicIndexAt(int) underlined.
void setToolTipTextAt(int, String) Sets or gets the text displayed on tool tips for the
String getToolTipTextAt(int) specified tab.
Setting Up Custom Components on Tabs
Method Purpose
Sets the component that is responsible for rendering
the title or icon (or both) for the tab specified by the
void setTabComponentAt(int,
first argument. When a null value is specified,
Component)
JTabbedPane renders the title or icon. The same
component cannot be used for several tabs.
Gets the tab component for the tab at the index
specified by the argument. If there is no tab
Component getTabComponentAt(int)
component for the specified tab, a null value is
returned.
Checks if the specified component belongs to one of
int
the tabs. Return the index of the corresponding tab
indexOfTabComponent(Component)
or -1 if there is no such a tab.
This table lists examples that use JTabbedPane and points to where those examples are
described.
Where
Example Notes
Described
Demonstrates a few tabbed pane features, such as tool
TabbedPaneDemo This page
tips, icons, scrollable layout, and mnemonics.
TabComponentsDemo This page
Demonstrates custom components on tabs. Uses a
tabbed pane with close buttons.
BoxAlignmentDemo How to Use Uses a JTabbedPane as the only child of a frame's content
673
BoxLayout pane.
BorderDemo
How to Use Uses its tabbed pane in a manner similar to
Borders BoxAlignmentDemo.
DialogDemo
How to Use Has a tabbed pane in the center of a frame's content
Dialogs pane, with a label below it.
Uses a tabbed pane at the bottom of the window to
display the contents of one or more files. The tabbed
Introduction
DragFileDemo pane isn't used until the user selects a file. The tabbed
to DnD
pane's state is controlled by an object of the custom type
TabbedPaneController.
With the JTable class you can display tables of data, optionally allowing the user to edit the data.
JTable does not contain or cache data; it is simply a view of your data. Here is a picture of a typical
table displayed within a scroll pane:
The rest of this section shows you how to accomplish some common table-related tasks. Here are the
topics this section covers:
674
Using a Combo Box as an Editor
Using Other Editors
Using an Editor to Validate User-Entered Text
Printing
Examples that Use Tables
Try this:
1. Click the Launch button to run SimpleTableDemo using Java™ Web Start
(download JDK 6). Or, to compile and run the example yourself, consult the
example index.
Object[][] data = {
{"Mary", "Campione",
"Snowboarding", new Integer(5), new Boolean(false)},
{"Alison", "Huml",
"Rowing", new Integer(3), new Boolean(true)},
{"Kathy", "Walrath",
"Knitting", new Integer(2), new Boolean(false)},
{"Sharon", "Zakhour",
"Speed reading", new Integer(20), new Boolean(true)},
{"Philip", "Milne",
675
"Pool", new Integer(10), new Boolean(false)}
};
There are two JTable constructors that directly accept data (SimpleTableDemo uses the first):
The advantage of these constructors is that they are easy to use. However, these constructors also
have disadvantages:
If you want to get around these restrictions, you need to implement your own table model, as
described in Creating a Table Model.
Here is typical code for creating a scroll pane that serves as a container for a table:
The JScrollPane constructor is invoked with an argument that refers to the table
object. This creates a scroll pane as a container for the table; the table is automatically
added to the container.
JTable.setFillsViewportHeight is invoked to set the fillsViewportHeight
property. When this property is true the table uses the entire height of the container,
even if the table doesn't have enough rows to use the whole vertical space. This makes
it easier to use the table as a drag-and-drop target.
The scroll pane automatically places the table header at the top of the viewport. The column names
remain visible at the top of the viewing area when the table data is scrolled.
If you are 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());
676
container.add(table.getTableHeader(), BorderLayout.PAGE_START);
container.add(table, BorderLayout.CENTER);
By default, all columns in a table start out with equal width, and the columns automatically fill the
entire width of the table. When the table becomes wider or narrower (which might happen when the
user resizes the window containing the table), all the column widths change appropriately.
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 to or removed from the column to the
left of the drag point.
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:
As the preceding code shows, each column in a table is represented by a TableColumn object.
TableColumn supplies getter and setter methods for the minimum, preferred, and maximum widths
of a column, as well as a method for getting the current width. For an example of setting cell widths
based on an approximation of the space needed to draw the cells' contents, see the initColumnSizes
method in TableRenderDemo.java.
When the user explicitly resizes columns, the columns' preferred widths are set such that the user-
specified sizes become the columns' new current widths. However, when table itself is resized —
typically because the window has resized —; the columns' preferred widths do not change. Instead,
the existing preferred widths are used to calculate new column widths to fill the available space.
User Selections
In its default configuration, a table supports a selection that consists of one or more rows. The user
can select a contiguous range of rows or an arbitrary set of rows. The last cell that the user indicated
gets a special indication; in the Metal look and feel, the cell is outlined. This cell is known as the
lead selection; it is sometimes called "the cell with the focus" or "the current cell".
The user uses the mouse and/or keyboard to make selections, as described in the following table:
677
Operation Mouse Action Keyboard Action
Shift-Click or
Extend contiguous
Drag over Shift-Up Arrow or Shift-Down Arrow.
selection.
rows.
Add row to Move lead selection with Control-Up Arrow or Control-
selection/toggle row Control-Click Down Arrow, then use Space Bar to add to selection or
selection. Control-Space Bar to toggle row selection.
To see how selections work, click the Launch button to run TableSelectionDemo using Java™ Web
Start (download JDK 6). Or, to compile and run the example yourself, consult the example index.
This example program presents the familiar table, and allows the user to manipulate certain JTable
options. There is also a text pane that logs selection events.
In the screenshot below, a user has run the program, clicked in the first row, then control-clicked in
the third row. Notice the outline around the last cell clicked; this is how the Metal look and feel
highlights the lead selection.
Under "Selection Mode" there are a set of radio buttons. Click the one labelled "Single Selection".
Now you can only select one row at a time. If you click on the "Single Interval Selection" radio
button, you can select a set of rows that must be contiguous.
All of the radio buttons under "Selection Mode" invoke JTable.setSelectionMode. This method
takes a single argument, which must be one of the following constants defined in
javax.swing.ListSelectionModel: MULTIPLE_INTERVAL_SELECTION,
SINGLE_INTERVAL_SELECTION, and SINGLE_SELECTION.
678
Returning to TableSelectionDemo, notice the three option checkboxes under "Selection Options."
Each of checkbox controls the state of a boolean bound variable defined by JTable:
NOTE: JTable uses a very simple concept of selection, managed as an intersection of rows and
columns. It was not designed to handle fully independent cell selections.
If you clear all three check boxes (setting all three bound properties to false), there is no selection;
only the lead selection is shown.
You may notice that the "Cell Selection" checkbox is disabled in multiple interval selection mode.
This is because cell selection is not supported in this mode in the demo. You can specify selection by
cell in multiple interval selection mode, but the result is a table that does not produce useful
selections.
You may also notice that changing any of the three selection options can affect the others. This is
because allowing both row selection and column selection is exactly the same as enabling cell
selection. JTable automatically updates the three bound variables as necessary to keep them
consistent.
NOTE: Setting cellSelectionEnabled to a value has the side effect of also setting both
rowSelectionEnabled and columnSelectionEnabled to that value. Setting both
rowSelectionEnabled and columnSelectionEnabled to a value has the side effect of also setting
cellSelectionEnabled to that value. Setting rowSelectionEnabled and
columnSelectionEnabled to different values has the side effect of also setting
cellSelectionEnabled to false.
To retrieve the current selection, use JTable.getSelectedRows which returns an array of row
indexes, and JTable.getSelectedColumns which returns an array of column indexes. To retrieve
the coordinates of the lead selection, refer to the selection models for the table itself and for the
table's column model. The following code formats a string containing the row and column of the lead
selection:
679
User selections generate a number of events. For information on these, refer to How to Write a List
Selection Listener in the Writing Event Listeners lesson.
NOTE: Selection data actually describes selected cells in the "view" (table data as it appears after
any sorting or filtering) rather than in the table model. This distinction does not matter unless your
viewed data has been rearranged by sorting, filtering, or user manipulation of columns. In that case,
you must convert selection coordinates using the conversion methods described in Sorting and
Filtering.
Every table object uses a table model object to manage the actual table data. A table model object
must implement the TableModel interface. If the programmer does not provide a table model object,
JTable automatically creates an instance of DefaultTableModel. This relationship is illustrated
below.
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);
}
}
680
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 hash map, or it might get the data from an
outside source such as a database. It might even generate the data at execution time.
This table is different from the SimpleTableDemo table in the following ways:
TableDemo's custom table model, even though it is simple, can easily determine the
data's type, helping the JTable display the data in the best format.
SimpleTableDemo's automatically created table model, on the other hand, does not
know that the # of Years column contains numbers (which should generally be right
aligned and have a particular format). It also does not know that the Vegetarian
column contains boolean values, which can be represented by check boxes.
The custom table model implemented in TableDemo does not let you edit the name
columns; it does, however, let you edit the other columns. In SimpleTableDemo, all
cells are editable.
See below the code taken from TableDemo.java that is different from the SimpleTableDemo.java.
Bold font indicates the code that makes this table's model different from the table model defined
automatically for SimpleTableDemo.
public TableDemo() {
...
JTable table = new JTable(new MyTableModel());
...
}
/*
* 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,
681
//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) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
...
}
A table model can have a set of listeners that are notified whenever the table data changes. Listeners
are instances of TableModelListener. In the following example code, SimpleTableDemo is
extended include such a listener. New code is in bold.
import javax.swing.event.*;
import javax.swing.table.TableModel;
In order to fire data change events the table model must know how to construct a TableModelEvent
object. This can be a complex procedure, but is already implemented in DefaultTableModel. You
can either allow JTable to use its default instance of DefaultTableModel, or create your own
custom subclass of DefaultTableModel.
If DefaultTableModel is not a suitable base class for your custom table model class, consider
subclassing AbstractTableModel. This class implements a simple framework for constructing
TableModelEvent objects. Your custom class simply needs to invoke one the following
AbstractTableModel methods each time table data is changed by an external source.
682
Method Change
fireTableCellUpdated Update of specified cell.
fireTableRowsUpdated Update of specified rows
fireTableDataChanged Update of entire table (data only).
fireTableRowsInserted New rows inserted.
fireTableRowsDeleted Existing rows Deleted
fireTableStructureChanged Invalidate entire table, both data and structure.
Before you go on to the next few tasks, you need to understand how tables draw their cells. You
might expect each cell in a table to be a component. However, for performance reasons, Swing tables
are implemented differently.
Instead, a single cell renderer is generally used to draw all of the cells 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 you
specified a renderer for that particular column. If you did not, then the 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:
Remember that if you let a table create its own model, it uses Object as the type of every column.
To specify more precise column types, the table model must define the getColumnClass method
appropriately, as demonstrated by TableDemo.java.
683
Keep in mind that although renderers determine how each cell or column header looks and can
specify its tool tip text, a renderer does not handle events. If you need to pick up the events that take
place inside a table, the technique you use varies by the sort of event you are interested in:
The next few sections tell you how to customize display and editing by specifying renderers and
editors. You can specify cell renderers and editors either by column or by data type.
This section tells you how to create and specify a cell renderer. You can set a type-specific cell
renderer using the JTable method setDefaultRenderer. To specify that cells in a particular column
should use a renderer, you use the TableColumn method setCellRenderer. You can even specify a
cell-specific renderer by creating a JTable subclass.
In the snapshot of TableDialogEditDemo, the renderer used for Favorite Color cells is a subclass of
JLabel called ColorRenderer. Here are excerpts from ColorRenderer.java that show how it is
implemented.
684
...
public ColorRenderer(boolean isBordered) {
this.isBordered = isBordered;
setOpaque(true); //MUST do this for background to show up.
}
Here is the code from TableDialogEditDemo.java that registers a ColorRenderer instance as the
default renderer for all Color data:
To specify a cell-specific renderer, 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:
By default, the tool tip text displayed for a table cell is determined by the cell's renderer. However,
sometimes it can be simpler to specify tool tip text by overriding JTable's implementation of the
getToolTipText(MouseEvent) method. This section shows you how to use both techniques.
685
To add a tool tip to a cell using its renderer, you first need to get or create the cell renderer. Then,
after making sure the rendering component is a JComponent, invoke the setToolTipText method on
it.
An example of setting tool tips for cells is in TableRenderDemo. Click the Launch button to run it
using Java™ Web Start (download JDK 6). Or, to compile and run the example yourself, consult the
example index.
The source code is in TableRenderDemo.java. It adds tool tips to the cells of the Sport column with
the following code:
Although the tool tip text in the previous example is static, you can also implement tool tips whose
text changes depending on the state of the cell or program. Here are a couple ways to do so:
An example of adding code to a cell renderer is in TableDialogEditDemo. Click the Launch button
to run it using Java™ Web Start (download JDK 6). Or, to compile and run the example yourself,
consult the example index.
686
You can specify tool tip text by overriding JTable's getToolTipText(MouseEvent) method. The
program TableToolTipsDemo shows how. Click the Launch button to run it using Java™ Web Start
(download JDK 6). Or, to compile and run the example yourself, consult the example index.
The cells with tool tips are in the Sport and Vegetarian columns. Here is a picture of its tool tip:
Here is the code from TableToolTipsDemo.java that implements tool tips for cells in the Sport and
Vegetarian columns:
687
tip = super.getToolTipText(e);
}
return tip;
}
...
}
The code is fairly straightforward, except perhaps for the call to convertColumnIndexToModel. That
call is necessary because if the user moves the columns around, the view's index for the column will
not match the model's index for the column. For example, the user might drag the Vegetarian
column (which the model considers to be at index 4) so it is displayed as the first column — at view
index 0. Since prepareRenderer provides the view index, you need to translate the view index to a
model index so you can be sure the intended column has been selected.
You can add a tool tip to a column header by setting the tool tip text for the table's JTableHeader.
Often, different column headers require different tool tip text. You can change the text by overriding
the table header's getToolTipText method. Alternately, you can invoke
TableColumn.setHeaderRenderer to provide a custom renderer for the header.
An example of using the same tool tip text for all column headers is in TableToolTipsDemo.java
has an example of implementing column header tool tips that vary by column. If you run
TableToolTipsDemo (click the Launch button) using Java™ Web Start (download JDK 6). Or, to
compile and run the example yourself, consult the example index.
You will see the tool tips when you mouse over any column header except for the first two. No tool
tips were suppled for the name columns since they seemed self-explanatory. Here is a picture of one
of the column header tool tips:
The following code implements the tool tips. Basically, it creates a subclass of JTableHeader that
overrides the getToolTipText(MouseEvent) method so that it returns the text for the current
column. To associate the revised table header with the table, the JTable method
createDefaultTableHeader is overridden so that it returns an instance of the JTableHeader
subclass.
688
JTable table = new JTable(new MyTableModel()) {
...
Table sorting and filtering is managed by a sorter object. The easiest way to provide a sorter object is
to set autoCreateRowSorter bound property to true:
To have more control over sorting, you can construct an instance of TableRowSorter and specify
that it is the sorter object for your table.
TableRowSorter<TableModel> sorter
= new TableRowSorter<TableModel>(table.getModel());
table.setRowSorter(sorter);
TableRowSorter uses java.util.Comparator objects to sort its rows. A class that implements this
interface must provide a method called compare that defines how any two objects are compared for
the purpose of sorting. For example, the following code creates a Comparator that sorts a set of
strings by the last word in each string:
689
};
To determine which Comparator to use for a column, TableRowSorter attempts to apply each of the
following rules in turn. Rules are followed in the order listed below; the first rule that provides the
sorter with a Comparator is used, and the remainining rules ignored.
For more sophisticated kinds of sorting, subclass TableRowSorter or its parent class
javax.swing.DefaultRowSorter.
To specify the sort order and sort precedence for columns, invoke setSortKeys. Here is an example
that sorts the table used in the examples by the first two columns. The precedence of the columns in
the sort is indicated by the order of the sort keys in the sort key list. In this case, the second column
has the first sort key, so they rows are sorted by first name, then last name.
In addition to reordering the results, a table sorter can also specify which rows will be displayed.
This is known as filtering. TableRowSorter implements filtering using javax.swing.RowFilter
objects. RowFilter implements several factory methods that create common kinds of filters. For
example, regexFilter returns a RowFilter that filters based on a regular expression.
In the following example code, you explicitly create a sorter object so you can later use it to specify a
filter:
690
//If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter(filterText.getText(), 0);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
In a subsequent example, newFilter() is invoked every time the text field changes. When the user
enters complicated regular expressions, the try...catch prevents the syntax exception from
interfering with input.
When a table uses a sorter, the data the users sees may be in a different order than that specified by
the data model, and may not include all rows specified by the data model. The data the user actually
sees is known as the view, and has its own set of coordinates. JTable provides methods that convert
from model coordinates to view coordinates — convertColumnIndexToView and
convertRowIndexToView — and that convert from view coordinates to model coordinates —
convertColumnIndexToModel and convertRowIndexToModel.
The following example brings together the ideas discussed in this section. TableFilterDemo.java
adds a small number of changes to TableDemo. These include the code snippets earlier in this
section, which provide a sorter for the main table, and use a text field to supply the filtering regular
expression. The following screen shot shows TableFilterDemo before any sorting or filtering has
been done. Notice that row 3 in the model is still the same as row 3 in the view:
If the user clicks twice on the second column, the fourth row becomes the first row — but only in the
view:
691
As previously noted, the text the user enters in the "Filter Text" text field defines a filter that
determines which rows are shown. As with sorting, filtering can cause view coordinates to diverge
from model coordinates:
Here is the code that updates the status field to reflect the current selection:
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {
int viewRow = table.getSelectedRow();
if (viewRow < 0) {
//Selection got filtered away.
statusText.setText("");
} else {
int modelRow =
table.convertRowIndexToModel(viewRow);
statusText.setText(
String.format("Selected Row in view: %d. " +
"Selected Row in model: %d.",
viewRow, modelRow));
}
}
}
);
Setting up a combo box as an editor is simple, as the following example shows. The bold line of code
sets up the combo box as the editor for a specific column.
692
The preceding code is from TableRenderDemo.java. You can run TableRenderDemo (click the
Launch button) using Java™ Web Start (download JDK 6). Or, to compile and run the example
yourself, consult the example index.
Whether you are setting the editor for a single column of cells (using the TableColumn
setCellEditor method) or for a specific type of data (using the JTable setDefaultEditor
method), you specify the editor using an argument that adheres to the TableCellEditor interface.
Fortunately, the DefaultCellEditor class implements this interface and provides constructors to let
you specify an editing component that is a JTextField, JCheckBox, or JComboBox. Usually you do
not have to explicitly specify a check box as an editor, since columns with Boolean data
automatically use a check box renderer and editor.
What if you want to specify an editor other than a text field, check box, or combo box? As
DefaultCellEditor does not support other types of components, you must do a little more work.
You need to create a class that implements the TableCellEditor interface. The
AbstractCellEditor class is a good superclass to use. It implements TableCellEditor's
superinterface, CellEditor, saving you the trouble of implementing the event firing code necessary
for cell editors.
Your cell editor class needs to define at least two methods — getCellEditorValue and
getTableCellEditorComponent. The getCellEditorValue method, required by CellEditor,
returns the cell's current value. The getTableCellEditorComponent method, required by
TableCellEditor, should configure and return the component that you want to use as the editor.
Here is a picture of a table with a dialog that serves, indirectly, as a cell editor. When the user begins
editing a cell in the Favorite Color column, a button (the true cell editor) appears and brings up the
dialog, with which the user can choose a different color.
693
You can run TableDialogEditDemo (click the Launch button) using Java™ Web Start (download
JDK 6). Or, to compile and run the example yourself, consult the example index.
Here is the code, taken from ColorEditor.java, that implements the cell editor.
public ColorEditor() {
button = new JButton();
button.setActionCommand(EDIT);
button.addActionListener(this);
button.setBorderPainted(false);
694
//The user has clicked the cell, so
//bring up the dialog.
button.setBackground(currentColor);
colorChooser.setColor(currentColor);
dialog.setVisible(true);
As you can see, the code is pretty simple. The only part that is a bit tricky is the call to
fireEditingStopped at the end of the editor button's action handler. Without this call, the editor
would remain active, even though the modal dialog is no longer visible. The call to
fireEditingStopped lets the table know that it can deactivate the editor, letting the cell be handled
by the renderer again.
If a cell's default editor allows text entry, you get some error checking for free if the cell's type is
specified as something other than String or Object. The error checking is a side effect of
converting the entered text into an object of the proper type.
The automatic checking of user-entered strings occurs when the default editor attempts to create a
new instance of the class associated with the cell's column. The default editor creates this instance
using a constructor that takes a String as an argument. For example, in a column whose cells have
type Integer, when the user types in "123" the default editor creates the corresponding Integer
using code equivalent to new Integer("123"). If the constructor throws an exception, the cell's
outline turns red and refuses to let focus move out of the cell. If you implement a class used as a
column data type, you can use the default editor if your class supplies a constructor that takes a
single argument of type String.
If you like having a text field as the editor for a cell, but want to customize it — perhaps to check
user-entered text more strictly or to react differently when the text is invalid — you can change the
cell editor to use a formatted text field. The formatted text field can check the value either
continuously while the user is typing or after the user has indicated the end of typing (such as by
pressing Enter).
695
The following code, taken from a demo named TableFTFEditDemo.java, sets up a formatted text
field as an editor that limits all integer values to be between 0 and 100. You can run
TableFTFEditDemo (click the Launch button) using Java™ Web Start (download JDK 6). Or, to
compile and run the example yourself, consult the example index.
The following code makes the formatted text field the editor for all columns that contain data of type
Integer.
table.setDefaultEditor(Integer.class,
new IntegerEditor(0, 100));
The override of getTableCellEditorComponent sets the formatted text field's value property (and
not just the text property it inherits from JTextField) before the editor is shown. The override of
getCellEditorValue keeps the cell value as an Integer, rather than, say, the Long value that the
formatted text field's parser tends to return. Finally, overriding stopCellEditing lets you check
whether the text is valid, possibly stopping the editor from being dismissed. If the text isn't valid,
your implementation of stopCellEditing puts up a dialog that gives the user the option of
continuing to edit or reverting to the last good value. The source code is a bit too long to include
here, but you can find it in IntegerEditor.java.
Printing
JTable provides a simple API for printing tables. The easiest way to print out a table is to invoke
JTable.print with no arguments:
try {
if (! table.print()) {
System.err.println("User cancelled printing");
}
} catch (java.awt.print.PrinterException e) {
System.err.format("Cannot print %s%n", e.getMessage());
}
Invoking print on a normal Swing application brings up a standard printing dialog box. (On a
headless application, the table is simply printed.) The return value indicates whether the user went
ahead with the print job or cancelled it. JTable.print can throw
java.awt.print.PrinterException, which is a checked exception; that's why the above example
uses a try ... catch.
JTable provides several overloads of print with various options. The following code from
TablePrintDemo.java shows how to define a page header:
696
try {
table.print(JTable.PrintMode.FIT_WIDTH, header, null);
} catch (java.awt.print.PrinterException e) {
System.err.format("Cannot print %s%n", e.getMessage());
}
This table lists examples that use JTable and where those examples are described.
697
be shared between the table and list. If you edit an item
in the first column of the table, the new value is
reflected in the list.
Creating
Examples that combine a tree and table to show
TreeTable, TreeTable TreeTables in
detailed information about a hierarchy such as a file
II Swing, Creating
system. The tree is a renderer for the table.
TreeTables: Part 2
Many of the Tutorial's examples use uneditable text areas to display program output. Here is a
picture of an example called TextDemo that enables you to type text using a text field (at the top) and
then appends the typed text to a text area (underneath).
Click the Launch button to run TextDemo using Java™ Web Start (download JDK 6). Alternatively,
to compile and run the example yourself, consult the example index.
You can find the entire code for this program in TextDemo.java. The following code creates and
initializes the text area:
textArea = new JTextArea(5, 20);
JScrollPane scrollPane = new JScrollPane(textArea);
textArea.setEditable(false);
The two arguments to the JTextArea constructor are hints as to the number of rows and columns,
respectively, that the text area should display. The scroll pane that contains the text area pays
attention to these hints when determining how big the scroll pane should be.
Without the creation of the scroll pane, the text area would not automatically scroll. The
JScrollPane constructor shown in the preceding snippet sets up the text area for viewing in a scroll
pane, and specifies that the scroll pane's scroll bars should be visible when needed. See How to Use
Scroll Panes if you want further information.
698
Text areas are editable by default. The code setEditable(false) makes the text area uneditable. It
is still selectable and the user can copy data from it, but the user cannot change the text area's
contents directly.
The following code adds text to the text area. Note that the text system uses the '\n' character
internally to represent newlines; for details, see the API documentation for DefaultEditorKit.
Unless the user has moved the caret (insertion point) by clicking or dragging in the text area, the text
area automatically scrolls so that the appended text is visible. You can force the text area to scroll to
the bottom by moving the caret to the end of the text area after the call to append:
textArea.setCaretPosition(textArea.getDocument().getLength());
You can customize text areas in several ways. For example, although a given text area can display
text in only one font and color, you can set which font and color it uses. This customization option
can be performed on any component. You can also determine how the text area wraps lines and the
number of characters per tab. Finally, you can use the methods that the JTextArea class inherits
from the JTextComponent class to set properties such as the caret, support for dragging, or color
selection.
The following code taken from TextSamplerDemo.java demonstrates initializing an editable text
area. The text area uses the specified italic font, and wraps lines between words.
By default, a text area does not wrap lines that are too long for the display area. Instead, it uses one
line for all the text between newline characters and — if the text area is within a scroll pane —
allows itself to be scrolled horizontally. This example turns line wrapping on with a call to the
setLineWrap method and then calls the setWrapStyleWord method to indicate that the text area
should wrap lines at word boundaries rather than at character boundaries.
To provide scrolling capability, the example puts the text area in a scroll pane.
699
Another Example: TextAreaDemo
The TextAreaDemo example introduces an editable text area with a special feature — a word
completion function. As the user types in words, the program suggests hints to complete the word
whenever the program's vocabulary contains a word that starts with what has been typed. Here is a
picture of the TextAreaDemo application.
Click the Launch button to run TextAreaDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
You can find the entire code for this program in TextAreaDemo.java.
This example provides a scrolling capacity for the text area with the default scroll bar policy. By
default, the vertical scroll bar only appears when the display area is entirely filled with text and there
is no room to append new words. You can provide a scroll pane of this type with the following code:
textArea.setWrapStyleWord(true);
jScrollPane1 = new JScrollPane(textArea);
As mentioned above, the text area is editable. You can play with the text area by typing and pasting
text, or by deleting some parts of text or the entire content. Also try using standard key bindings for
editing text within the text area.
Now explore how the word completion function is implemented. Type in a word like "Swing" or
"special". As soon as you have typed "sw" the program shows a possible completion "ing"
highlighted in light-blue. Press Enter to accept the completion or continue typing.
The following code adds a document listener to the text area's document:
textArea.getDocument().addDocumentListener(this);
When you started typing a word, the insertUpdate method checks whether the program's
vocabulary contains the typed prefix. Once a completion for the prefix is found, a call to the
invokeLater method submits a task for changing the document later. It is important to remember
that you cannot modify the document from within the document event notification, otherwise you
will get an exception. Examine the following code below.
String prefix = content.substring(w + 1).toLowerCase();
int n = Collections.binarySearch(words, prefix);
if (n < 0 && -n <= words.size()) {
String match = words.get(-n - 1);
if (match.startsWith(prefix)) {
// A completion is found
String completion = match.substring(pos - w);
// We cannot modify Document from within notification,
// so we submit a task that does the change later
SwingUtilities.invokeLater(
700
new CompletionTask(completion, pos + 1));
}
} else {
// Nothing found
mode = Mode.INSERT;
}
The code shown in bold illustrates how the selection is created. The caret is first set to the end of the
complete word, then moved back to a position after the last character typed. The
moveCaretPosition method not only moves the caret to a new position but also selects the text
between the two positions. The completion task is implemented with the following code:
private class CompletionTask implements Runnable {
String completion;
int position;
The following tables list the commonly used JTextArea constructors and methods. Other methods
you are likely to call are defined in JTextComponent, and listed in The Text Component API.
You might also invoke methods on a text area 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 areas includes the following categories:
701
Fine Tuning the Text Area's Appearance
Method Purpose
void setEditable(boolean)
boolean isEditable() Sets or indicates whether the user can edit the text in the text area.
(defined in JTextComponent)
void setColumns(int); Sets or obtains the number of columns displayed by the text area. This
int getColumns() is really just a hint for computing the area's preferred width.
void setRows(int); Sets or obtains the number of rows displayed by the text area. This is
int getRows() a hint for computing the area's preferred height.
int setTabSize(int) Sets the number of characters a tab is equivalent to.
Sets whether lines are wrapped if they are too long to fit within the
int setLineWrap(boolean) allocated width. By default this property is false and lines are not
wrapped.
Sets whether lines can be wrapped at white space (word boundaries)
int
or at any character. By default this property is false, and lines can be
setWrapStyleWord(boolean)
wrapped (if line wrapping is turned on) at any character.
Implementing the Text Area's Functionality
Method Purpose
void selectAll()
Selects all characters in the text area.
(defined in JTextComponent)
void append(String) Adds the specified text to the end of the text area.
void insert(String, int) Inserts the specified text at the specified position.
void replaceRange(String, int, Replaces the text between the indicated positions with the specified
int) string.
int getLineCount()
int getLineOfOffset(int) Utilities for finding a line number or the position of the beginning or
int getLineStartOffset(int) end of the specified line.
int getLineEndOffset(int)
This table lists examples that use text areas and points to where those examples are described.
Example Where Described Notes
TextDemo This section An application that appends user-entered text to a text
area.
TextAreaDemo This section An application that has a text area with a word
completion function.
TextSamplerDemo Using Text Uses one of each Swing text components.
Components
HtmlDemo How to Use HTML in A text area that enables the user to type HTML code
Swing Components to be displayed in a label.
BasicDnD Introduction to DnD Demonstrates built-in drag-and-drop functionality of
several Swing components, including text areas.
ExtendedDnDDemo Introduction to DnD Demonstrates dragging and dropping text between a
text area, a list, and a table.
702
DragFileDemo Introduction to DnD
Demonstrates dragging file contents from a file
chooser into a text area. A tabbed pane lets you easily
switch between files.
FocusConceptsDemo How to Use the Focus Demonstrates how focus works using a few
Subsystem components that include a text area.
The Swing API provides several classes for components that are either varieties of text fields or that
include text fields.
The following example displays a basic text field and a text area. The text field is editable. The text
area is not editable. When the user presses Enter in the text field, the program copies the text field's
contents to the text area, and then selects all the text in the text field.
Click the Launch button to run TextDemo using Java™ Web Start (download JDK 6). Alternatively,
to compile and run the example yourself, consult the example index.
You can find the entire code for this program in TextDemo.java. The following code creates and
sets up the text field:
textField = new JTextField(20);
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
703
calculate the field's preferred width. It does not limit the number of characters the user can enter. To
do that, you can either use a formatted text field or a document listener, as described in Text
Component Features.
Note: We encourage you to specify the number of columns for each text field. If you do not specify
the number of columns or a preferred size, then the field's preferred size changes whenever the text
changes, which can result in unwanted layout updates.
The next line of code registers a TextDemo object as an action listener for the text field.
textField.addActionListener(this);
The actionPerformed method handles action events from the text field:
private final static String newline = "\n";
...
public void actionPerformed(ActionEvent evt) {
String text = textField.getText();
textArea.append(text + newline);
textField.selectAll();
}
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 Enter key that
fired the action event.
You have seen how a basic text field can be used. Because the JTextField class inherits from the
JTextComponent class, text fields are very flexible and can be customized almost any way you like.
For example, you can add a document listener or a document filter to be notified when the text
changes, and in the filter case you can modify the text field accordingly. Information on text
components can be found in Text Component Features. Before customizing a JTextField, however,
make sure that one of the other components based on text fields will not do the job for you.
Often text fields are paired with labels that describe the text fields. See Examples That Use Text
Fields for pointers on creating these pairs.
The TextFieldDemo example introduces a text field and a text area. You can find the entire code for
this program in TextFieldDemo.java.
As you type characters in the text field the program searches for the typed text in the text area. If the
entry is found it gets highlighted. If the program fails to find the entry then the text field's
background becomes pink. A status bar below the text area displays a message whether text is found
or not. The Escape key is used to start a new search or to finish the current one. Here is a picture of
the TextFieldDemo application.
704
Click the Launch button ro run TextFieldDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
To highlight text, this example uses a highlighter and a painter. The code below creates and sets up
the highlighter and the painter for the text area.
final Highlighter hilit;
final Highlighter.HighlightPainter painter;
...
hilit = new DefaultHighlighter();
painter = new DefaultHighlighter.DefaultHighlightPainter(HILIT_COLOR);
textArea.setHighlighter(hilit);
This code adds a document listener to the text field's document.
entry.getDocument().addDocumentListener(this);
Document listener's insertUpdate and removeUpdate methods call the search method, which not
only performs a search in the text area but also handles highlighting. The following code highlights
the found text, sets the caret to the end of the found match, sets the default background for the text
field, and displays a message in the status bar.
hilit.addHighlight(index, end, painter);
textArea.setCaretPosition(end);
entry.setBackground(entryBg);
message("'" + s + "' found. Press ESC to end search");
The status bar is a JLabel object. The code below shows how the message method is implemented.
private JLabel status;
...
void message(String msg) {
status.setText(msg);
}
If there is no match in the text area, the following code changes the text field's background to pink
and displays a proper information message.
entry.setBackground(ERROR_COLOR);
message("'" + s + "' not found. Press ESC to start a new search");
The CancelAction class is responsible for handling the Escape key as follows.
class CancelAction extends AbstractAction {
public void actionPerformed(ActionEvent ev) {
hilit.removeAllHighlights();
entry.setText("");
entry.setBackground(entryBg);
}
}
705
The Text Field API
The following tables list the commonly used JTextField constructors and methods. Other methods
you are likely to call are defined in the JTextComponent class. Refer to The Text Component API.
You might also invoke methods on a text field inherited from the text field's 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 falls into these categories:
This table shows a few of the examples that use text fields and points to where those examples are
described. For examples of code that are similar among all varieties of text fields such as dealing
706
with layout, look at the example lists for related components such as formatted text fields and
spinners.
Where
Example Notes
Described
TextDemo This section An application that uses a basic text field with an action
listener.
TextFieldDemo This section An application that uses a text field and a text area. A search is
made in the text area to find an entry from the text field.
DialogDemo How to Make CustomDialog.java includes a text field whose value is
Dialogs checked. You can bring up the dialog by clicking the More
Dialogs tab, selecting the Input-validating dialog option, and
then clicking the Show it! button.
TextSamplerDemo Using Text Lays out label-text field pairs using a GridBagLayout and a
Components convenience method:
addLabelTextRows(JLabel[] labels,
JTextField[] textFields,
GridBagLayout gridbag,
Container container)
TextInputDemo How to Use Lays out label-text field pairs using a SpringLayout and a
Formatted Text SpringUtilities convenience method:
Fields makeCompactGrid(Container parent,
int rows, int cols,
int initialX, int initialY,
int xPad, int yPad)
The following images show an application named ToolBarDemo that contains a tool bar above a text
area. Click the Launch button to run ToolBarDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run it yourself, consult the example index.
707
By default, the user can drag the tool bar to another 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 behavior to work correctly, the tool bar must be in a container that uses the
BorderLayout layout manager. 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, and 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 creates the tool bar and adds it to a container. You can find the entire program in
ToolBarDemo.java.
708
bar can also be dragged out into its own window, in which case the window has the title "Still
draggable", as specified by the JToolBar constructor.
The buttons in the tool bar are ordinary JButton instances that use images from the Java Look and
Feel Graphics Repository. Use images from the Java Look and Feel Graphics Repository if your tool
bar has the Java look and feel.
Here is the code that creates the buttons and adds them to the tool bar.
//first button
button = makeNavigationButton("Back24", PREVIOUS,
"Back to previous something-or-other",
"Previous");
toolBar.add(button);
//second button
button = makeNavigationButton("Up24", UP,
"Up to something-or-other",
"Up");
toolBar.add(button);
return button;
}
The first call to makeNavigationButton creates the image for the first button, using the 24x24
"Back" navigation image in the graphics repository.
Besides finding the image for the button, the makeNavigationButton method also creates the
button, sets the strings for its action command and tool tip text, and adds the action listener for the
709
button. If the image is missing, the method prints an error message and adds text to the button, so
that the button is still usable.
Note: If any buttons in your tool bar duplicate the functionality of other components, such as menu
items, you should probably create and add the tool bar buttons as described in How to Use Actions.
By adding a few lines of code to the preceding example, we can demonstrate some more tool bar
features:
You can see these features by running ToolBarDemo2. Click the Launch button to run
ToolBarDemo2 using Java™ Web Start (download JDK 6). Alternatively, to compile and run it
yourself, consult the example index.
You can find the entire code for this program in ToolBarDemo2.java. Below you can see a picture
of a new UI using these customized features.
Because the tool bar can no longer be dragged, it no longer has bumps at its left edge. Here is the
code that turns off dragging:
toolBar.setFloatable(false);
The tool bar is in rollover mode, so the button under the cursor has a visual indicator. The kind of
visual indicator depends on the look and feel. For example, the Metal look and feel uses a gradient
effect to indicate the button under the cursor while other types of look and feel use borders for this
purpose. Here is the code that sets rollover mode:
toolBar.setRollover(true);
Another visible difference in the example above is that the tool bar contains two new components,
which are preceded by a blank space called a separator. Here is the code that adds the separator:
toolBar.addSeparator();
Here is the code that adds the new components:
710
//fourth button
button = new JButton("Another button");
...
toolBar.add(button);
The following table lists the commonly used JToolBar constructors and methods. Other methods
you might call are listed in the API tables in The JComponent Class.
Method or
Purpose
Constructor
JToolBar() Creates a tool bar. The optional int parameter lets you specify the
JToolBar(int) orientation; the default is HORIZONTAL. The optional String parameter
JToolBar(String) allows you to specify the title of the tool bar's window if it is dragged outside
JToolBar(String, int) of its container.
Adds a component to the tool bar.
Component
add(Component) You can associate a button with an Action using the setAction(Action)
method defined by the AbstractButton.
void addSeparator() Adds a separator to the end of the tool bar.
The floatable property is true by default, and indicates that the user can drag
void
the tool bar out into a separate window. To turn off tool bar dragging, use
setFloatable(boolean)
toolBar.setFloatable(false). Some types of look and feel might ignore
boolean isFloatable()
this property.
void The rollover property is false by default. To make tool bar buttons be
setRollover(boolean) indicated visually when the user passes over them with the cursor, set this
boolean isRollover() property to true. Some types of look and feel might ignore this property.
This table lists examples that use JToolBar and points to where those examples are described.
Where
Example Notes
Described
ToolBarDemo This page A basic tool bar with icon-only buttons.
ToolBarDemo2 This page
Demonstrates a non-floatable tool bar in rollover mode that contains
a separator and a non-button component.
How to Use
ActionDemo Implements a tool bar using Action objects.
Actions
711
How to Use Tool Tips
Creating a tool tip for any JComponent object is easy. Use the setToolTipText method to set up a
tool tip for the component. For example, to add tool tips to three buttons, you add only three lines of
code:
b1.setToolTipText("Click this button to disable the middle button.");
b2.setToolTipText("This middle button does not react when you click it.");
b3.setToolTipText("Click this button to enable the middle button.");
When the user of the program pauses with the cursor over any of the program's buttons, the tool tip
for the button comes up. You can see this by running the ButtonDemo example, which is explained in
How to Use Buttons, Check Boxes, and Radio Buttons. Here is a picture of the tool tip that appears
when the cursor pauses over the left button in the ButtonDemo example.
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 can specify the tab-specific tool tip text in an argument passed to the
addTab or setToolTipTextAt 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. The table and tree sections provide examples of tool tip text determined by a custom
renderer. An alternative that works for all JComponents is creating a subclass of the component and
overriding its getToolTipText(MouseEvent) method.
Most of the API you need in order to set up tool tips belongs to the JComponent class, and thus is
inherited by most Swing components. More tool tip API can be found in individual classes such as
JTabbedPane. In general, those APIs are sufficient for specifying and displaying tool tips; you
usually do not need to deal directly with the implementing classes JToolTip and ToolTipManager.
The following table lists the tool tip API in the JComponent class. For information on individual
components' support for tool tips, see the how-to section for the component in question.
712
By default, returns the same value returned by
getToolTipText(). Multi-part components such as
String
JTabbedPane, JTable, and JTree override this method to return
getToolTipText(MouseEvent)
a string associated with the mouse event location. For example,
each tab in a tabbed pane can have different tool tip text.
Returns the location (in the receiving component's coordinate
system) where the upper left corner of the component's tool tip
Point
appears. The argument is the event that caused the tool tip to be
getToolTipLocation(MouseEvent)
shown. The default return value is null, which tells the Swing
system to choose a location.
This table lists some examples that use tool tips and points to where those examples are described.
Example Where Described Notes
ButtonDemo
This section and How to Use Buttons, Uses a tool tip to provide instructions
Check Boxes, and Radio Buttons for a button.
Uses a tool tip in a label to provide
IconDemo How to Use Icons name and size information for an
image.
Uses tab-specific tool tip text specified
TabbedPaneDemo How to Use Tabbed Panes
in an argument to the addTab method.
Adds tool tips to a table using a
TableRenderDemo Specifying Tool Tips for Cells
renderer.
Specifying Tool Tips for Cells,
TableToolTipsDemo Specifying Tool Tips for Column
Adds tool tips to a table using various
techniques.
Headers
Adds tool tips to a tree using a custom
TreeIconDemo2 Customizing a Tree's Display
renderer.
Adds tool tips to buttons that have been
ActionDemo How to Use Actions
created using Actions.
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
713
either have children or not. We refer to nodes that can have children — whether or not they currently
have children — as branch nodes. Nodes that can not have children are leaf nodes.
Branch nodes can have any number of children. Typically, the user can expand and collapse branch
nodes — making their children visible or invisible — by clicking them. By default, all branch nodes
except the root node start out collapsed. A program can detect changes in branch nodes' expansion
state by listening for tree expansion or tree-will-expand events, as described in How to Write a Tree
Expansion Listener and How to Write a Tree-Will-Expand Listener.
A specific node in a tree can be identified either by a TreePath, an object that encapsulates a node
and all of its ancestors, or by its display row, where each row in the display area displays one node.
An expanded node is a non-leaf node, that will display its children when all its
ancestors are expanded.
A collapsed node is one which hides them.
A hidden node is one which is under a collapsed ancestor.
Creating a Tree
Responding to Node Selection
Customizing a Tree's Display
Dynamically Changing a Tree
Creating a Data Model
The Tree API
Examples that Use Trees
Creating a Tree
Here is a picture of an application, the top half of which displays a tree in a scroll pane.
Try this:
1. Click the Launch button to run the Tree Demo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
714
2. Expand one or more nodes.
You can do this by clicking the circle to the left of the item.
3. Collapse a node.
You do this by clicking the circle to the left of an expanded node.
The following code, taken from TreeDemo.java, creates the JTree object and puts it in a scroll
pane:
Here is the code that creates the nodes under the root node:
//original Tutorial
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial: A Short Course on the Basics",
"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);
715
category = new DefaultMutableTreeNode("Books for Java Implementers");
top.add(category);
//VM
book = new DefaultMutableTreeNode(new BookInfo
("The Java Virtual Machine Specification",
"vm.html"));
category.add(book);
//Language Spec
book = new DefaultMutableTreeNode(new BookInfo
("The Java Language Specification",
"jls.html"));
category.add(book);
}
The argument to the DefaultMutableTreeNode constructor is the user object which is an object that
contains or points to the data associated with the tree node. The user object can be a string, or it can
be a custom object. If you implement a custom object, you should implement its toString method
so that it returns the string to be displayed for that node. JTree, by default, renders each node using
the value returned from toString, so it is important that toString returns something meaningful.
Sometimes, it is not feasible to override toString; in such a scenario you can override the
convertValueToText of JTree to map the object from the model into a string that gets displayed.
For example, the BookInfo class used in the previous code snippet is a custom class that holds two
pieces of data: the name of a book, and the URL for an HTML file describing the book. The
toString method is implemented to return the book name. Thus, each node associated with a
BookInfo object displays a book name.
Note: You can specify text formatting in a tree node by putting HTML tags in the string for the node.
See Using HTML in Swing Components for details.
To summarize, you can create a tree by invoking the JTree constructor, specifying the class that
implements TreeNode as an argument. You should probably put the tree inside a scroll pane, so that
the tree would not take up too much space. You do not have to do anything to make the tree nodes
expand and collapse in response to user clicks. However, you do have to add some code to make the
tree respond when the user selects a node — by clicking the node, for example.
Responding to tree node selections is simple. You implement a tree selection listener and register it
on the tree. The following code shows the selection-related code from the TreeDemo program:
//Where the tree is initialized:
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
716
if (node == null)
//Nothing is selected.
return;
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.
Registers an event handler on the tree. The event handler is an object that implements
the TreeSelectionListener interface.
In the event handler, determines which node is selected by invoking the tree's
getLastSelectedPathComponent method.
Uses the getUserObject method to get the data associated with the node.
For more details about handling tree selection events, see How to Write a Tree Selection Listener.
Here is a picture of some tree nodes, as drawn by the Java, Windows, and Mac OS look and feel
implementations.
Java look and feel Windows look and feel Mac OS look and feel
As the preceding figures show, a tree conventionally displays an icon and some text for each node.
You can customize these, as we will show shortly.
A tree typically also performs some look-and-feel-specific painting to indicate relationships between
nodes. You can customize this painting in a limited way. First, you can use
tree.setRootVisible(true) to show the root node or tree.setRootVisible(false) to hide it.
Second, you can use tree.setShowsRootHandles(true) to request that a tree's top-level nodes —
the root node (if it is visible) or its children (if not) — have handles that let them be expanded or
collapsed.
If you are using the Java look and feel, you can customize whether lines are drawn to show
relationships between tree nodes. By default, the Java look and feel draws angled lines between
nodes. By setting the JTree.lineStyle client property of a tree, you can specify a different
convention. For example, to request that the Java look and feel use only horizontal lines to group
nodes, use the following code:
tree.putClientProperty("JTree.lineStyle", "Horizontal");
717
To specify that the Java look and feel should draw no lines, use this code:
tree.putClientProperty("JTree.lineStyle", "None");
The following snapshots show the results of setting the JTree.lineStyle property, when using the
Java look and feel.
No matter what the look and feel, the default icon displayed by a node is determined by whether the
node is a leaf and, if not, whether it is expanded. For example, in the Windows and Motif look and
feel implementations, the default icon for each leaf node is a dot; in the Java look and feel, the
default leaf icon is a paper-like symbol. In all the look-and-feel implementations we have shown,
branch nodes are marked with folder-like symbols. Some look and feels might have different icons
for expanded branches versus collapsed branches.
You can easily change the default icon used for leaf, expanded branch, or collapsed branch nodes. To
do so, you first create an instance of DefaultTreeCellRenderer. You could always create your own
TreeCellRenderer implementation from scratch, reusing whatever components you like. Next,
specify the icons to use by invoking one or more of the following methods on the renderer:
setLeafIcon (for leaf nodes), setOpenIcon (for expanded branch nodes), setClosedIcon (for
collapsed branch nodes). If you want the tree to display no icon for a type of node, then specify null
for the icon. Once you have set up the icons, use the tree's setCellRenderer method to specify that
the DefaultTreeCellRenderer paint its nodes. Here is an example, taken from
TreeIconDemo.java:
718
Try this:
Click the Launch button to run the TreeIconDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
If you want finer control over the node icons or you want to provide tool tips, you can do so by
creating a subclass of DefaultTreeCellRenderer and overriding the
getTreeCellRendererComponent method. Because DefaultTreeCellRenderer is a subclass of
JLabel, you can use any JLabel method — such as setIcon — to customize the
DefaultTreeCellRenderer.
The following code, from TreeIconDemo2.java, creates a cell renderer that varies the leaf icon
depending on whether the word "Tutorial" is in the node's text data. The renderer also specifies tool-
tip text, as the bold lines show.
Try this:
Click the Launch button to run the TreeIconDemo2 using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself,
consult the example index.
super.getTreeCellRendererComponent(
719
tree, value, sel,
expanded, leaf, row,
hasFocus);
if (leaf && isTutorialBook(value)) {
setIcon(tutorialIcon);
setToolTipText("This book is in the Tutorial series.");
} else {
setToolTipText(null); //no tool tip
}
return this;
}
return false;
}
}
Here is the result:
You might be wondering how a cell renderer works. When a tree paints each node, neither the JTree
nor its look-and-feel-specific implementation actually contains the code that paints the node. Instead,
the tree uses the cell renderer's painting code to paint the node. For example, to paint a leaf node that
has the string "The Java Programming Language", the tree asks its cell renderer to return a
component that can paint a leaf node with that string. If the cell renderer is a
DefaultTreeCellRenderer, then it returns a label that paints the default leaf icon followed by the
string.
A cell renderer only paints; it cannot handle events. If you want to add event handling to a tree, you
need to register your handler on either the tree or, if the handling occurs only when a node is
selected, the tree's cell editor. For information about cell editors, see Concepts: Editors and
Renderers. That section discusses table cell editors and renderers, which are similar to tree cell
editors and renderers.
720
The following figure shows an application called DynamicTreeDemo that lets you add nodes to and
remove nodes from a visible tree. You can also edit the text in each node.
Try this:
Click the Launch button to run the DynamicTreeDemo using Java™ Web
Start (download JDK 6). Alternatively, to compile and run the example
yourself, consult the example index.
To make the text in the tree's nodes editable, we invoke setEditable(true) on the tree. When the
user has finished editing a node, the model generates a tree model event that tells any listeners —
including the JTree — that tree nodes have changed. Note that although DefaultMutableTreeNode
has methods for changing a node's content, changes should go through the DefaultTreeModel cover
methods. Otherwise, the tree model events would not be generated, and listeners such as the tree
would not know about the updates.
721
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode)
(e.getTreePath().getLastPathComponent());
/*
* If the event lists children, then the changed
* node is the child of the node we have already
* gotten. Otherwise, the changed node and the
* specified node are the same.
*/
try {
int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode)
(node.getChildAt(index));
} catch (NullPointerException exc) {}
Here is the code that the Add button's event handler uses to add a new node to the tree:
if (parentPath == null) {
//There is no selection. Default to the root node.
parentNode = rootNode;
} else {
parentNode = (DefaultMutableTreeNode)
(parentPath.getLastPathComponent());
}
//Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
The code creates a node, inserts it into the tree model, and then, if appropriate, requests that the
nodes above it be expanded and the tree scrolled so that the new node is visible. To insert the node
722
into the model, the code uses the insertNodeInto method provided by the DefaultTreeModel
class.
If DefaultTreeModel does not suit your needs, then you will need to write a custom data model.
Your data model must implement the TreeModel interface. TreeModel specifies methods for getting
a particular node of the tree, getting the number of children of a particular node, determining whether
a node is a leaf, notifying the model of a change in the tree, and adding and removing tree model
listeners.
Interestingly, the TreeModel interface accepts any kind of object as a tree node. It does not require
that nodes be represented by DefaultMutableTreeNode objects, or even that nodes implement the
TreeNode interface. Thus, if the TreeNode interface is not suitable for your tree model, feel free to
devise your own representation for tree nodes. For example, if you have a pre-existing hierarchical
data structure, you do not need to duplicate it or force it into the TreeNode mold. You just need to
implement your tree model so that it uses the information in the existing data structure.
The following figure shows an application called GenealogyExample that displays the descendants
or ancestors of a particular person. (Thanks to tutorial reader Olivier Berlanger for providing this
example.)
Try this:
Click the Launch button to run the Genealogy Example using Java™ Web
Start (download JDK 6). Alternatively, to compile and run the example
yourself, consult the example index.
You can find the custom tree model implementation in GenealogyModel.java. Because the model is
implemented as an Object subclass instead of, say, a subclass of DefaultTreeModel, it must
implement the TreeModel interface directly. This requires implementing methods for getting
information about nodes, such as which is the root and what are the children of a particular node. In
the case of GenealogyModel, each node is represented by an object of type Person, a custom class
that does not implement TreeNode.
723
A tree model must also implement methods for adding and removing tree model listeners, and must
fire TreeModelEvents to those listeners when the tree's structure or data changes. For example, when
the user instructs GenealogyExample to switch from showing ancestors to showing descendants, the
tree model makes the change and then fires an event to inform its listeners (such as the tree
component).
Lazy loading is a characteristic of an application when the actual loading and instantiation of a class
is delayed until the point just before the instance is actually used.
Do we gain anything by loading them lazily? Yes, this would definitely add to the performance of an
application. By lazily loading, you can dedicate the memory resources to load and instantiate an
object only when it is actually used. You can also speed up the initial loading time of an application.
One of the ways you can lazily load children of a Tree is by utilizing the TreeWillExpandListener
interface. For example, you can declare and load root, grandparent and parent of a Tree along with
the application as shown in the following code:
dummyParent = parent;
return root;
}
You can load above declared nodes to the tree as shown in the following code:
TreeNode rootNode = createNodes();
tree = new JTree(rootNode);
tree.addTreeExpansionListener(this);
tree.addTreeWillExpandListener(this);
.......
.......
setViewportView(tree);
Now, you can load children lazily to the application whenever the parent node Restaurants is
visible in the application. To do this, let us declare two children in a separate method and call that
method as shown in the following code:
724
private void LoadLazyChildren(){
DefaultMutableTreeNode child;
child = new DefaultMutableTreeNode("Thai Barbeque");
dummyParent.add(child);
child = new DefaultMutableTreeNode("Goat Hill Pizza");
dummyParent.add(child);
textArea.append(" Thai Barbeque and Goat Hill Pizza are loaded
lazily");
}
.......
.......
LoadLazyChildren();
}
The tree API is quite extensive. The following tables list just a bit of the API, concentrating on the
following categories:
For more information about the tree API, see the API documentation for JTree and for the various
classes and interfaces in the tree package. Also refer to The JComponent Class for information on the
API JTree inherits from its superclass.
725
TreeCellEditor Respectively, the interface that a tree cell editor must implement and
DefaultTreeCellEditor the usual implementation used.
TreeSelectionModel Respectively, the interface that the tree's selection model must
DefaultTreeSelectionModel implement and the usual implementation used.
TreeSelectionListener The interface and event type used for detecting tree selection changes.
TreeSelectionEvent For more information, see Getting Started.
TreeModelListener The interface and event type used for detecting tree model changes. For
TreeModelEvent more information, see How to Write a Tree Model Listener.
TreeExpansionListener The interfaces and event type used for detecting tree expansion and
TreeWillExpandListener collapse. For more information, see How to Write a Tree Expansion
TreeExpansionEvent Listener and How to Write a Tree-Will-Expand Listener.
An exception that a TreeWillExpandListener can throw to indicate
ExpandVetoException 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
Create a tree. The TreeNode argument specifies the root node,
to be managed by the default tree model. The TreeModel
argument specifies the model that provides the data to the table.
The no-argument version of this constructor is for use in
JTree(TreeNode) builders; it creates a tree that contains some sample data. If you
JTree(TreeNode, boolean) specify a Hashtable, array of objects, or Vector as an
JTree(TreeModel) argument, then the argument is treated as a list of nodes under
JTree() the root node (which is not displayed), and a model and tree
JTree(Hashtable) nodes are constructed accordingly.
JTree(Object[])
JTree(Vector) The boolean argument, if present, specifies how the tree
should determine whether a node should be displayed as a leaf.
If the argument is false (the default), any node without children
is displayed as a leaf. If the argument is true, a node is a leaf
only if its getAllowsChildren method returns false.
void
Set the renderer that draws each node.
setCellRenderer(TreeCellRenderer)
The first method sets whether the user can edit tree nodes. By
void setEditable(boolean)
default, tree nodes are not editable. The second sets which
void setCellEditor(TreeCellEditor)
customized editor to use.
Set whether the tree shows the root node. The default value is
void setRootVisible(boolean) false if the tree is created using one of the constructors that
takes a data structure, and true otherwise.
Set whether the tree shows handles for its leftmost nodes,
void letting you expand and collapse the nodes. The default is false.
setShowsRootHandles(boolean) If the tree does not show the root node, then you should invoke
setShowsRootHandles(true).
Set or get the dragEnabled property, which must be true to
void setDragEnabled(boolean)
enable drag handling on this component. The default value is
boolean getDragEnabled()
false. See Drag and Drop for more details.
Implementing Selection
726
Method Purpose
void Register a listener to detect when the a node is
addTreeSelectionListener(TreeSelectionListener) selected or deselected.
Set or get the model used to control node
void setSelectionModel(TreeSelectionModel)
selections. You can turn off node selection
TreeSelectionModel getSelectionModel()
completely using setSelectionModel(null).
Set or get the selection mode. The value can be
void setSelectionMode(int) 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 node.
TreePath getSelectionPath()
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 node.
TreePath getSelectionPath()
Showing and Hiding Nodes
Method Purpose
Register a listener to detect when the tree
void nodes have expanded or collapsed, or will be
addTreeExpansionListener(TreeExpansionListener) expanded or collapsed, respectively. To veto
void an impending expansion or collapse, a
addTreeWillExpandListener(TreeWillExpandListener) TreeWillExpandListener can throw a
ExpandVetoException.
void expandPath(TreePath)
Expand or collapse the specified tree path.
void collapsePath(TreePath)
Ensure that the node specified by the path is
visible — that the path leading up to it is
void scrollPathToVisible(TreePath)
expanded and the node is in the scroll pane's
viewing area.
Ensure that the node specified by the path is
viewable — that the path leading up to it is
void makeVisible(TreePath)
expanded. The node might not end up within
the viewing area.
Set or get whether the tree attempts to scroll
void setScrollsOnExpand(boolean)
to show previous hidden nodes. The default
boolean getScrollsOnExpand()
value is true.
Set or get the number of mouse clicks before
void setToggleClickCount(int)
a node will expand or close. The default is
int getToggleClickCount()
two.
TreePath getNextMatch(String, int, Position.Bias) Return the TreePath to the next tree
727
element that begins with the specific prefix.
This table lists examples that use JTree and where those examples are described.
Example Where Described Notes
Creating a Tree,
Creates a tree that responds to user
Responding to Node
TreeDemo selections. It also has code for customizing
Selection, Customizing a
the line style for the Java look and feel.
Tree's Display
Customizing a Tree's
TreeIconDemo Adds a custom leaf icon to TreeDemo.
Display
Customizing a Tree's Customizes certain leaf icons and also
TreeIconDemo2
Display provides tool tips for certain tree nodes.
Dynamically Changing a Illustrates adding and removing nodes from
DynamicTreeDemo
Tree a tree. Also allows editing of node text.
Implements a custom tree model and
GenealogyExample Creating a Data Model
custom node type.
How to Write a Tree Shows how to detect node expansions and
TreeExpandEventDemo
Expansion Listener collapses.
How to Write a Tree-
TreeExpandEventDemo2 Shows how to veto node expansions.
Will-Expand Listener
Creating TreeTables in Examples in The Swing Connection that
TreeTable, TreeTable II, Swing, Creating combine a tree and table to show detailed
Editable JTreeTable TreeTables: Part 2, information about a hierarchy such as a file
Editable JTreeTable system. The tree is a renderer for the table.
To specify that a component's text has HTML formatting, just put the <html> tag at the beginning of
the text, then use any valid HTML in the remainder. Here is an example of using HTML in a button's
text:
728
An Example: HtmlDemo
An application called HtmlDemo lets you play with HTML formatting by setting the text on a label.
You can find the entire code for this program in HtmlDemo.java. Here is a picture of the HtmlDemo
example.
Try This:
1. Click the Launch button to run HtmlDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the
example index.
2. Edit the HTML formatting in the text area at the left and click the "Change the
label" button. The label at the right shows the result.
3. Remove the <html> tag from the text area on the left. The label's text is no
longer parsed as HTML.
Example 2: ButtonHtmlDemo
Let us look at another example that uses HTML. ButtonHtmlDemo adds font, color, and other text
formatting to three buttons. You can find the entire code for this program in ButtonHtmlDemo.java.
Here is a picture of the ButtonHtmlDemo example.
Click the Launch button to run ButtonHtmlDemo using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
729
The left and right buttons have multiple lines and text styles and are implemented using HTML. The
middle button, on the other hand, uses just one line, font, and color, so it does not 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));
...
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 have to use a <u> tag to cause the mnemonic characters "D" and "E" to be underlined in
the buttons that use HTML. Note also that when a button is disabled, its HTML text unfortunately
remains black, instead of becoming gray. (Refer to bug #4783068 to see if this situation changes.)
This section discussed how to use HTML in ordinary, non-text components. For information on
components whose primary purpose is formatting text, see Using Text Components.
You often don't need to know about the models that a component uses. For example, programs that
use buttons usually deal directly with the JButton object, and don't deal at all with the ButtonModel
object.
Why then do models exist? The biggest reason is that they give you flexibility in determining how
data is stored and retrieved. For example, if you're designing a spreadsheet application that displays
data in a sparsely populated table, you can create your own table model that is optimized for such
use.
Models have other benefits, too. They mean that data isn't copied between a program's data structures
and those of the Swing components. Also, models automatically propagate changes to all interested
listeners, making it easy for the GUI to stay in sync with the data. For example, to add items to a list
you can invoke methods on the list model. When the model's data changes, the model fires events to
the JList and any other registered listeners, and the GUI is updated accordingly.
730
architecture is more accurately described as a separable model architecture. If you're interested in
learning more about the Swing model architecture, see A Swing Architecture Overview, an article in
The Swing Connection.
An Example: Converter
This section features an example called Converter, which is an application that continuously converts
distance measurements between metric and U.S. units. You can run Converter ( download JDK 6).
Or, to compile and run the example yourself, consult the example index.
As the following picture shows, Converter features two sliders, each tied to a text field. The sliders
and text fields all display the same data — a distance — but using two different units of measure.
The important thing for this program is ensuring that only one model controls the value of the data.
There are various ways to achieve this; we did it by deferring to the top slider's model. The bottom
slider's model (an instance of a custom class called FollowerRangeModel) forwards all data queries
to the top slider's model (an instance of a custom class called ConverterRangeModel). Each text
field is kept in sync with its slider, and vice versa, by event handlers that listen for changes in value.
Care is taken to ensure that the top slider's model has the final say about what distance is displayed.
When we started implementing the custom slider models, we first looked at the API section of How
to Use Sliders. It informed us that all slider data models must implement the BoundedRangeModel
interface. The BoundedRangeModel API documentation tells us that the interface has an
implementing class named DefaultBoundedRangeModel. The API documentation for
DefaultBoundedRangeModel shows that it's a general-purpose implementation of
BoundedRangeModel.
We didn't use DefaultBoundedRangeModel directly because it stores data as integers, and Converter
uses floating-point data. Thus, we implemented ConverterRangeModel as a subclass of Object. We
then implemented FollowerRangeModel as a subclass of ConverterRangeModel.
To find out about the models for individual components, see the "How to" pages and API
documentation for individual components. Here are some of our examples that use models directly:
All but the simplest of the table examples implement custom table data models.
The color chooser demos have change listeners on the color chooser's selection model
so they can be notified when the user selects a new color. In ColorChooserDemo2, the
CrayonPanel class directly uses the color selection model to set the current color.
731
The DynamicTreeDemo example sets the tree model (to an instance of
DefaultTreeModel), interacts directly with it, and listens for changes to it.
ListDemo sets the list data model (to an instance of DefaultListModel) and interacts
directly with it.
SharedModelDemo defines a SharedDataModel class that extends
DefaultListModel and implements TableModel. A JList and JTable share an
instance of SharedDataModel, providing different views of the model's data.
In the event listener examples, ListDataEventDemo creates and uses a
DefaultListModel directly.
Our spinner examples create spinner models.
As you've already seen, the Converter example defines two custom slider models.
Here's a snapshot of an application with three labels, two decorated with an icon:
The program uses one image icon to contain and paint the yellow splats. One statement creates the
image icon and two more statements include the image icon on each of the two labels:
The createImageIcon method (used in the preceding snippet) is one we use in many of our code
samples. It finds the specified file and returns an ImageIcon for that file, or null if that file couldn't
be found. Here is a typical implementation:
732
return null;
}
}
In the preceding snippet, the first argument to the ImageIcon constructor is relative to the location of
the current class, and will be resolved to an absolute URL. The description argument is a string
that allows assistive technologies to help a visually impaired user understand what information the
icon conveys.
Generally, applications provide their own set of images used as part of the application, as is the case
with the images used by many of our demos. You should use the Class getResource method to
obtain the path to the image. This allows the application to verify that the image is available and to
provide sensible error handling if it is not. When the image is not part of the application,
getResource should not be used and the ImageIcon constructor is used directly. For example:
When you specify a filename or URL to an ImageIcon constructor, processing is blocked until after
the image data is completely loaded or the data location has proven to be invalid. If the data location
is invalid (but non-null), an ImageIcon is still successfully created; it just has no size and, therefore,
paints nothing. As shown in the createImageIcon method, it is advisable to first verify that the
URL points to an existing file before passing it to the ImageIcon constructor. This allows graceful
error handling when the file isn't present. If you want more information while the image is loading,
you can register an observer on an image icon by calling its setImageObserver method.
Under the covers, each image icon uses an Image object to hold the image data.
Here's an application that uses six image icons. Five of them display thumbnail images and the sixth
diplays the full size the photograph.
733
Try this:
1. Click the Launch button to run IconDemo using Java™ Web Start (download
JDK 6). Or, to compile and run the example yourself, consult the example
index.
2. Click any of the thumbnail images to view the full size photographs.
3. Hold the mouse over a photograph. A tool tip appears that displays the
photograph caption.
The photographs are loaded in a separate thread by loadimages.execute. The loadimages code is
shown a little later in this section.
/**
* Action class that shows the image specified in it's constructor.
*/
private class ThumbnailAction extends AbstractAction{
/**
*The icon if the full image we want to display.
*/
734
private Icon displayPhoto;
/**
* @param Icon - The full size photo to show in the button.
* @param Icon - The thumbnail to show in the button.
* @param String - The descriptioon of the icon.
*/
public ThumbnailAction(Icon photo, Icon thumb, String desc){
displayPhoto = photo;
/**
* Shows the full image in the main area and sets the application title.
*/
public void actionPerformed(ActionEvent e) {
photographLabel.setIcon(displayPhoto);
setTitle("Icon Demo: " + getValue(SHORT_DESCRIPTION).toString());
}
}
Most often, an image icon's data comes from an image file. There are a number of valid ways that
your application's class and image files may be configured on your file server. You might have your
class files in a JAR file, or your image files in a JAR file; they might be in the same JAR file, or they
might be in different JAR files. The following figures illustrate a few of the ways these files can be
configured:
Class file next to an image directory containing the image file, in PNG Class file in same directory as JAR file. The JAR file was created with all the images in
format. an images directory.
Class file in one JAR file and the images in another JAR file. Class and image files in same JAR file.
If you are writing a real-world application, it is likely (and recommended) that you put your files into
a package. For more information on packages, see Creating and Using Packages in the Learning the
Java Language trail. Here are some possible configurations using a package named "omega":
735
Class file in directory named omega. Image in omega/images Class file in omega directory. Image in JAR file not inside of omega
directory. directory, but created with omega/images hierarchy.
One big JAR file with class files under omega directory and image files
under omega/images directory.
All seven configurations shown are valid, and the same code reads the image:
The getResource method causes the class loader to look through the directories and JAR files in the
program's class path, returning a URL as soon as it finds the desired file. In the example the
MyDemo program attempts to load the images/myImage.png file from the omega class. The class
loader looks through the directories and JAR files in the program's class path for
/omega/images/myImage.png. If the class loader finds the file, it returns the URL of the JAR file or
directory that contained the file. If another JAR file or directory in the class path contains the
images/myImage.png file, the class loader returns the first instance that contains the file.
If your image and class files are in separate JAR files, your command line will
look something like:
In the situation where all the files are in one JAR file, you can use either of the
following commands:
736
java -cp .;MyAppPlusImages.jar MyApp [Microsoft Windows]
In the program's JNLP file (used by Java Web Start). For example, here is the
JNLP file used by DragPictureDemo:
<?xml version="1.0" encoding="utf-8"?>
<!-- JNLP File for DragPictureDemo -->
<jnlp
spec="1.0+"
codebase="http://java.sun.com/docs/books/tutorialJWS/src/
uiswing/misc/examples"
href="DragPictureDemo.jnlp">
<information>
<title>DragPictureDemo</title>
<vendor>The Java(tm) Tutorial: Sun Microsystems,
Inc.</vendor>
<homepage
href="http://java.sun.com/docs/books/tutorial/uiswing/mis
c/examples/index.html#DragPictureDemo"/>
<description>DragPictureDemo</description>
<description kind="short">A demo showing how to
install
data transfer on a custom component.</description>
<offline-allowed/>
</information>
<resources>
<j2se version="1.6+"/>
<jar href="allClasses.jar"/>
<jar href="images.jar"/>
</resources>
<application-desc main-class="DragPictureDemo"/>
</jnlp>
In this example, the class files and the images files are in separate JAR files.
The JAR files are specified using the XML jar tag.
Most of the Swing Tutorial examples put the images in an images directory under the directory that
contains the examples' class files. When we create JAR files for the examples, we keep the same
relative locations, although often we put the class files in a different JAR file than the image JAR
file. No matter where the class and image files are in the file system — in one JAR file, or in
multiple JAR files, in a named package, or in the default package — the same code finds the image
files using getResource.
For more information, see Accessing Resources in a Location-Independent Manner and the
Application Development Considerations.
737
Loading Images Into Applets
Applets generally load image data from the computer that served up the applet. The APPLET tag is
where you specify information about the images used in the applet. For more information on the
APPLET tag see Using the APPLET Tag
Because the photograph images can be slow to access, IconDemoApp.java uses a SwingWorker to
improve the performance of the program as perceived by the user.
Background image loading — the program uses a javax.swing.SwingWorker object to load each
photograph image and compute it's thumbnail in a background thread. Using a SwingWorker
prevents the program from appearing to freeze up while loading and scaling the images.
/**
* SwingWorker class that loads the images a background thread and calls publish
* when a new one is ready to be displayed.
*
* We use Void as the first SwingWroker param as we do not need to return
* anything from doInBackground().
*/
private SwingWorker loadimages = new SwingWorker() {
/**
* Creates full size and thumbnail versions of the target image files.
*/
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < imageCaptions.length; i++) {
ImageIcon icon;
icon = createImageIcon(imagedir + imageFileNames[i],
imageCaptions[i]);
ThumbnailAction thumbAction;
if(icon != null){
} else {
// the image failed to load for some reason
// so load a placeholder instead
thumbAction = new ThumbnailAction(placeholderIcon,
placeholderIcon, imageCaptions[i]);
}
publish(thumbAction);
}
// unfortunately we must return something, and only null is valid to
// return when the return type is void.
return null;
}
738
/**
* Process all loaded images.
*/
@Override
protected void process(List chunks) {
for (ThumbnailAction thumbAction : chunks) {
JButton thumbButton = new JButton(thumbAction);
// add the new button BEFORE the last glue
// this centers the buttons in the toolbar
buttonBar.add(thumbButton, buttonBar.getComponentCount() - 1);
}
}
};
SwingWorker invokes the doInBackground method in a background thread. The method places a
full size image, thumbnail size image and caption into a ThumbnailAction object. The
SwingWorker then delivers the ThumbnailAction to the process method. The process method
executes on the event dispatch thread and updates the GUI by adding a button to the toolbar.
JButton has a constructor that takes an action object. The action object determines a number of the
button's properties. In our case the button icon, the caption and the action to be performed when the
button is pressed is all determined by the ThumbnailAction.
Overhead — this program eventually loads all the source images into memory. This may not be
desirable in all situations. Loading a number of very large files could cause the program to allocate a
very large amount or memory. Care should be taken to manage the number and size of images that
are loaded.
As with all performance-related issues, this technique is applicable in some situations and not others.
Also the technique described here is designed to improve the program's perceived performance, but
does not necessarily impact its real performance.
The createImageIcon method returns null when it cannot find an image, but what should the
program do then? One possibility would be to ignore that image and move on. Another option would
be to provide some sort of default icon to display when the real one cannot be loaded. Making
another call to createImageIcon might result in another null so using that is not a good idea. Instead
lets create a custom Icon implementation.
739
You can find the implementation of the custom icon class in MissingIcon.java. Here are the
interesting parts of its code:
/**
* The "missing icon" is a white box with a black border and a red x.
* It's used to display something when there are issues loading an
* icon from an external location.
*
* @author Collin Fagan
*/
public class MissingIcon implements Icon{
g2d.setColor(Color.WHITE);
g2d.fillRect(x +1 ,y + 1,width -2 ,height -2);
g2d.setColor(Color.BLACK);
g2d.drawRect(x +1 ,y + 1,width -2 ,height -2);
g2d.setColor(Color.RED);
g2d.setStroke(stroke);
g2d.drawLine(x +10, y + 10, x + width -10, y + height -10);
g2d.drawLine(x +10, y + height -10, x + width -10, y + 10);
g2d.dispose();
}
The paintIcon method is passed a Graphics object. The Graphics object gives the paintIcon
method access to the entire Java2D API. For more information about painting and Java2D, see
Performing Custom Painting.
The following code demonstrates how the MissingIcon class is used in the SwingWorker
doInBackground method.
...
if(icon != null) {
...
} else {
// the image failed to load for some reason
// so load a placeholder instead
740
thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon,
imageCaptions[i]);
}
The following tables list the commonly used ImageIcon constructors and methods. Note that
ImageIcon is not a descendent of JComponent or even of Component.
The API for using image icons falls into these categories:
741
Setting or Getting Information about the Image Icon
Method Purpose
void
Set or get a description of the image. This description is intended for use
setDescription(String)
by assistive technologies.
String getDescription()
int getIconWidth()
Get the width or height of the image icon in pixels.
int getIconHeight()
Watching the Image Icon's Image Load
Method Purpose
void
setImageObserver(ImageObserver) Set or get an image observer for the image icon.
ImageObserver getImageObserver()
Get the loading status of the image icon's image. The values
int getImageLoadStatus()
returned by this method are defined by MediaTracker.
The following table lists just a few of the many examples that use ImageIcon.
Note: The photographs used in the IconDemo are copyright ©2006 spriggs.net and licenced under a
Creative Commons Licence.
742
How to Use Borders
Every JComponent can have one or more borders. Borders are incredibly useful objects that, while
not themselves components, know how to draw the edges of Swing components. Borders are useful
not only for drawing lines and fancy edges, but also for providing titles and empty space around
components.
Note: Our examples set borders on JPanels, JLabels, and custom subclasses of JComponent.
Although technically you can set the border on any object that inherits from JComponent, the look
and feel implementation of many standard Swing components doesn't work well with user-set
borders. In general, when you want to set a border on a standard Swing component other than
JPanel or JLabel, we recommend that you put the component in a JPanel and set the border on the
JPanel.
To put a border around a JComponent, you use its setBorder method. You can use the
BorderFactory class to create most of the borders that Swing provides. If you need a reference to a
border — say, because you want to use it in multiple components — you can save it in a variable of
type Border. Here is an example of code that creates a bordered container:
Here's a picture of the container, which contains a label component. The black line drawn by the
border marks the edge of the container.
The following pictures show an application called BorderDemo that displays the borders Swing
provides. We show the code for creating these borders a little later, in Using the Borders Provided by
Swing.
Click the Launch button to run the BorderDemo example using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
743
The next picture shows some matte borders. When creating a matte border, you specify how many
pixels it occupies at the top, left, bottom, and right of a component. You then specify either a color or
an icon for the matte border to draw. You need to be careful when choosing the icon and determining
your component's size; otherwise, the icon might get chopped off or have mismatch at the
component's corners.
The next picture shows titled borders. Using a titled border, you can convert any border into one that
displays a text description. If you don't specify a border, a look-and-feel-specific border is used. For
example, the default titled border in the Java look and feel uses a gray line, and the default titled
border in the Windows look and feel uses an etched border. By default, the title straddles the upper
left of the border, as shown at the top of the following figure.
744
The next picture shows compound borders. With compound borders, you can combine any two
borders, which can themselves be compound borders.
The code that follows shows how to create and set the borders you saw in the preceding figures. You
can find the program's code in BorderDemo.java.
//Keep references to the next few borders,
//for use in titles and compound borders.
Border blackline, raisedetched, loweredetched,
raisedbevel, loweredbevel, empty;
blackline = BorderFactory.createLineBorder(Color.black);
raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
raisedbevel = BorderFactory.createRaisedBevelBorder();
loweredbevel = BorderFactory.createLoweredBevelBorder();
745
empty = BorderFactory.createEmptyBorder();
//Simple borders
jComp1.setBorder(blackline);
jComp2.setBorder(raisedbevel);
jComp3.setBorder(loweredbevel);
jComp4.setBorder(empty);
//Matte borders
ImageIcon icon = createImageIcon("images/wavy.gif",
"wavy-line border icon"); //20x22
jComp5.setBorder(BorderFactory.createMatteBorder(
-1, -1, -1, -1, icon));
jComp6.setBorder(BorderFactory.createMatteBorder(
1, 5, 1, 1, Color.red));
jComp7.setBorder(BorderFactory.createMatteBorder(
0, 20, 0, 0, icon));
//Titled borders
TitledBorder title;
title = BorderFactory.createTitledBorder("title");
jComp8.setBorder(title);
title = BorderFactory.createTitledBorder(
blackline, "title");
title.setTitleJustification(TitledBorder.CENTER);
jComp9.setBorder(title);
title = BorderFactory.createTitledBorder(
loweredetched, "title");
title.setTitleJustification(TitledBorder.RIGHT);
jComp10.setBorder(title);
title = BorderFactory.createTitledBorder(
loweredbevel, "title");
title.setTitlePosition(TitledBorder.ABOVE_TOP);
jComp11.setBorder(title);
title = BorderFactory.createTitledBorder(
empty, "title");
title.setTitlePosition(TitledBorder.BOTTOM);
jComp12.setBorder(title);
//Compound borders
Border compound;
Border redline = BorderFactory.createLineBorder(Color.red);
746
As you probably noticed, the code uses the BorderFactory class to create each border. The
BorderFactory class, which is in the javax.swing package, returns objects that implement the
Border interface.
If BorderFactory doesn't offer you enough control over a border's form, then you might need to
directly use the API in the border package — or even define your own border. In addition to
containing the Border interface, the border package contains the classes that implement the borders
you've already seen: LineBorder, EtchedBorder, BevelBorder, EmptyBorder, MatteBorder,
TitledBorder, and CompoundBorder. The border package also contains a class named
SoftBevelBorder, which produces a result similar to BevelBorder, but with softer edges.
If none of the Swing borders is suitable, you can implement your own border. Generally, you do this
by creating a subclass of the AbstractBorder class. In your subclass, you must implement at least
one constructor and the following two methods:
paintBorder, which contains the drawing code that a JComponent executes to draw
the border.
getBorderInsets, which specifies the amount of space the border needs to draw
itself.
If a custom border has insets (and they typically have insets) you need to override both
AbstractBorder.getBorderInsets(Component c) and
AbstractBorder.getBorderInsets(Component c, Insets insets) to provide the correct insets.
For examples of implementing borders, see the source code for the classes in the
javax.swing.border package.
The following tables list the commonly used border methods. The API for using borders falls into
two categories:
747
Border createEtchedBorder(int) border methods to be specified as either
Border createEtchedBorder(int, EtchedBorder.RAISED or EtchedBorder.LOWERED. The
Color, Color) methods without the int arguments create a lowered etched
border.
Create a border that gives the illusion of the component being
Border createLoweredBevelBorder()
lower than the surrounding area.
CompoundBorder Combine two borders into one. The first argument specifies the
createCompoundBorder(Border, outer border; the second, the inner border.
Border)
748
Setting or Getting a Component's Border
Method Purpose
void setBorder(Border) Set or get the border of the receiving
Border getBorder() JComponent.
void setBorderPainted(boolean)
boolean isBorderPainted() Set or get whether the border of the
(in AbstractButton, JMenuBar, JPopupMenu, component should be displayed.
JProgressBar, and JToolBar)
Many examples in this lesson use borders. The following table lists a few interesting cases.
Where
Example Notes
Described
Shows an example of each type of border that BorderFactory
BorderDemo This section can create. Also uses an empty border to add breathing space
between each pane and its contents.
How to Use
BoxAlignmentDemo Uses titled borders.
BoxLayout
BoxLayoutDemo
How to Use Uses a red line to show where the edge of a container is, so that
BoxLayout you can see how the extra space in a box layout is distributed.
Uses a compound border to combine a line border with an
How to Use
ComboBoxDemo2 empty border. The empty border provides space between the
Combo Boxes
line and the component's innards.
Problem: I am having trouble implementing a model (or some other code that is similar to
something already in Java SE Platform, Standard Edition).
Look at the Java SE source code. It is distributed with the JDK, and it is a great resource for
finding code examples of implementing models, firing events, and the like.
Problem: Whenever the text in my text field updates, the text field's size changes.
You should specify the preferred width of the text field by specifying the number of columns
it should have room to display. To do this, you can use either an int argument to the
JTextField constructor or the setColumns method.
Problem: Certain areas of the content pane look weird when they are repainted.
749
If you set the content pane, make sure it is opaque. You can do this by invoking
setOpaque(true) on your content pane. Note that although JPanels are opaque in most look
and feels, that is not true in the GTK+ look and feel. See Adding Components to the Content
Pane for details.
If one or more of your components performs custom painting, make sure you implemented it
correctly. See Solving Common Painting Problems for help.
You might have a thread safety problem. See the next entry.
Problem: My program is exhibiting weird symptoms that sometimes seem to be related to timing.
Make sure your code is thread-safe. See Concurrency in Swing for details.
If the dialog has a null parent component, try setting it to a valid frame or component when
you create it.
This bug was fixed in the 6.0 release. For more information, see 4255200.
If you want a scroll bar to appear all the time, specify either VERTICAL_SCROLLBAR_ALWAYS
or HORIZONTAL_SCROLLBAR_ALWAYS for the scroll bar policy as appropriate.
If you want the scroll bars to appear as needed, and you want to force the scroll bars to be
needed when the scroll pane is created, you have two choices: either set the preferred size of
scroll pane or its container, or implement a scroll-savvy class and return a value smaller than
the component's standard preferred size from the getPreferredScrollableViewportSize
method. Refer to Sizing a Scroll Pane for information.
You need to set the minimum size of at least one of the components in the split pane. Refer to
Positioning the Divider and Restricting Its Range for information.
The setDividerLocation(double) method has no effect if the split pane has no size
(typically true if it is not onscreen yet). You can either use setDividerLocation(int) or
specify the preferred sizes of the split pane's contained components and the split pane's resize
weight instead. Refer to Positioning the Divider and Restricting Its Range for information.
750
If you nest split panes, the borders accumulate — the border of the inner split panes display
next to the border of the outer split pane causing borders that look extra wide. The problem is
particularly noticeable when nesting many split panes. The workaround is to set the border to
null on any split pane that is placed within another split pane. For information, see bug #
4131528 in the Bug Database at the Sun Developer Network (SDN).
This can happen if you use an int instead of an Integer when adding components to a
layered pane. To see what happens, make the following change to LayeredPaneDemo:
Problem: The method call colorChooser.setPreviewPanel(null) does not remove the color
chooser's preview panel as expected.
A null argument specifies the default preview panel. To remove the preview panel, specify a
standard panel with no size, like this: colorChooser.setPreviewPanel(new JPanel());
Use the information in this lesson and the component how-to sections to help you complete these
questions and exercises.
Questions
1. Find the component that best fits each of the following needs. Write down both the component’s
common name (such as “frame”) and find the component's how-to page online. [Hint: You can use A
Visual Index to the Swing Components to help you answer this question.]
b. A component that displays an icon, but that doesn’t react to user clicks.
c. A component that looks like a button and that, when pressed, brings up a menu of items for the
user to choose from.
d. A container that looks like a frame, but that appears (usually with other, similar containers) within
a real frame.
e. A container that lets the user determine how two components share a limited amount of space.
2. Which method do you use to add a menu bar to a top-level container such as a JFrame?
751
3. Which method do you use to specify the default button for a top-level container such as a JFrame
or JDialog?
4. Which method do you use to enable and disable components such as JButtons? What class is it
defined in?
5. a. Which Swing components use ListSelectionModel? [Hint: The “Use” link at the top of the
specification for each interface and class takes you to a page showing where in the API that interface
or class is referenced.]
b. Do those components use any other models to handle other aspects of the components’ state? If so,
list the other models’ types.
Exercises
1. Implement a program with a GUI that looks like the one shown below. Put the main method in a
class named MyDemo1.
Use the information in this lesson and the component how-to sections to help you complete these
questions and exercises.
Questions
Question 1: Find the component that best fits each of the following needs. Write down both the
component’s common name (such as “frame”) and find the component's how-to page online. [Hint:
You can use A Visual Index to the Swing Components to help you answer this question.]
Question 1b: A component that displays an icon, but that doesn’t react to user clicks.
Answer 1b: label
Question 1c: A component that looks like a button and that, when pressed, brings up a menu of items
752
for the user to choose from.
Answer 1c: uneditable combo box
Question 1d: A container that looks like a frame, but that appears (usually with other, similar
containers) within a real frame.
Answer 1d: internal frame
Question 1e: A container that lets the user determine how two components share a limited amount of
space.
Answer 1e: split pane
Question 2: Which method do you use to add a menu bar to a top-level container such as a JFrame?
Answer 2: setJMenuBar
Question 3: Which method do you use to specify the default button for a top-level container such as a
JFrame or JDialog?
Answer 3: JRootPane's setDefaultButton method. (You get the top-level container's root pane
using the getRootPane method defined by the RootPaneContainer interface, which every top-level
container implements.)
Question 4: Which method do you use to enable and disable components such as JButtons? What
class is it defined in?
Answer 4: setEnabled, which is defined in the Component class
Question 5a: Which Swing components use ListSelectionModel? [Hint: The “Use” link at the top
of the specification for each interface and class takes you to a page showing where in the API that
interface or class is referenced.]
Answer 5a: JList and JTable
Question 5b: Do those components use any other models to handle other aspects of the components’
state? If so, list the other models’ types.
Answer 5b: JList also uses a ListModel, which holds the list's data. JTable uses a TableModel to
hold its data and a TableColumnModel to manage the table's columns.
Exercises
Exercise 1. Implement a program with a GUI that looks like the one shown below. Put the main
method in a class named MyDemo1.
Answer 1: See MyDemo1.java . Here's the code that adds the bold, italicized text:
753
frame.getContentPane().add(BorderLayout.CENTER, label);
label.setFont(label.getFont().deriveFont(Font.ITALIC | Font.BOLD));
label.setHorizontalAlignment(JLabel.CENTER)
Exercise 2. Make a copy of MyDemo1.java named MyDemo2.java. Add a menu bar to MyDemo2.
Answer 2: See MyDemo2.java . The menu bar can be implemented with this code:
754
Lesson: Concurrency in Swing
This lesson discusses concurrency as it applies to Swing programming. It assumes that you are
already familiar with the content of the Concurrency lesson in the Essential Classes trail.
Careful use of concurrency is particularly important to the Swing programmer. A well-written Swing
program uses concurrency to create a user interface that never "freezes" — the program is always
responsive to user interaction, no matter what it's doing. To create a responsive program, the
programmer must learn how the Swing framework employs threads.
The programmer does not need to provide code that explicitly creates these threads: they are
provided by the runtime or the Swing framework. The programmer's job is to utilize these threads to
create a responsive, maintainable Swing program.
Like any other program running on the Java platform, a Swing program can create additional threads
and thread pools, using the tools described in the Concurrency lesson. But for basic Swing programs
the threads described here are sufficient.
This lesson discuss each of the three kinds of threads in turn. Worker threads require the most
discussion because tasks that run on them are created using javax.swing.SwingWorker. This class
has many useful features, including communication and coordination between worker thread tasks
and the tasks on other threads.
Initial Threads
Every program has a set of threads where the application logic begins. In standard programs, there's
only one such thread: the thread that invokes the main method of the program class. In applets the
initial threads are the ones that construct the applet object and invoke its init and start methods;
these actions may occur on a single thread, or on two or three different threads, depending on the
Java platform implementation. In this lesson, we call these threads the initial threads.
In Swing programs, the initial threads don't have a lot to do. Their most essential job is to create a
Runnable object that initializes the GUI and schedule that object for execution on the event dispatch
thread. Once the GUI is created, the program is primarily driven by GUI events, each of which
causes the execution of a short task on the event dispatch thread. Application code can schedule
additionals tasks on the event dispatch thread (if they complete quickly, so as not to interfere with
event processing) or a worker thread (for long-running tasks).
755
difference is indicated by their names: invokeLater simply schedules the task and returns;
invokeAndWait waits for the task to finish before returning.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
}
In an applet, the GUI-creation task must be launched from the init method using invokeAndWait;
otherwise, init may return before the GUI is created, which may cause problems for a web browser
launching an applet. In any other kind of program, scheduling the GUI-creation task is usually the
last thing the initial thread does, so it doesn't matter whether it uses invokeLater or
invokeAndWait.
Why does not the initial thread simply create the GUI itself? Because almost all code that creates or
interacts with Swing components must run on the event dispatch thread. This restriction is discussed
further in the next section.
A note on thread safety: It may seem strange that such an important part of the Java platform is not
thread safe. It turns out that any attempt to create a thread-safe GUI library faces some fundamental
problems. For more on this issue, see the following entry in Graham Hamilton's blog: MultiThreaded
toolkits: A failed dream?
It's useful to think of the code running on the event dispatch thread as a series of short tasks. Most
tasks are invocations of event-handling methods, such as ActionListener.actionPerformed.
Other tasks can be scheduled by application code, using invokeLater or invokeAndWait. Tasks on
the event dispatch thread must finish quickly; if they don't, unhandled events back up and the user
interface becomes unresponsive.
If you need to determine whether your code is running on the event dispatch thread, invoke
javax.swing.SwingUtilities.isEventDispatchThread.
756
SwingWorker provides a number of communication and control features:
The SwingWorker subclass can define a method, done, which is automatically invoked on the
event dispatch thread when the background task is finished.
SwingWorker implements java.util.concurrent.Future. This interface allows the
background task to provide a return value to the other thread. Other methods in this interface
allow cancellation of the background task and discovering whether the background task has
finished or been cancelled.
The background task can provide intermediate results by invoking SwingWorker.publish,
causing SwingWorker.process to be invoked from the event dispatch thread.
The background task can define bound properties. Changes to these properties trigger events,
causing event-handling methods to be invoked on the event dispatch thread.
Note: The javax.swing.SwingWorker class was added to the Java platform in Java SE 6. Prior to
this, another class, also called SwingWorker, was widely used for some of the same purposes. The
old SwingWorker was not part of the Java platform specification, and was not provided as part of the
JDK.
The new javax.swing.SwingWorker is a completely new class. Its functionality is not a strict
superset of the old SwingWorker. Methods in the two classes that have the same function do not have
the same names. Also, instances of the old SwingWorker class were reusable, while a new instance
of javax.swing.SwingWorker is needed for each new background task.
To avoid these problems, TumbleItem creates and executes an instance of SwingWorker from its
initial threads. The object's doInBackground method, executing in a worker thread, loads the images
into an ImageIcon array, and returns a reference to it. Then the done method, executing in the event
dispatch thread, invokes get to retrieve this reference, which it assigns to an applet class field named
imgs This allows TumbleItem to contruct the GUI immediately, without waiting for the images to
finish loading.
Here is the code that defines and executes the SwingWorker object.
757
}
@Override
public void done() {
//Remove the "Loading images" label.
animator.removeAll();
loopslot = -1;
try {
imgs = get();
} catch (InterruptedException ignore) {}
catch (java.util.concurrent.ExecutionException e) {
String why = null;
Throwable cause = e.getCause();
if (cause != null) {
why = cause.getMessage();
} else {
why = e.getMessage();
}
System.err.println("Error retrieving file: " + why);
}
}
};
Notice that SwingWorker is a generic class, with two type parameters. The first type parameter
specifies a return type for doInBackground, and also for the get method, which is invoked by other
threads to retrieve the object returned by doInBackground. SwingWorker's second type parameter
specifies a type for interim results returned while the background task is still active. Since this
example doesn't return interim results, Void is used as a placeholder.
You may wonder if the code that sets imgs is unnecessarily complicated. Why make
doInBackground return an object and use done to retrieve it? Why not just have doInBackground
set imgs directly? The problem is that the object imgs refers to is created in the worker thread and
used in the event dispatch thread. When objects are shared between threads in this way, you must
make sure that changes made in one thread are visible to the other. Using get guarantees this,
because using get creates a happens before relationship between the code that creates imgs and the
code that uses it. For more on the happens before relationship, refer to Memory Consistency Errors
in the Concurrency lesson.
There are actually two ways to retrieve the object returned by doInBackground.
Be careful when invoking either overload of get from the event dispatch thread; until get returns, no
GUI events are being processed, and the GUI is "frozen". Don't invoke get without arguments unless
you are confident that the background task is complete or close to completion.
For more on the TumbleItem example, refer to How to Use Swing Timers in the lesson Using Other
Swing Features.
758
Tasks that Have Interim Results
It is often useful for a background task to provide interim results while it is still working. The task
can do this by invoking SwingWorker.publish. This method accepts a variable number of
arguments. Each argument must be of the type specified by SwingWorker's second type parameter.
Let's look at the way the Flipper.java example uses publish to provide interim results. Click the
Launch button to run Flipper using Java™ Web Start (download JDK 6). Or, to compile and run the
example yourself, consult the example index.
This program tests the fairness of java.util.Random by generating a series of random boolean
values in a background task. This is equivalent to flipping a coin; hence the name Flipper. To report
its results, the background task uses an object of type FlipPair
759
headsText.setText(String.format("%d", pair.heads));
totalText.setText(String.format("%d", pair.total));
devText.setText(String.format("%.10g",
((double) pair.heads)/((double) pair.total) - 0.5));
}
If Random is fair, the value displayed in devText should get closer and closer to 0 as Flipper runs.
Note: The setText method used in Flipper is actually "thread safe" as defined in its specification.
That means that we could dispense with publish and process and set the text fields directly from
the worker thread. We've chosen to ignore this fact in order to provide a simple demonstration of
SwingWorker interim results.
The cancel method takes a single boolean argument. If the argument is true, cancel sends the
background task an interrupt. Whether the argument is true or false, invoking cancel changes the
cancellation status of the object to true. This is the value returned by isCanceled. Once changed,
the cancellation status cannot be changed back.
The Flipper example from the previous section uses the status-only idiom. The main loop in
doInBackground exits when isCancelled returns true. This will occur when the user clicks the
"Cancel" button, triggering code that invokes cancel with an argument of false.
The status-only approach makes sense for Flipper because its implementation of
SwingWorker.doInBackground does not include any code that might throw
InterruptedException. To respond to an interrupt, the background task would have to invoke
Thread.isInterrupted at short intervals. It's just as easy to use SwingWorker.isCancelled for
the same purpose
Note: If get is invoked on a SwingWorker object after its background task has been cancelled,
java.util.concurrent.CancellationException is thrown.
760
By implementing a property change listener, a program can track changes to progress, state, and
other bound properties. For more information, refer to How to Write a Property Change Listener in
Writing Event Listeners.
The progress bound variable is an int value that can range from 0 to 100. It has a predefined setter
method (the protected SwingWorker.setProgress) and a predefined getter method (the public
SwingWorker.getProgress).
The ProgressBarDemo example uses progress to update a ProgressBar control from a background
task. For a detailed discussion of this example, refer to How to Use Progress Bars in Using Swing
Components.
The state bound variable indicates where the SwingWorker object is in its lifecycle. The bound
variable contains an enumeration value of type SwingWorker.StateValue. Possible values are:
PENDING
The state during the period from the construction of the object until just before
doInBackground is invoked.
STARTED
The state during the period from shortly before doInBackground is invoked until shortly
before done is invoked.
DONE
The state for the remainder of the existence of the object.
The current value of the state bound variable is returned by SwingWorker.getState.
Status Methods
Two methods, part of the Future interface, also report on the status of the background task. As we
saw in Canceling Background Tasks, isCancelled returns true if the task has been canceled. In
addition, isDone returns true if the task has finished, either normally, or by being cancelled.
Questions and Exercises: Concurrency in Swing
Questions
1. For each of the following tasks, specify which thread it should be executed in and why.
o Initializing the GUI.
o Loading a large file.
o Invoking javax.swing.JComponent.setFont to change the font of a component.
o Invoking javax.swing.text.JTextComponent.setText to change the text of a
component.
2. One set of threads is not used for any of the tasks mentioned in the previous question. Name
this thread and explain why its applications are so limited.
3. SwingWorker has two type parameters. Explain how these type parameters are used, and why
it often doesn't matter what they are.
761
Exercises
1. Modify the Flipper example so that it pauses 5 seconds between "coin flips." If the user
clicks the "Cancel", the coin-flipping loop terminates immediately.
Questions
Question 1: For each of the following tasks, specify which thread it should be executed in and why.
Answer 1:
Initializing the GUI. The event dispatch thread; most interactions with the GUI
framework must occur on this thread.
Loading a large file. A worker thread. Executing this task on the event dispatch thread
would prevent GUI events from being processed, "freezing" the GUI until the task is
finished. Executing this task on an initial thread would cause a delay in creating the
GUI.
Invoking javax.swing.JComponent.setFont to change the font of a component.
The event dispatch thread. As with most Swing methods, it is not safe to invoke
setFont from any other thread.
Invoking javax.swing.text.JTextComponent.setText to change the text of a
component. This method is documented as thread-safe, so it can be invoked from any
thread.
Question 2: One thread is not the preferred thread for any of the tasks mentioned in the previous
question. Name this thread and explain why its applications are so limited.
Answer 2: The initial threads launch the first GUI task on the event dispatch thread. After that, a
Swing program is primarily driven by GUI events, which trigger tasks on the event dispatch thread
and the worker thread. Usually, the initial threads are left with nothing to do.
Question 3: SwingWorker has two type parameters. Explain how these type parameters are used, and
why it often doesn't matter what they are.
Answer 3: The type parameters specify the type of the final result (also the return type of the
doInBackground method) and the type of interim results (also the argument types for publish and
process). Many background tasks do not provide final or interim results.
Exercises
Question 1: Modify the Flipper example so that it pauses 5 seconds between "coin flips." If the user
clicks the "Cancel", the coin-flipping loop terminates immediately.
Answer 1: See the source code for Flipper2. The modified program adds a delay in the central
doInBackground loop:
protected Object doInBackground() {
long heads = 0;
long total = 0;
Random random = new Random();
while (!isCancelled()) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
//Cancelled!
return null;
762
}
total++;
if (random.nextBoolean()) {
heads++;
}
publish(new FlipPair(heads, total));
}
return null;
}
The try ... catch causes doInBackground to return if an interrupt is received while the thread is
sleeping. Invoking cancel with an argument of true ensures that an interrupt is sent when the task is
cancelled.
763
Lesson: Using Other Swing Features
This lesson contains a collection of how-to pages to help you use miscellaneous Swing features.
With the Desktop class you can enable your Java application to interact with default applications
associated with specific file types on the host platform.
With Action objects, you can coordinate the state and event handling of two or more components
that generate action events. For example, you can use a single Action to create and coordinate a
tool-bar button and a menu item that perform the same function.
With the Swing Timer class, you can implement a thread that performs an action after a delay, and
optionally continues to repeat the action. The action executes in the event dispatch thread.
Swing components have built-in support for assistive technologies. Your program can provide even
better support by following a few rules.
Some programs need to manipulate focus — for example, to validate input, or change the tab-order
of components. This section describes some techniques you can use to customize focus in your
program.
With key bindings, you can specify how components react to user typing.
This section describes a new modality model emerged in the Java™ SE version 6 and enables you to
apply different modality types to dialog boxes.
This section describes the printing capabilities for tables and explains how to add printing support to
your programs.
764
This section describes the printing capabilities for text components and explains how to add printing
support to your programs.
With the SplashScreen class you can close the splash screen, change the splash-screen image,
obtain the image position or size, and paint in the splash screen.
This section describes how to add a tray icon to the system tray and apply a text tooltip, a pop-up
menu, ballon messages, and a set of listeners associated with it.
This section tells you how to fix problems you might encounter while trying to use the information in
this lesson.
Java™ Standard Edition version 6 narrows the gap between performance and integration of native
applications and Java applications. Along with the new system tray functionality, splash screen
support, and enhanced printing for JTables , Java SE version 6 provides the Desktop API
(java.awt.Desktop) API, which allows Java applications to interact with default applications
associated with specific file types on the host platform.
New functionality is provided by the Desktop class. The API arises from the JDesktop Integration
Components (JDIC) project. The goal of the JDIC project is to make "Java technology-based
applications first-class citizens" of the desktop, enabling seamless integration. JDIC provides Java
applications with access to functionalities and facilities provided by the native desktop. Regarding
the new Desktop API, this means that a Java application can perform the following operations:
Launch the host system's default browser with a specific Uniform Resource Identifier (URI)
Launch the host system's default email client
Launch applications to open, edit, or print files associated with those applications
Note: The Desktop API uses the host operating system's file associations to launch applications
associated with specific file types. For example, if OpenDocument text (.odt) file extensions are
associated with the OpenOffice Writer application, a Java application could launch OpenOffice
Writer to open, edit, or even print files with that association. Depending on the host system, different
applications may be associated with different actions. For example, if a particular file cannot be
printed, check first whether its extension has a printing association on the given operating system.
Use the isDesktopSupported() method to determine whether the Desktop API is available. On the
Solaris Operating System and the Linux platform, this API is dependent on Gnome libraries. If those
libraries are unavailable, this method will return false. After determining that the Desktop API is
765
supported, that is, the isDesktopSupported() returns true, the application can retrieve a Desktop
instance using the static method getDesktop() .
Once retrieved, the Desktop instance allows an application to browse, mail, open, edit, or even print
a file or URI, but only if the retrieved Desktop instance supports these activities. Each of these
activities is called an action, and each is represented as a Desktop.Action enumeration instance:
Different applications may be registered for these different actions even on the same file type. For
example, the Firefox browser may be launched for the OPEN action, Emacs for the EDIT action, and
yet a different application for the PRINT action. Your host desktop's associations are used to
determine which application should be invoked. The ability to manipulate desktop file associations is
not possible with the current version of the Desktop API in JDK 6, and those associations can be
created or changed only with platform-dependent tools at this time.
Try this:
3. Enter an URI value into the URI text field, for example –
http://java.sun.com/docs/books/tutorial.
4. Press the Launch Browser button.
5. Ensure that the default browser window opens and the Tutorial main page is loaded.
6. Change URI to an arbitrary value, press the Launch Browser button, and ensure that
the web page you requested is loaded successfully.
766
7. Switch back to the DesktopDemo dialog box and enter a mail recipient name in the E-
mail text field. You can also use the mailto scheme supporting CC, BCC, SUBJECT,
and BODY fields, for example – [email protected]?SUBJECT=Hello Duke!.
8. Press the Launch Mail button.
9. The compositing dialog box of the default email client will appear. Be sure that the To
and Subject fields are as follows.
10. You can continue to compose a message or try to enter different combinations of the
mail schema in the E-mail field.
11. Switch back to the DesktopDemo dialog box and press the ellipsis button to choose
any text file.
12. Select either Open, Edit, or Print to set the type of operation, then press the Launch
Application button.
13. Be sure that operation completes correctly. Try other file formats, for example .odt,
.html, .pdf. Note: If you try to edit .pdf file, the DesktopDemo returns the
following message:
Cannot perform the given operation to the < file name> file
The following code snippets provide more details on the DeskDemo application implementation. The
DesktopDemo constructor disables the few components right after instantiating the UI and checks
whether the Desktop API is available.
public DesktopDemo() {
// init all gui components
initComponents();
// disable buttons that launch browser, email client,
// disable buttons that open, edit, print files
disableActions();
// before any Desktop APIs are used, first check whether the API is
// supported by this particular VM on this particular host
if (Desktop.isDesktopSupported()) {
desktop = Desktop.getDesktop();
// now enable buttons for actions that are supported.
enableSupportedActions();
}
...
/**
* Disable all graphical components until we know
767
* whether their functionality is supported.
*/
private void disableActions() {
txtBrowserURI.setEnabled(false);
btnLaunchBrowser.setEnabled(false);
txtMailTo.setEnabled(false);
btnLaunchEmail.setEnabled(false);
rbEdit.setEnabled(false);
rbOpen.setEnabled(false);
rbPrint.setEnabled(false);
txtFile.setEnabled(false);
btnLaunchApplication.setEnabled(false);
}
...
Once a Desktop object is acquired, you can query the object to find out which specific actions are
supported. If the Desktop object does not support specific actions, or if the Desktop API itself is
unsupported, DesktopDemo simply keeps the affected graphical components disabled.
/**
* Enable actions that are supported on this host.
* The actions are the following: open browser,
* open email client, and open, edit, and print
* files using their associated application.
*/
private void enableSupportedActions() {
if (desktop.isSupported(Desktop.Action.BROWSE)) {
txtBrowserURI.setEnabled(true);
btnLaunchBrowser.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.MAIL)) {
txtMailTo.setEnabled(true);
btnLaunchEmail.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.OPEN)) {
rbOpen.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.EDIT)) {
rbEdit.setEnabled(true);
}
if (desktop.isSupported(Desktop.Action.PRINT)) {
rbPrint.setEnabled(true);
}
768
try {
uri = new URI(txtBrowserURI.getText());
desktop.browse(uri);
} catch(IOException ioe) {
System.out.println("The system cannot find the " + uri +
" file specified");
//ioe.printStackTrace();
} catch(URISyntaxException use) {
System.out.println("Illegal character in path");
//use.printStackTrace();
}
}
Applications can launch the host's default email client, if that action is supported, by calling the
mail(uriMailTo) method of this Desktop instance.
private void onLaunchMail(ActionEvent evt) {
String mailTo = txtMailTo.getText();
URI uriMailTo = null;
try {
if (mailTo.length() > 0) {
uriMailTo = new URI("mailto", mailTo, null);
desktop.mail(uriMailTo);
} else {
desktop.mail();
}
} catch(IOException ioe) {
ioe.printStackTrace();
} catch(URISyntaxException use) {
use.printStackTrace();
}
}
Java applications can open, edit, and print files from their associated application using the open(),
edit(), and print() methods of the Desktop class, respectively.
private void onLaunchDefaultApplication(ActionEvent evt) {
String fileName = txtFile.getText();
File file = new File(fileName);
try {
switch(action) {
case OPEN:
desktop.open(file);
break;
case EDIT:
desktop.edit(file);
break;
case PRINT:
desktop.print(file);
break;
}
} catch (IOException ioe) {
//ioe.printStackTrace();
System.out.println("Cannot perform the given operation
to the " + file + " file");
}
}
The complete code for this demo is available in the DesktopDemo.java file.
The Desktop class allows Java applications to launch the native desktop applications that handle
URIs or files.
769
Method Purpose
Tests whether this class is supported on the current platform. If it is
isDesktopSupported()
supported, use getDesktop() to retrieve an instance.
Returns the Desktop instance of the current browser context. On some
platforms the Desktop API may not be supported. Use the
getDesktop()
isDesktopSupported() method to determine if the current desktop is
supported.
Tests whether an action is supported on the current platform. Use the
isSupported(Desktop.Action) following constans of the Desktop.Action enum: BROWSE, EDIT,
MAIL, OPEN, PRINT.
Launches the default browser to display a URI. If the default browser
is not able to handle the specified URI, the application registered for
browse(URI) handling URIs of the specified type is invoked. The application is
determined from the protocol and path of the URI, as defined by the
URI class.
Launches the mail composing window of the user default mail client,
mail(URI)
filling the message fields specified by a mailto: URI.
open(File) Launches the associated application to open a file.
edit(File) Launches the associated editor application and opens a file for editing.
Prints a file with the native desktop printing facility, using the
print(File)
associated application's print command.
The following table lists the example that uses the Desktop class integration.
Where
Example Notes
Described
DesktopDemo This section Launches the host system's default browser with the specified URI and
default email client; launches an application to open, edit, or print a file.
You typically attach an action to a component using the setAction method. Here's what happens
when setAction is invoked on a component:
The component's state is updated to match the state of the Action. For example, if the
Action's text and icon values were set, the component's text and icon are set to those
values.
The Action object is registered as an action listener on the component.
770
If the state of the Action changes, the component's state is updated to match the
Action. For example, if you change the enabled status of the action, all components
it's attached to change their enabled states to match the action.
Here's an example of creating a tool-bar button and menu item that perform the same function:
To create an Action object, you generally create a subclass of AbstractAction and then instantiate
it. In your subclass, you must implement the actionPerformed method to react appropriately when
the action event occurs. Here's an example of creating and instantiating an AbstractAction
subclass:
When the action created by the preceding code is attached to a button and a menu item, the button
and menu item display the text and icon associated with the action. The L character is used for
mnemonics on the button and menu item, and their tool-tip text is set to the SHORT_DESCRIPTION
string followed by a representation of the mnemonic key.
For example, we have provided a simple example, ActionDemo.java, which defines three actions.
Each action is attached to a button and a menu item. Thanks to the mnemonic values set for each
button's action, the key sequence Alt-L activates the left button, Alt-M the middle button, and Alt-R
the right button. The tool tip for the left button displays This is the left button. Alt-L. All of this
configuration occurs automatically, without the program making explicit calls to set the mnemonic or
tool-tip text. As we'll show later, the program does make calls to set the button text, but only to avoid
using the values already set by the actions.
771
Try this:
1. Click the Launch button to run ActionDemo using Java™ Web Start
(download JDK 6). Or, to compile and run the example yourself, consult the
example index.
2. Choose the top item from the left menu (Menu > Go left).
The text area displays some text identifying both the event source and the
action listener that received the event.
3. Click the leftmost button in the tool bar.
The text area again displays information about the event. Note that although
the source of the events is different, both events were detected by the same
action listener: the Action object attached to the components.
4. Choose the top item from the Action State menu.
This disables the "Go left" Action object, which in turn disables its associated
menu item and button.
Here is what the user sees when the "Go left" action is disabled:
772
After you create components using an Action, you might well need to customize them. For example,
you might want to customize the appearance of one of the components by adding or deleting the icon
or text. For example, ActionDemo.java has no icons in its menus, and no text in its buttons. Here's
the code that accomplishes this:
menuItem = new JMenuItem();
menuItem.setAction(leftAction);
menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
...
button = new JButton();
button.setAction(leftAction);
button.setText(""); //an icon-only button
We chose to create an icon-only button and a text-only menu item from the same action by setting
the icon property to null and the text to an empty string. However, if a property of the Action
changes, the widget may try to reset the icon and text from the Action again.
The following tables list the commonly used Action constructors and methods. The API for using
Action objects falls into three categories:
Action Properties
773
This table defines the properties that can be set on an action. The second column lists which
components automatically use the properties (and what method is specifically called). For example,
setting the ACCELERATOR_KEY on an action that is then attached to a menu item, means that
JMenuItem.setAccelerator(KeyStroke) is called automatically.
Auto-Applied to:
Property Class Purpose
(Method Called)
The KeyStroke to be used as the
JMenuItem
accelerator for the action. For a discussion
ACCELERATOR_KEY (setAccelerator) of accelerators versus mnemonics, see
Enabling Keyboard Operation. Introduced
in 1.3.
AbstractButton,
, The command string associated with the
ACTION_COMMAND_KEY JCheckBox
JRadioButton ActionEvent.
(setActionCommand)
The longer description for the action. Can
LONG_DESCRIPTION None
be used for context-sensitive help.
AbstractButton, The mnemonic for the action. For a
JMenuItem, JCheckBox, discussion of accelerators versus
MNEMONIC_KEY JRadioButton mnemonics, see Enabling Keyboard
(setMnemonic) Operation. Introduced in 1.3.
The name of the action. You can set this
AbstractButton,
property when creating the action using the
JMenuItem, JCheckBox,
NAME AbstractAction(String) or
JRadioButton
AbstractAction(String, Icon)
(setText)
constructors.
AbstractButton,
JCheckBox,
SHORT_DESCRIPTION JRadioButton
The short description of the action.
(setToolTipText)
The icon for the action used in the tool bar
AbstractButton, or on a button. You can set this property
SMALL_ICON JMenuItem when creating the action using the
(setIcon) AbstractAction(name, icon)
constructor.
774
discussion begins in Concepts: About Editor Kits.
In general, we recommend using Swing timers rather than general-purpose timers for GUI-related
tasks because Swing timers all share the same, pre-existing timer thread and the GUI-related task
automatically executes on the event-dispatch thread. However, you might use a general-purpose
timer if you don't plan on touching the GUI from the timer, or need to perform lengthy processing.
Swing timers are very easy to use. When you create the timer, you specify an action listener to be
notified when the timer "goes off". The actionPerformed method in this listener should contain the
code for whatever task you need to be performed. When you create the timer, you also specify the
number of milliseconds between timer firings. If you want the timer to go off only once, you can
invoke setRepeats(false) on the timer. To start the timer, call its start method. To suspend it,
call stop.
Note that the Swing timer's task is performed in the event dispatch thread. This means that the task
can safely manipulate components, but it also means that the task should execute quickly. If the task
might take a while to execute, then consider using a SwingWorker instead of or in addition to the
timer. See Concurrency in Swing for instructions about using the SwingWorker class and information
on using Swing components in multi-threaded programs.
Let's look at an example of using a timer to periodically update a component. The TumbleItem
applet uses a timer to update its display at regular intervals. (To see this applet running, go to How to
Make Applets. This applet begins by creating and starting a timer:
After starting the timer, TumbleItem begins loading a series of images in a background thread.
Meanwhile, the timer events begin to occur, causing the actionPerformed method to execute:
775
//If still loading, can't animate.
if (!worker.isDone()) {
return;
}
loopslot++;
if (off < 0) {
off = width - maxWidth;
} else if (off + maxWidth > width) {
off = 0;
}
}
animator.repaint();
if (loopslot == nimgs - 1) {
timer.restart();
}
}
Until the images are loaded, worker.isDone returns false, so timer events are effectively ignored.
The first part of the event handling code simply sets values that are employed in the animation
control's paintComponent method: loopslot (the index of the next graphic in the animation) and
off (the horizontal offset of the next graphic).
Eventually, loopslot will reach the end of the image array and start over. When this happens, the
code at the end of actionPerformed restarts the timer. Doing this causes a short delay before the
animation sequence begins again.
Assistive technologies — voice interfaces, screen readers, alternate input devices, and so on — are
useful not only for people with disabilities, but also for people using computers in non-office
environments. For example, if you're stuck in a traffic jam, you might use assistive technologies to
check your email, using only voice input and output. The information that enables assistive
technologies can be used for other tools, as well, such as automated GUI testers and input devices
such as touchscreens. Assistive technologies get information from components using the
Accessibility API, which is defined in the javax.accessibility package.
Because support for the Accessibility API is built into the Swing components, your Swing program
will probably work just fine with assistive technologies, even if you do nothing special. For example,
assistive technologies can automatically get the text information that is set by the following lines of
code:
776
Assistive technologies can also grab the tool-tip text (if any) associated with a component and use it
to describe the component to the user.
Making your program function smoothly with assistive technologies is easy to do and, in the United
States, may be required by federal law. For more information see Global Legal Resources for IT
Related Accessibility Issues.
Here are a few things you can do to make your program work as well as possible with assistive
technologies:
If a component doesn't display a short string (which serves as its default name),
specify a name with the setAccessibleName method. You might want to do this for
image-only buttons, panels that provide logical groupings, text areas, and so on.
Set tool tip text for components whenever it makes sense to do so. For example:
aJComponent.setToolTipText(
"Clicking this component causes XYZ to happen.");
If you don't want to provide a tool tip for a component, use the
setAccessibleDescription method to provide a description that assistive
technologies can give the user. For example:
aJComponent.getAccessibleContext().
setAccessibleDescription(
"Clicking this component causes XYZ to happen.");
Specify keyboard alternatives wherever possible. Make sure you can use your
program with only the keyboard. Try hiding your mouse! Note that if the focus is in
an editable text component, you can use Shift-Tab to move focus to the next
component.
Assign a textual description to all ImageIcon objects in your program. You can set
this property by using either the setDescription method or one of the String forms
of the ImageIcon constructors.
If a bunch of components form a logical group, try to put them into one container. For
example, use a JPanel to contain all the radio buttons in a radio button group.
Whenever you have a label that describes another component, use the setLabelFor
method so that assistive technologies can find the component that the label is
777
associated with. This is especially important when the label displays a mnemonic for
another component (such as a text field).
If you create a custom component, make sure it supports accessibility. In particular,
be aware that subclasses of JComponent are not automatically accessible. Custom
components that are descendants of other Swing components should override
inherited accessibility information as necessary. For more information, see Concepts:
How Accessibility Works and Making Custom Components Accessible.
Use the examples provided with the accessibility utilities to test your program.
Although the primary purpose of these examples is to show programmers how to use
the Accessibility API when implementing assistive technologies, these examples are
also quite useful for testing application programs for accessibility. Testing for
Accessibility shows ScrollDemo running with Monkey—one of the accessibility
utilities examples. Monkey shows the tree of accessible components in a program and
lets you interact with those components.
Finally, don't break what you get for free! If your GUI has an inaccessible container—
for example, your own subclass of Container or JComponent or any other container
that doesn't implement the Accessible interface—any components inside that
container become inaccessible.
The examples that come with the accessibility utilities can give you an idea of how accessible your
program is. For instructions on getting these utilities, see the Accessibility home page. Follow the
instructions in the accessibility utilities documentation for setting up the Java Virtual Machine (VM)
to run one or more of the utilities automatically.
Let's use an accessibility utility to compare the original version of one of our demos to a version in
which the rules for supporting accessibility have been applied. Here's a picture of a program called
ScrollDemo.
Try this:
778
1. Click the Launch button to run ScrollDemo using Java™ Web Start
(download JDK 6). Or, to compile and run the example yourself,
consult the example index.
3. Compare the two versions side by side. The only noticeable difference
is that the cm toggle button and the photograph have tool tips in the
accessible version.
4. Now run the two versions under the accessibility utility called
Monkey. Note that when the accessibility tools have been downloaded
and configured in the accessibility.properties file, the Monkey
window automatically comes up when you click on the Run
ScrollDemo and AccessibleScrollDemo links (in steps 1 and 2).
If the Monkey window does not appear on startup, the problem may be
that the accessibility.properties file is not present in the version
of the VM being used by Java Web Start. You can change the VM you
use by running the Java Web Start Application Manager and selecting
File > Preferences > Java.
5. Note that when the Monkey window comes up you need to select File
> Refresh Trees to see information appear under Accessible Tree.
You can then expand the tree by successively clicking on the
horizontal icons displayed by each folder icon. When the tree has been
expanded, you can see detailed information for the various
components. The custom components (rules and corners) that weren't
accessible in the original version are accessible in the modified
version. This can make quite a difference to assistive technologies.
779
The left side of the split pane shows the actual component hierarchy for the program. The right side
shows the accessible components in the hierarchy, which is what interests us.
The first thing to notice is that, even with no explicit support in ScrollDemo, Monkey is able to
discover a lot of information about the various components in the program. Most of the components
and their children appear in the tree. However, the names for most of the components are empty
(null), which is rather unhelpful. The descriptions are also empty.
Further trouble comes with the program's custom components. The two rulers are inaccessible, so
they are not included in the accessible tree. The viewports that contain the rulers are displayed as leaf
nodes because they have no accessible children. The custom corners are also missing from the
accessible tree.
780
The rules are now listed as children of the viewports, and the corners are listed as children of the
scroll pane. Furthermore, many of the components now have non-null names.
In the previous snapshot of Monkey, the Column Header item is selected. Monkey highlights the
corresponding component in ScrollDemo program.
When an item is selected, you can use Monkey's Panels menu to bring up one of four different
panels that let you interact with the selected component. Choosing Panels > Accessibility API panel
brings up a panel like the one shown in the following figure. This panel displays information
781
available through methods defined in the AccessibleContext base class and the
AccessibleComponent interface.
The accessibility utilities examples are handy as testing tools and can give you an idea of how
accessible the components in your program are. However, even if your components behave well in
Monkey or the other examples, they still might not be completely accessible because Monkey and
the other examples exercise only certain portions of the Accessibility API. The only true test of
accessibility is to run your programs with real-world assistive technologies.
Giving your program's components accessible names and descriptions is one of the easiest and most
important steps in making your program accessible. Following is a complete listing of the
AccessibleScrollDemo constructor that creates the scroll pane and the custom components it uses.
The boldface statements give components names and descriptions that assistive technologies can use.
public AccessibleScrollDemo() {
// Get the image to use.
ImageIcon david = createImageIcon("images/youngdad.jpeg",
"Photograph of David McNabb in his youth.");
782
columnView.getAccessibleContext().setAccessibleName("Column Header");
columnView.getAccessibleContext().
setAccessibleDescription("Displays horizontal ruler for " +
"measuring scroll pane client.");
rowView = new Rule(Rule.VERTICAL, true);
if (david != null) {
rowView.setPreferredHeight(david.getIconHeight());
} else {
rowView.setPreferredHeight(480);
}
rowView.getAccessibleContext().setAccessibleName("Row Header");
rowView.getAccessibleContext().
setAccessibleDescription("Displays vertical ruler for " +
"measuring scroll pane client.");
pictureScrollPane.setColumnHeaderView(columnView);
pictureScrollPane.setRowHeaderView(rowView);
783
upperRight);
An object is accessible if it implements the Accessible interface. The Accessible interface defines
just one method, getAccessibleContext, which returns an AccessibleContext object. The
AccessibleContext object is an intermediary that contains the accessible information for an
accessible object. The following figure shows how assistive technologies get the accessible context
from an accessible object and query it for information:
AccessibleContext is an abstract class that defines the minimum set of information an accessible
object must provide about itself. The minimum set includes name, description, role, state set, and so
on. To identify its accessible object as having particular capabilities, an accessible context can
implement one or more of the interfaces as shown in the Accessible Interfaces table. For example,
JButton implements AccessibleAction, AccessibleValue, AccessibleText, and
AccessibleExtendedComponent. It is not necessary for JButton to implement AccessibleIcon
because that is implemented by the ImageIcon attached to the button.
Because the JComponent class itself does not implement the Accessible interface, instances of its
direct subclasses are not accessible. If you write a custom component that inherits directly from
JComponent, you need to explicitly make it implement the Accessible interface. JComponent does
have an accessible context, called AccessibleJComponent, that implements the
AccessibleComponent interface and provides a minimal amount of accessible information. You can
provide an accessible context for your custom components by creating a subclass of
AccessibleJComponent and overriding important methods. Making Custom Components
Accessible shows two examples of doing this.
All the other standard Swing components implement the Accessible interface and have an
accessible context that implements one or more of the preceding interfaces as appropriate. The
accessible contexts for Swing components are implemented as inner classes and have names of this
style:
Component.AccessibleComponent
784
If you create a subclass of a standard Swing component and your subclass is substantially different
from its superclass, then you should provide a custom accessible context for it. The easiest way is to
create a subclass of the superclass's accessible context class and override methods as necessary. For
example, if you create a JLabel subclass substantially different from JLabel, then your JLabel
subclass should contain an inner class that extends AccessibleJLabel. The next section shows how
to do so, using examples in which JComponent subclasses extend AccessibleJComponent.
The scroll demo program uses three custom component classes. ScrollablePicture is a subclass of
JLabel, and Corner and Rule are both subclasses of JComponent.
The ScrollablePicture class relies completely on accessibility inherited from JLabel through
JLabel.AccessibleJLabel. The code that creates an instance of ScrollablePicture sets the tool-
tip text for the scrollable picture. The tool-tip text is used by the context as the component's
accessible description. This behavior is provided by AccessibleJLabel.
The accessible version of the Corner class contains just enough code to make its instances
accessible. We implemented accessibility support by adding the code shown in bold to the original
version of Corner.
Rule provides an accessible context for itself in the same manner as Corner, but the context
overrides two methods to provide details about the component's role and state:
785
if (orientation == VERTICAL) {
states.add(AccessibleState.VERTICAL);
} else {
states.add(AccessibleState.HORIZONTAL);
}
if (isMetric) {
states.add(AccessibleRulerState.CENTIMETERS);
} else {
states.add(AccessibleRulerState.INCHES);
}
return states;
}
}
AccessibleRole is an enumeration of objects that identify roles that Swing components can play. It
contains predefined roles such as label, button, and so on. The rulers in our example don't fit well
into any of the predefined roles, so the program invents a new one in a subclass of AccessibleRole:
class AccessibleRuleRole extends AccessibleRole {
public static final AccessibleRuleRole RULER
= new AccessibleRuleRole("ruler");
786
The tables in this section cover just part of the accessibility API. For more information about the
accessibility API, see the API documentation for the classes and packages in the accessibility
package. Also, refer to the API documentation for the accessible contexts for individual Swing
components.
The API for supporting accessibility falls into the following categories:
787
Accessible Interfaces
Interface Purpose
Indicates that the object can perform actions. By implementing this
interface, the accessible context can give information about what
AccessibleAction
actions the accessible object can perform and can tell the accessible
object to perform them.
Indicates that the accessible object has an onscreen presence.
Through this interface, an accessible object can provide information
about its size, position, visibility and so on. The accessible contexts
AccessibleComponent for all standard Swing components implement this interface,
directly or indirectly. The accessible contexts for your custom
components should do the same. As of 1.4,
AccessibleExtendedComponent is preferred.
Indicates that the accessible object displays editable text. In
AccessibleEditableText addition to the information available from its superinterface,
(Introduced in 1.4) AccessibleText, methods are provided for cutting, pasting,
deleting, selecting, and inserting text.
In addition to the information available from its superinterface,
AccessibleExtendedComponent
AccessibleComponent, methods are provided for obtaining key
(Introduced in 1.4)
bindings, border text, and tool-tip text.
In addition to the information available from its superinterface,
AccessibleExtendedTable
AccessibleTable, methods are provided to convert between an
(Introduced in 1.4)
index and its row or column.
Indicates that the accessible object contains hyperlinks. Through
AccessibleHypertext this interface, an accessible object can provide information about its
links and allow them to be traversed.
Indicates that the accessible object has an associated icon. Methods
AccessibleIcon
are provided that return information about the icon, such as size and
(Introduced in 1.3)
description.
Indicates that the accessible object supports one or more keyboard
AccessibleKeyBinding
shortcuts that can be used to select the object. Methods are provided
(Introduced in 1.4)
that return the key bindings for a given object.
Indicates that the accessible object can contain a selection.
Accessible contexts that implement this interface can report
AccessibleSelection
information about the current selection and can modify the
selection.
Indicates that the accessible object presents data in a two-
dimensional data object. Through this interface information about
AccessibleTable
the table such as table caption, row and column size, description,
(Introduced in 1.3)
and name are provided. As of 1.4, AccessibleExtendedTable is
preferred.
Indicates that the accessible object displays text. This interface
AccessibleText provides methods for returning all or part of the text, attributes
applied to it, and other information about the text such as its length.
Indicates that the object has a numeric value. Through this interface
AccessibleValue an accessible object provides information about its current value
and its minimum and maximum values.
788
Examples that Use the Accessibility API
The following table lists some of our examples that have good support for assistive technologies.
Example Where Described Notes
Contains two custom components that implement the
AccessibleScrollDemo This section Accessible interface. To see a less accessible version
of this program see How to Use Scroll Panes.
How to Use the
Uses three buttons. Supports accessibility through
ButtonDemo Common Button
button text, mnemonics, and tool tips.
API
From the user's point of view, the component with the keyboard focus is generally prominent — with
a dotted or black border, for example. The window containing the component is also more prominent
than other windows onscreen. These visual cues let the user know to which component any typing
will relate. Only one component at a time in the window system can have the keyboard focus.
Exactly how a window gains the focus depends on the windowing system. There is no foolproof
way, across all platforms, to ensure that a window gains the focus. On some operating systems, such
as Microsoft Windows, the front window usually becomes the focused window. In these cases, the
Window.toFront method moves the window to the front, thereby giving it the focus. However, on
other operating systems, such as Solaris™ Operating System, the window manager may choose the
focused window based on cursor position, and in these cases the behavior of the Window.toFront
method is different.
A component generally gains the focus when the user clicks it, or when the user tabs between
components, or otherwise interacts with a component. A component can also be given the focus
programmatically, such as when its containing frame or dialog-box is made visible. This code
snippet shows how to give a particular component the focus every time the window gains the focus:
If you want to ensure that a particular component gains the focus the first time a window is activated,
you can call the requestFocusInWindow method on the component after the component has been
realized, but before the frame is displayed. The following sample code shows how this operation can
be done:
789
//Create the component that will have the initial focus.
JButton button = new JButton("I am first");
panel.add(button);
frame.getContentPane().add(panel); //Add it to the panel
Alternatively, you can apply a custom FocusTraversalPolicy to the frame and call the
getDefaultComponent method to determine which component will gain the focus.
Version note: This section describes the focus architecture implemented in JDK 1.4. Prior
to the 1.4 release, JComponent methods, such as setNextFocusableComponent,
getNextFocusableComponent, requestDefaultFocus, and isManagingFocus, were used to
manage the keyboard focus. These methods are now deprecated. Another method,
requestFocus, is discouraged because it tries to give the focus to the component's window,
which is not always possible. As of JDK 1.4, you should instead use the
requestFocusInWindow method, which does not attempt to make the component's window
focused. The method returns a boolean value indicating whether the method succeeded.
The focus subsystem is designed to do the right thing as invisibly as possible. In most cases it
behaves in a reasonable manner, and if it does not you can tweak its behavior in various ways. Some
common scenarios might include:
The ordering is right but the first component with the focus is not set. As shown in a
code snippet in the preceding section, you can use the requestFocusInWindow
method to set the focus on a component when the window becomes visible.
The ordering is wrong. To fix this issue, you can change the containment hierarchy,
you can change the order that the components are added to their containers, or you
can create a custom focus traversal policy. For more details see Customizing Focus
Traversal.
A component must to be prevented from losing focus, or you need to check a value
in a component before it loses focus. Input verification is a solution to this problem.
790
A custom component is not getting the focus. To fix this issue, you need to make
sure that it satisfies all the requirements outlined in Making a Custom Component
Focusable.
Try this:
1. Click the Launch button to run FocusConceptsDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
The KeyboardFocusManager is a critical element of the focus subsystem. It manages state and
initiates changes. The keyboard manager tracks the focus owner — the component that receives
typing from the keyboard. The focused window is the window that contains the focus owner.
JWindow and focus: To use a JWindow component in your GUI, you should know that the
JWindow component's owning frame must be visible in order for any components in the
window to get the focus. By default, if you do not specify an owning frame for a JWindow
791
component, an invisible owning frame is created for it. The result is that components in the
JWindow component might not be able to get the focus. The solution is either to specify a
visible owning frame when creating the JWindow component, or to use an undecorated
JFrame component instead.
A focus cycle (or focus traversal cycle) is a set of components that share a common ancestor in the
containment hierarchy. The focus cycle root is the container that is the root for a particular focus
traversal cycle. By default, every JWindow and JInternalFrame component can be a focus cycle
root. A focus cycle root can itself contain one or more focus cycle roots. The following Swing
objects can be focus cycle roots: JApplet, JDesktopPane, JDialog, JEditorPane, JFrame,
JInternalFrame, and JWindow. While it might appear that JTable and JTree objects are focus
cycle roots, they are not.
A focus traversal policy determines the order in which a group of components are navigated. Swing
provides the LayoutFocusTraversalPolicy class, which decides the order of navigation based on
layout manager-dependent factors, such as size, location, and orientation of components. Within a
focus cycle, components can be navigated in a forward or backward direction. In a hierarchy of focus
cycle roots, upwards traversal takes the focus out of the current cycle into the parent cycle.
In most Look and Feel models, components are navigated using the Tab and Shift-Tab keys. These
keys are the default focus traversal keys and can be changed programmatically. For example, you
can add Enter as a forward focus traversal key with the following four lines of code:
You have just received a brief introduction to the focus architecture. If you want more details, see the
specification for the Focus Subsystem.
Validating Input
A common requirement of GUI design is a component that restricts the user's input — for example, a
text field that allows only numeric input in decimal format (money, for example) or a text field that
allows only 5 digits for a zip code. Release 1.4 provides a handy, easy-to-use formatted text field
component that allows input to be restricted to a variety of localizable formats. You can also specify
a custom formatter for the text field, which can perform special checking such as determining
whether values are not only formatted correctly, but also reasonable.
You can use an input verifier as an alternative to a custom formatter, or when you have a component
that is not a text field. An input verifier allows you to reject specific values, such as a properly
792
formatted but invalid zip code, or values outside of a desired range, for example a body temperature
higher than 110°F. To use an input verifier, you create a subclass of InputVerifier (a class
introduced in JDK 1.3), create an instance of your subclass, and set the instance as the input verifier
for one or more components.
A component's input verifier is consulted whenever the component is about to lose the focus. If the
component's value is not acceptable, the input verifier can take appropriate action, such as refusing to
yield the focus on the component or replacing the user's input with the last valid value and then
allowing the focus to transfer to the next component. However, InputVerifier is not called when
the focus is transferred to another toplevel component.
The following two examples show mortgage calculators. One uses formatted text fields and the other
uses input verification with standard text fields.
Try this:
1. Click the Launch button to run the FormattedTextFieldDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
2. Click the Launch button to run the InputVerificationDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
3. Compare the two mortgage calculators side by side. You will see that the input
verification demo specifies valid input values in the associated label for each editable
text field. Try entering badly formatted values in both examples to observe behavior.
Then try entering a properly formatted, but unacceptable value.
You can find the code for the Input Verification demo in InputVerificationDemo.java. Here is
the code for the InputVerifier subclass, MyVerifier:
793
double MIN_RATE = 0.0;
int MIN_PERIOD = 1;
int MAX_PERIOD = 40;
if (inputOK) {
return true;
} else {
Toolkit.getDefaultToolkit().beep();
return false;
}
}
794
//it returns true; otherwise, returns false. If the
//change argument is true, this method sets the
//value to the minimum or maximum value if necessary and (even if not) sets
it to the
//parsed number so that it looks good -- no letters,
//for example.
protected boolean checkAmountField(boolean change) {
boolean wasValid = true;
double amount = DEFAULT_AMOUNT;
return wasValid;
}
795
}
}
Note that the verify method is implemented to detect invalid values but does nothing else. The
verify method exists only to determine whether the input is valid — it should never bring up a
dialog-box or cause any other side effects. The shouldYieldFocus method calls verify and, if a
values is invalid, sets it to the minimum or maximum value. The shouldYieldFocus method is
allowed to cause side effects, in this case, it always formats the text field and may also change its
value. In our example, the shouldYieldFocus method always returns true so that the transfer of the
focus is never actually prevented. This is just one way verification can be implemented. Find another
version of this demo called InputVerificationDialogDemo that puts up a dialog-box when user
input is invalid and requires the user to enter a legal value.
The input verifier is installed using the setInputVerifier method of the JComponent class. For
example, the InputVerificationDemo has the following code:
For a component to gain the focus, it must satisfy three requirements: it must be visible, enabled, and
focusable. An input map may also be given. For more information about input map, read How to Use
Key Bindings.
The TrackFocusDemo example defines the simple component Picture. Its constructor is shown
below:
The call to the setFocusable(true) method makes the component focusable. If you explicitly give
your component key bindings in its WHEN_FOCUSED input map, you do not need to call the
setFocusable method.
To visually show changes in the focus (by drawing a red border only when the component has the
focus), Picture has a focus listener.
To gain the focus when the user clicks on the picture, the component has a mouse listener. The
listener's mouseClicked method requests for the focus to be transferred to the picture. Here is the
code:
See Tracking Focus Changes to Multiple Components for more discussion of the TrackFocusDemo
example.
796
Customizing Focus Traversal
The focus subsystem determines a default order that is applied when using the focus traversal keys
(such as Tab) to navigate. The policy of a Swing application is determined by
LayoutFocusTraversalPolicy . You can set a focus traversal policy on any Container by using
the setFocusCycleRoot method. However, if the container is not a focus cycle root, it may have no
apparent effect. Alternatively you can pass focus traversal policy providers to the
FocusTraversalPolicy methods instead of focus cycle roots. Use the
isFocusTraversalPolicyProvider() method to determine whether a Container is a focus
traversal policy provider. Use the setFocusTraversalPolicyProvider() method to set a container
for providing focus traversal policy.
Try this:
1. Click the Launch button to run FocusTraversalDemo using Java™ Web Start
(download JDK 6). Alternatively, to compile and run the example yourself, consult
the example index.
The check box was removed from the focus cycle with this line of code:
797
togglePolicy.setFocusable(false);
Here is the application's custom FocusTraversalPolicy:
...
JTextField tf1 = new JTextField("Field 1");
JTextField tf2 = new JTextField("A Bigger Field 2");
JTextField tf3 = new JTextField("Field 3");
JTextField tf4 = new JTextField("A Bigger Field 4");
JTextField tf5 = new JTextField("Field 5");
JTextField tf6 = new JTextField("A Bigger Field 6");
JTable table = new JTable(4,3);
...
public FocusTraversalDemo() {
super(new BorderLayout());
Control-Tab
leftTextPanel.add(tf1, BorderLayout.PAGE_START);
leftTextPanel.add(tf3, BorderLayout.CENTER);
leftTextPanel.add(tf5, BorderLayout.PAGE_END);
leftTextPanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
rightTextPanel.add(tf2, BorderLayout.PAGE_START);
rightTextPanel.add(tf4, BorderLayout.CENTER);
rightTextPanel.add(tf6, BorderLayout.PAGE_END);
rightTextPanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
tablePanel.add(table, BorderLayout.CENTER);
798
tablePanel.setBorder(BorderFactory.createEtchedBorder());
bottomPanel.add(togglePolicy, BorderLayout.PAGE_START);
bottomPanel.add(label, BorderLayout.PAGE_END);
add(leftTextPanel, BorderLayout.LINE_START);
add(rightTextPanel, BorderLayout.CENTER);
add(tablePanel, BorderLayout.LINE_END);
add(bottomPanel, BorderLayout.PAGE_END);
setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
order.add(tf1);
order.add(tf2);
order.add(tf3);
order.add(tf4);
order.add(tf5);
order.add(tf6);
order.add(table);
To use a custom FocusTraversalPolicy, implement the following code on any focus cycle root.
You can remove the custom focus traversal policy by setting the FocusTraversalPolicy to null,
which will restore the default policy.
In some situations an application may need to track which component has the focus. This
information might be used to dynamically update menus or perhaps a status bar. If you need to track
the focus only on specific components, it may make sense to implement a focus event listener.
799
If a focus listener is not appropriate, you can instead register a PropertyChangeListener on the
KeyboardFocusManager. The property change listener is notified of every change involving the
focus, including changes to the focus owner, the focused window, and the default focus traversal
policy. See the KeyboardFocusManager Properties table for a complete list.
The following example demonstrates tracking the focus owner by installing a property change
listener on the keyboard focus manager.
Try this:
1. Click the Launch button to run TrackFocusDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.
You can view the demo's code in TrackFocusDemo.java. The custom component used for drawing
the images can be found in Picture.java. Here is the code that defines and installs the property
change listener:
800
KeyboardFocusManager focusManager =
KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (("focusOwner".equals(prop)) &&
((e.getNewValue()) instanceof Picture)) {
Component comp = (Component)e.getNewValue();
String name = comp.getName();
Integer num = new Integer(name);
int index = num.intValue();
if (index < 0 || index > comments.length) {
index = 0;
}
info.setText(comments[index]);
}
}
}
);
The custom component, Picture, is responsible for drawing the image. All six components are
defined in this manner:
Focus transfers are asynchronous. This quality can lead to some odd timing-related problems and
assumptions, especially during automatic transfers of the focus. For example, imagine an application
with a window containing a Start button, a Cancel button and a text field. The components are added
in this order:
1. Start button
2. Text field
3. Cancel button
As desired, the focus goes from the Start button to the Cancel button, rather than to the text field. But
a different result would occur if the same methods were called in the opposite order as follows:
801
//This does not work.
cancel.requestFocusInWindow();
start.setEnabled(false);
}
In this case, the focus is requested on the Cancel button before it has left the Start button.
The call to the requestFocusInWindow method initiates the focus transfer, but it does not
immediately move the focus to the Cancel button. When the Start button is disabled, the
focus is transferred to the next component (so there is always a component with the focus)
and, in this case, it would then move the focus to the text field, not to the Cancel button.
There are several situations in which you need to make focus requests after all other changes that
might affect the focus applies to:
The following tables list the commonly used constructors and methods related to focus. The
focus API falls into four categories:
For more detailed information about the focus architecture, see the specification for the Focus
Subsystem. You may also find How to Write a Focus Listener useful.
802
method is recommended over the setFocusable
method and will allow your program to work better for
users employing assistive technologies.
Sets or gets the focusable state of the component. A
component must be focusable in order to gain the
focus. When a component has been removed from the
setFocusable(boolean) focus cycle with setFocusable(false), it can no longer
isFocusable() be navigated with the keyboard. The
setRequestFocusEnabled method is recommended so
that your program can be run by users employing
assistive technologies.
Requests that this component should get the focus.
The component's window must be the current focused
window. For this request to be granted a subclass of
JComponent must be visible, enabled, and focusable,
requestFocusInWindow() and have an input map for this request to be granted. It
should not be assumed that the component has the
focus until it fires a FOCUS_GAINED event. This method is
preferred to the requestFocus method, which is
platform-dependent.
Sets or gets the focus traversal keys for a particular
direction or determines whether any focus traversal
keys have been explicitly set on this container. If no
focus traversal keys have been set, they are inherited
from an ancestor or from the keyboard focus manager.
Focus traversal keys can be set for the following
setFocusTraversalKeys(int, Set)
directions:
getFocusTraversalKeys(int)
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
areFocusTraversalKeysSet(int) KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS
(in java.awt.Container) KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS, or
KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS. If
you set the UP_CYCLE_TRAVERSAL_KEYS or the
DOWN_CYCLE_TRAVERSAL_KEYS, you must also invoke
setImplicitDownCycleTraversal(false) on the focus
traversal policy.
803
getDefaultComponent(Container)
Returns the component that should have
(in
the default focus.
javax.swing.SortingFocusTraversalPolicy)
Returns the first component in the
getFirstComponent(Container)
traversal cycle.
Returns the component that should
getInitialComponent(Container) receive the focus when a window is made
visible for the first time.
Returns the last component in the traversal
getLastComponent(Container)
cycle.
Sets or gets the focus traversal policy or
determines if a policy has been set. Note
that setting a focus traversal policy on a
setFocusTraversalPolicy(FocusTraversalPolicy) container that is not the focus cycle root
getFocusTraversalPolicy(FocusTraversalPolicy) may have no apparent effect. A value of
(in java.awt.Container) null means that a policy has not been
explicitly set. If no policy has been set, a
policy is inherited from the parent focus
cycle root.
isFocusCycleRoot()
Checks or sets whether a container is the
setFocusCycleRoot(boolean)
root of a focus traversal cycle.
(in java.awt.Container)
isFocusTraversalPolicyProvider()
Checks or sets whether a container will be
setFocusTraversalPolicyProvider(boolean)
used to provide focus traversal policy.
(in java.awt.Container)
804
by shouldYieldFocus.
setInputVerifier(inputVerifier) Sets or gets the input verifier assigned to the
getInputVerifier() component. By default, components have no
(in JComponent) input verifier.
Sets or gets whether the input verifier for the
current focus owner is called before this
setVerifyInputWhenFocusTarget(boolean) component requests the focus. The default is
getVerifyInputWhenFocusTarget() true. This method should be set to false for
(in JComponent) components, such as a Cancel button or a scroll
bar, that should receive the focus even if input is
invalid.
KeyboardFocusManager Properties
This table defines the bound properties for KeyboardFocusManager. A listener can be registered for
these properties by calling addPropertyChangeListener.
Property Purpose
focusOwner The component that currently receives key events.
The component that most recently received a
permanent FOCUS_GAINED event. Typically the same
permanentFocusOwner
as focusOwner, unless a temporary focus change is
currently in effect.
focusedWindow The window that is or that contains the focus owner.
The component must always be either a Frame or a
Dialog. The active window is either the focused
activeWindow
window, or the first frame or dialog-box that is an
owner of the focused window.
The default focus traversal policy, which can be set
defaultFocusTraversalPolicy by the setFocusTraversalPolicy method of the
Container class.
The set of default focus keys for a forward traversal.
For multi-line text components, these keys default
forwardDefaultFocusTraversalKeys
to Control-Tab. For all other components, these
keys default to Tab and Control-Tab.
The set of default focus keys for a backward
traversal. For multi-line text components these keys
backwardDefaultFocusTraversalKeys default to Control-Shift-Tab. For all other
components these keys default to Shift-Tab and
Control-Shift-Tab.
The set of default focus keys for an up cycle. These
keys are null, by default, for Swing components. If
upCycleDefaultFocusTraversalKeys you set these keys on the KeyboardFocusManager, or
if you set the downCycleFocusTraversalKeys on a
focus cycle root, you must also invoke the
805
setImplicitDownCycleTraversal(false) method on
the focus traversal policy.
The set of default focus keys for a down cycle.
These keys are null, by default, for Swing
components. If you set these keys on the
KeyboardFocusManager, or if you set the
downCycleDefaultFocusTraversalKeys
upCycleFocusTraversalKeys on a focus cycle root,
you must also invoke the
setImplicitDownCycleTraversal(false) method on
the focus traversal policy.
currentFocusCycleRoot The container that is the current focus cycle root.
You're creating a custom component and want to support keyboard access to it.
For example, you might want the component to react when it has the focus and the
user presses the Space key.
You want to override the behavior of an existing key binding.
For example, if your application normally reacts to presses of the F2 key in a
particular way, you might want it to perform a different action or ignore the key press.
806
You want to provide a new key binding for an existing action.
For example, you might feel strongly that Control-Shift-Insert should perform a paste
operation.
You often don't need to use key bindings directly. They're used behind the scenes by mnemonics
(supported by all buttons and by tabbed panes as well as by JLabel) and accelerators (supported by
menu items). You can find coverage of mnemonics and accelerators in the section Enabling
Keyboard Operation.
An alternative to key bindings is using key listeners. Key listeners have their place as a low-level
interface to keyboard input, but for responding to individual keys key bindings are more appropriate
and tend to result in more easily maintained code. Key listeners are also difficult if the key binding is
to be active when the component doesn't have focus. Some of the advantages of key bindings are
they're somewhat self documenting, take the containment hierarchy into account, encourage reusable
chunks of code (Action objects), and allow actions to be easily removed, customized, or shared.
Also, they make it easy to change the key to which an action is bound. Another advantage of Actions
is that they have an enabled state which provides an easy way to disable the action without having to
track which component it is attached to.
The rest of this section gives you the details you need to use key bindings:
The key binding support provided by JComponent relies on the InputMap and ActionMap classes,
which were introduced in 1.3. An input map binds key strokes to action names, and an action map
specifies the action corresponding to each action name. Technically, you don't need to use action
names in the maps; you can use any object as the "key" into the maps. By convention, however, you
use a string that names an action.
Each InputMap/ActionMap has a parent that typically comes from the UI. Any time the look and feel
is changed, the parent is reset. In this way, any bindings specified by the developer are never lost on
look and feel changes.
Version Note: Prior to 1.3, the JComponent method registerKeyboardAction was used instead of
input and action maps. registerKeyboardAction is now obsolete. (To ensure compatibility for
older programs, registerKeyboardAction was reimplemented to use InputMap and ActionMap.)
Each JComponent has one action map and three input maps. The input maps correspond to the
following focus situations:
JComponent.WHEN_FOCUSED
The component has the keyboard focus. The WHEN_FOCUSED input map is typically
used when the component has no children. For example, buttons bind the Space key
using the WHEN_FOCUSED map.
These bindings are only effective when the component has focus.
807
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
The component contains (or is) the component that has the focus. This input map is
commonly used for a composite component — a component whose implementation
depends on child components. For example, JTables make all their bindings using
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT so that if the user is editing, the up-arrow
key (for example) still changes the selected cell.
JComponent.WHEN_IN_FOCUSED_WINDOW
The component's window either has the focus or contains the component that has the
focus. This input map is commonly used for mnemonics or accelerators, which need
to be active regardless of where focus is in the window.
When the user types a key, the JComponent key event processing code searches through one or more
input maps to find a valid binding for the key. When it finds a binding, it looks up the corresponding
action in the action map. If the action is enabled, the binding is valid and the action is executed. If it's
disabled, the search for a valid binding continues.
If more than one binding exists for the key, only the first valid one found is used. Input maps are
checked in this order:
Let's consider what happens in two typical key binding cases: a button reacting to the Space key, and
a frame with a default button reacting to the Enter key.
In the first case, assume the user presses the Space key while a JButton has the keyboard focus.
First, the button's key listeners are notified of the event. Assuming none of the key listeners
consumes the event (by invoking the consume method on the KeyEvent) the button's WHEN_FOCUSED
input map is consulted. A binding is found because JButton uses that input map to bind Space to an
action name. The action name is looked up in the button's action map, and the actionPerformed
method of the action is invoked. The KeyEvent is consumed, and processing stops.
In the second case, assume the Enter key is pressed while the focus is anywhere inside a frame that
has a default button (set using the JRootPane setDefaultButton method). Whatever the focused
component is, its key listeners are first notified. Assuming none of them consumes the key event the
focused component's WHEN_FOCUSED input map is consulted. If it has no binding for the key or the
Action bound to the key is disabled, the focused component's
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT input map is consulted and then (if no binding is found or
the Action bound to the key is disabled) the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT input maps of
each of the component's ancestors in the containment hierarchy. Eventually, the root pane's
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT input map is searched. Since that input map has a valid
binding for Enter, the action is executed, causing the default button to be clicked.
808
component.getInputMap().put(KeyStroke.getKeyStroke("F2"),
"doSomething");
component.getActionMap().put("doSomething",
anAction);
//where anAction is a javax.swing.Action
As the preceding code shows, to get a component's action map you use the getActionMap method
(inherited from JComponent). To get an input map, you can use the getInputMap(int) method,
where the integer is one of the JComponent.WHEN_*FOCUSED* constants shown in the preceding list.
Or, in the usual case where the constant is JComponent.WHEN_FOCUSED, you can just use
getInputMap with no arguments.
To add an entry to one of the maps, use the put method. You specify a key using a KeyStroke
object, which you can get using the KeyStroke.getKeyStroke(String) method. You can find
examples of creating an Action (to put in an action map) in How to Use Actions.
Here's a slightly more complex example that specifies that a component should react to the Space
key as if the user clicked the mouse.
component.getInputMap().put(KeyStroke.getKeyStroke("SPACE"),
"pressed");
component.getInputMap().put(KeyStroke.getKeyStroke("released SPACE"),
"released");
component.getActionMap().put("pressed",
pressedAction);
component.getActionMap().put("released",
releasedAction);
//where pressedAction and releasedAction are javax.swing.Action objects
To make a component ignore a key that it normally responds to, you can use the special action name
"none". For example, the following code makes a component ignore the F2 key.
component.getInputMap().put(KeyStroke.getKeyStroke("F2"),
"none");
The following tables list the commonly used API for key bindings. Also see the API table Creating
and Using an Action, in the section How to Use Actions.
809
Getting and Using InputMaps
Method Purpose
InputMap
Get one of the input maps for the component. The arguments can be one of
getInputMap()
these JComponent constants: WHEN_FOCUSED, WHEN_IN_FOCUSED_WINDOW,
InputMap
or WHEN_ANCESTOR_OF_FOCUSED_COMPONENT. The no-argument method
getInputMap(int)
gets the WHEN_FOCUSED input map.
(in JComponent)
void put(KeyStroke, Set the action name associated with the specified key stroke. If the second
Object) argument is null, this method removes the binding for the key stroke. To
(in InputMap) make the key stroke be ignored, use "none" as the second argument.
Get the object specifying a particular user keyboard activity. Typical
static KeyStroke
arguments are "alt shift X", "INSERT", and "typed a". See the KeyStroke
getKeyStroke(String)
API documentation for full details and for other forms of the
(in KeyStroke)
getKeyStroke method.
Java™ SE 6 has resolved modality issues that arose in earlier versions of the platform. The new
modality model enables the developer to scope, or limit, a dialog box's modality blocking.
Before proceding with the new modality model, review the following terms:
810
Dialog box — A top-level pop-up window with a title and a border that typically takes some
form of input from the user. A dialog box can be modal or modeless. For more information
about dialog boxes, see An Overview of Dialogs in the How to Make Dialogs page.
Modal dialog box — A dialog box that blocks input to some other top-level windows in the
application, except for windows created with the dialog box as their owner. The modal dialog
box captures the window focus until it is closed, usually in response to a button press.
Modeless dialog box — A dialog box that enables you to operate with other windows while
this dialog box is shown.
In Java SE 6 the behavior of both modal and modeless dialog boxes has been changed so that they
always appear on top of both not only of their parent windows and of all blocked windows as well.
Modeless type — A modeless dialog box does not block any other window while it is
visible.
Document-modal type — A document-modal dialog box blocks all windows from the same
document, except windows from its child hierarchy. In this context, a document is a hierarchy
of windows that share a common ancestor, called the document root, which is the closest
ancestor window without an owner.
Application-modal type — An application-modal dialog box blocks all windows from the
same application, except windows from its child hierarchy. If several applets are launched in
a browser environment, the browser is allowed to treat them either as separate applications or
as a single application. This behavior is implementation-dependent.
Toolkit-modal type — A toolkit-modal dialog box blocks all windows that run in the same
toolkit, except windows from its child hierarchy. If several applets are launched, all of them
run with the same toolkit. Hence, a toolkit-modal dialog box shown from an applet may
affect other applets and all windows of the browser instance that embeds the Java runtime
environment for this toolkit.
Exclusion mode — Any top-level window can be marked not to be blocked by modal
dialogs. This property enables you to set up the modal exclusion mode. The
Dialog.ModalExclusionType enum specifies the possible modal exclusion types.
Note : The new modality model does not implement a system modality, which blocks all
applications (including Java applications) that are displayed on the desktop while a modal dialog box
is active.
The ModalityDemo example demonstrates the first three of the four modality types mentioned
above.
811
This figure has been reduced to fit on the page.
Click the image to view it at its natural size.
Try this:
1. Click the Launch button to run ModalityDemo using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.
Note : The modeless dialog box title has been changed to Funny Tale.
812
6. Select OK in the modeless dialog box.
7. The Funny tale document-modal dialog box appears.
8. Type some text in the text field. Notice that it is signed by the name you entered in the
modeless dialog box.
9. Switch to the modeless dialog box and try to change your name. You will not be able
to do so, because the document-modal dialog box blocks all windows in its parent
hierarchy.
10. Perform the same sequence of operations (steps 3 - 9) for the Book 2 parent frame.
11. Try switching to different dialog boxes. You will notice that you can switch either to
the Classics frame or to the Feedback frame as well as to the dialog box of either the
Book 1 frame or the Book 2 frame.
12. Switch to the Feedback parent frame. Select Rate Yourself.
13. The confirmation dialog box will appear. Try switching to different dialog boxes. You
are only enabled to switch to the Classics dialog box because the standard
confirmation dialog box is an application-modal dialog box and it blocks all windows
from the same application. However, you will notice that you can select your favorite
classical author in the Classics frame. This frame has been created by using the
APPLICATION_EXCLUDE modality exclusion type, which prevents all top-level
windows from being blocked by any application-modal dialog boxes.
The following code snippet shows how to create dialog boxes of different modality types:
...
...
...
...
...
...
813
...
In Java SE 6 you can create a document-modal dialog box without a parent. Because the Dialog
class is a subclass of the Window class, a Dialog instance automatically becomes the root of the
document if it has no owner. Thus, if such a dialog box is document-modal, its scope of blocking is
empty, and it behaves as if it were a modeless dialog box.
The JDialog class constructors enable you to create dialog boxes of various modality types.
Constructor Purpose
Creates a modeless dialog box with the specified Dialog
JDialog(Dialog owner)
owner but without a title.
JDialog(Dialog owner, boolean Creates a dialog box with the specified Dialog owner and
modal) modality.
Creates a modeless dialog box with the specified Dialog
JDialog(Dialog owner, String title)
owner and title.
JDialog(Dialog owner, String title, Creates a dialog box with the specified Dialog owner, title,
boolean modal) and modality.
JDialog(Dialog owner, String title,
Creates a dialog box with the specified Dialog owner, title,
boolean modal, GraphicsConfiguration
modality, and graphics configuration.
gc)
Creates a modeless dialog box without a title with the
specified Frame owner. If the value for the owner is null, a
JDialog(Frame owner)
shared, hidden frame will be set as the owner of the dialog
box.
JDialog(Window owner, String title, Creates a dialog box with the specified Window owner, title,
Dialog.ModalityType modalityType) and modality.
The following table lists methods inherited from the java.awt.Dialog class.
Method Purpose
getModalityType Returns the modality type for this dialog box.
814
Sets the modality type for this dialog box. See ModalityType for possible modality
types. If the given modality type is not supported, then the MODELESS type is used.
setModalityType
To ensure that the modality type has been set, call the getModalityType()
method after calling this method.
The following table lists the example that uses modality in dialogs.
Where
Example Notes
Described
ModalityDemo This section Creates dialog boxes of different modality types, demonstrates scope
blocking for those types.
The JTable class provides support for printing tables. The JTable printing API includes
methods that allow you to implement both basic and advanced printing tasks. For common
printing tasks, when you need to simply print a table, use the print method directly. The
print method has several forms with various argument sets. This method prepares your
table, gets a corresponding Printable object, and sends it to a printer.
If the default implementation of the Printable object does not meet your needs, you can customize
the printing layout by overriding the getPrintable method to wrap the default Printable or even
replace it altogether.
The easiest way to print your table is to call the print method without parameters. See the code
example below.
try {
boolean complete = table.print();
if (complete) {
/* show a success message */
...
} else {
/*show a message indicating that printing was cancelled */
...
}
} catch (PrinterException pe) {
/* Printing failed, report to the user */
...
}
When you call the print method with no parameters, a print dialog is displayed, and then
your table is printed interactively in the FIT_WIDTH mode without a header or a footer. The
code example below shows the print method signature with the complete set of
arguments.
boolean complete = table.print(JTable.PrintMode printMode,
MessageFormat headerFormat,
MessageFormat footerFormat,
boolean showPrintDialog,
PrintRequestAttributeSet attr,
boolean interactive,
815
PrintService service);
When you call the print method with all arguments, you explicitly choose printing features
such as a printing mode, a header and a footer text, printing attributes, a destination print
service, and also whether to show a print dialog or not, and whether to print interactively or
non-interactively. To decide which parameters suit your needs best, see the description of
available features below.
In interactive mode a progress dialog with an abort option is shown for the duration of
printing. Here is a sample of a progress dialog.
This dialog enables the user to keep track of printing progress. The progress dialog is
modal, which means that while it is shown on the screen, the user cannot interact with the
table. It is important that your table remain unchanged while it is being printed, otherwise
the printing behavior will be undefined. Nevertheless, printing interactively does not block
other developer's code from changing the table. For example, there is another thread that
posts updates using the SwingUtilities.invokeLater method. Therefore, to ensure correct
printing behavior, you should be sure that your own code refrains from modifying the table
during printing.
Alternatively, you can print your table non-interactively. In this mode, printing begins immediately
on the event dispatch thread and completely blocks any events to be processed. On the one hand, this
mode securely keeps the table against any changes until printing is done. On the other hand, this
mode completely deprives the user of any interaction with the GUI. That is why printing non-
interactively can only be recommended when printing from applications with non-visible GUI.
Print Dialog
You can display a standard print dialog which allows the user to do the following:
Select a printer
Specify number of copies
Change printing attributes
Cancel printing before it has been started
Start printing
816
You may notice that the print dialog does not specify the total number of pages in the
printout. This is because the table printing implementation uses the Printable API and the
total number of pages is not known ahead of printing time.
Headers and footers are provided by MessageFormat parameters. These parameters allow
the header and footer to be localized. Read the documentation for the MessageFormat class,
as some characters, such as single quotes, are special and need to be avoided. Both
headers and footers are centered. You can insert a page number by using {0}.
Since the total number of pages in the output is not known before printing time, there is no way to
specify a numbering format like "Page 1 of 5".
Printing Modes
Printing modes are responsible for scaling the output and spreading it across pages. You
can print your table in one of the following modes:
PrintMode.NORMAL
PrintMode.FIT_WIDTH
In the NORMAL mode a table is printed at its current size. If columns do not fit a page, they spread
across additional pages according to the table's ComponentOrientation. In the FIT_WIDTH mode a
table has a smaller size, if necessary, to fit all columns on each page. Note that both width and height
are scaled to provide an output of the same aspect ratio. In both modes rows spread across multiple
pages sequentially with as many rows on a page as possible.
817
Automatic Layout and Pagination
With the use of the JTable printing API you do not need to take care of layout and
pagination. You only need to specify appropriate parameters to the print method such as
printing mode and footer text format (if you want to insert the page number in the footer). As
demonstrated earlier, you can specify the page number in your footer by including "{0}" in
the string given to the MessageFormat footer parameter. In the printed output, {0} will be
replaced by the current page number.
Let us look at an example called TablePrintDemo1. The entire code for this program can be
found in TablePrintDemo1.java. This demo's rich GUI is built automatically by the NetBeans
IDE GUI builder. Here is a picture of the TablePrintDemo1 application.
Try this:
818
1. Click the Launch button to run TablePrintDemo1 using Java™ Web Start (download
JDK 6). Alternatively, to compile and run the example yourself, consult the example
index.
2. Each checkbox in the bottom part of the application window has a tool tip. Hold the
cursor over a checkbox to find out its purpose.
3. Edit the text in the Header or Footer checkboxes or both to provide a different
header or footer.
4. Clear the Header or Footer checkboxes or both to turn the header or footer off.
5. Clear the Show print dialog checkbox to turn the print dialog off.
6. Clear the Fit width to printed page checkbox to select printing in the NORMAL mode.
7. Clear the Interactive (Show status dialog) checkbox to turn the print dialog off.
8. Click the Print button to print the table according to the selected options.
Whenever a web-launched application tries to print, Java Web Start pops up a security
dialog asking the user for permission to print. To proceed with printing, the user has to
accept the request.
Note when you clear the Interactive checkbox, a message appears that warns the user about the
disadvantage of printing non-interactively. You can find the printing code in the PrintGradesTable
method. When called, this method first obtains the set of selected options from the GUI components
and then calls the print method as follows.
Another important feature is the table printing API's use of table renderers. By using the table's
renderers, the API provides a printed output that looks like the table on the screen. Look at the last
column of the table on the screen. It contains custom images denoting the passed or failed status of
each student. Now look at the printed result. You can see that the check and X marks look the same.
819
This figure has been reduced to fit on the page.
Click the image to view it at its natural size.
TablePrintDemo2 Example
The TablePrintDemo2 example is based on the previous demo and has an identical
interface. The only difference is in the printed output. If you look at the TablePrintDemo1's
printed result more attentively, you may notice that the check and X marks are fuzzy. The
TablePrintDemo2 example shows how to customize the table to make the images more
distinguishable in the table printout. In this demo, the overridden
getTableCellRendererComponent method finds out whether the table is being printed and
returns clearer black and white images. If the table is not being printed, it returns colored
images that you can see on the screen.
Click the Launch button to run TablePrintDemo2 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
820
*/
protected static class BWPassedColumnRenderer extends PassedColumnRenderer {
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
return this;
}
}
821
TablePrintDemo3 Example
The TablePrintDemo3 example is based on the two previous demos. This example shows
how to provide a customized Printable implementation by wrapping the default Printable
with extra decoration. This demo has a similar interface but the Header and Footer
checkboxes are disabled since the customized printable object will provide its own header
and footer.
Click the Launch button to run TablePrintDemo3 using Java™ Web Start (download JDK 6).
Alternatively, to compile and run the example yourself, consult the example index.
This example prints the table inside an image of a clipboard. Here is a picture of the printed
result in the FIT_WIDTH mode.
822
/* Fetch the default printable */
Printable delegate = super.getPrintable(printMode, null, pageNumber);
Pay attention to the flexibility of the code that assembles the clipboard image with respect to the
page size. The code takes into account the actual page dimensions and puts together the auxiliary
images, stretching some of them as necessary so that the final clipboard image fits the actual page
size. The picture below shows the auxiliary images and indicates how those images form the final
output.
This section lists methods defined in the JTable class that allow you to print tables.
823
Method Purpose
When called without arguments, displays a print
dialog, and then prints this table interactively in the
boolean print() FIT_WIDTH mode without a header or a footer text.
boolean print(printMode) Returns true if the user continued printing and false
boolean print(printMode, if the user cancelled printing.
MessageFormat, MessageFormat) When called with a full set of arguments, prints this
boolean print(printMode, table according to the specified arguments. The first
MessageFormat, MessageFormat, argument specifies the printing mode. Two
boolean, PrintRequestAttributeSet, MessageFormat arguments specify header and footer
boolean) text. The first boolean argument defines whether to
boolean print(printMode, show a print dialog or not. Another boolean
MessageFormat, MessageFormat, argument specifies whether to print interactively or
boolean, PrintRequestAttributeSet, not. With two other arguments you can specify
boolean, PrintService) printing attributes and a print service.
Whenever a PrintService argument is omitted, the
default printer will be used.
Returns a Printable for printing a table. Override
Printable getPrintable(PrintMode, this method to get a customized Printable object.
MessageFormat, MessageFormat) You can wrap one Printable object into another to
get various layouts.
This table lists examples that use table printing and points to where those examples are
described.
Where
Example Notes
Described
TablePrintDemo How to Use Demonstrates basic features in table printing such as
Tables displaying a print dialogue, and then printing interactively in
the FIT_WIDTH mode with a page number as a header.
TablePrintDemo1 This page Demostrates the basics of table printing and provides a rich
GUI. Allows the user to specify a header or a footer text,
select the printing mode, turn the print dialog on or off, and
select printing interactively or non-interactively.
TablePrintDemo2 This page Based on the TablePrintDemo1, this example has an
identical interface. This demo shows how to customize the
table so that the printed result looks differently compared to
the table being shown on the screen.
TablePrintDemo3 This page This demo shows advanced table printing features such as
wrapping the default table printable into another printable to
get a different layout.
824
argument sets. This method prepares your text document, gets a corresponding Printable object,
and sends it to a printer.
If the default implementation of the Printable object does not meet your needs, you can customize
the printing layout by overriding the getPrintable method to wrap the default Printable or even
replace it altogether.
The easiest way to print your text component is to call the print method without parameters. See the
code example below.
try {
boolean complete = textComponent.print();
if (complete) {
/* show a success message */
...
} else {
/*show a message indicating that printing was cancelled */
...
}
} catch (PrinterException pe) {
/* Printing failed, report to the user */
...
}
When you call the print method with no parameters, a print dialog is displayed, and then your text
component is printed interactively without a header or a footer. The code example below shows the
print method signature with the complete set of arguments.
boolean complete = textComponent.print(MessageFormat headerFormat,
MessageFormat footerFormat,
boolean showPrintDialog,
PrintService service
PrintRequestAttributeSet attributes,
boolean interactive);
When you call the print method with all arguments, you explicitly choose printing features such as
header and footer text, printing attributes, a destination print service, and also whether to show a
print dialog or not, and whether to print interactively or non-interactively. To decide which
parameters suit your needs best, see the description of available features below.
In interactive mode a progress dialog with an abort option is shown for the duration of printing. Here
is a sample of a progress dialog.
825
This dialog allows the user to keep track of printing progress. The progress dialog is modal when the
print method is called on the event dispatch thread and non-modal otherwise. It is important that
your document remain unchanged while being printed, otherwise the printing behavior is undefined.
The print method ensures that your document will not be changed and disables the component for
the duration of printing.
If you call the print method on the event dispatch thread in non-interactive mode, then all events
including repaints will be blocked. That is why printing non-interactively on EDT is only
recommended for applications with non-visible GUI.
Print Dialog
You can display a standard print dialog which allows the user to do the following:
Select a printer
Specify number of copies
Change printing attributes
Cancel printing before it has been started
Start printing
You may notice that the print dialog does not specify the total number of pages in the printout. This
is because the text printing implementation uses the Printable API and the total number of pages is
not known before printing time.
Headers and footers are provided by MessageFormat parameters. These parameters allow the header
and footer to be localized. Read the documentation for the MessageFormat class as characters such
826
as single quotes are special and need to be avoided. Both headers and footers are centered. You can
insert a page number by using {0}.
Since the total number of pages in the output is not known before printing time, there is no way to
specify a numbering format like "Page 1 of 5".
With the use of the JTextComponent printing API you do not need to take care of layout and
pagination. Both layout and pagination are done automatically. The document content is formatted to
fit the page size and spreads across multiple pages. You only need to specify an appropriate footer
text format to the print method if you want to insert a page number in the footer. As demonstrated
earlier, you can specify the page number in your footer by including "{0}" in the string given to the
MessageFormat footer parameter. In the printed output, {0} will be replaced by the current page
number.
Let us look at an example called TextAreaPrintingDemo. The main feature of this demo is printing
a text document either on the event dispatch thread or on a background thread depending on the
user's choice. This demo displays a text area, allows to select several printing features, and prints the
text area's content according to the selected options. The entire code for this program can be found in
TextAreaPrintingDemo.java. This demo's rich GUI is built in the NetBeans IDE GUI builder.
Here is a picture of the TextAreaPrintingDemo application.
827
Try this:
2. Edit the text in the Header or Footer checkboxes or both to provide a different
header or footer.
3. Clear the Show Progress Dialog checkbox if you want to print without
displaying a progress dialog, which means printing non-interactively. Note
that you will not be able to cancel printing once it has been started.
4. Clear the Print in Background checkbox to select printing on the event
dispatch thread. Note that printing on EDT non-interactively will make your
application unresponsive — interaction with your application will be blocked
for the duration of the printing process.
5. Click the Print button to print the text area's content according to the selected
options.
Whenever a web-launched application tries to print, Java Web Start opens up a security dialog asking
the user for permission to print unless this permission has already been granted in the system
settings. To proceed with printing the user has to accept the request.
An action listener is registered for the Print button. As the user clicks the Print button the
actionPerformed method calls the print method, which initiates a printing task. The printing task
is a SwingWorker object. The code example below shows how the PrintingTask class is
implemented.
@Override
protected Object doInBackground() {
try {
complete = text.print(headerFormat, footerFormat,
true, null, null, interactive);
message = "Printing " + (complete ? "complete" : "canceled");
} catch (PrinterException ex) {
message = "Sorry, a printer error occurred";
} catch (SecurityException ex) {
message =
"Sorry, cannot access the printer due to security reasons";
}
return null;
}
828
@Override
protected void done() {
message(!complete, message);
}
}
The code example below shows how the print method obtains the set of selected options from the
GUI components, then creates an instance of the PrintingTask class, and performs printing.
private void print(java.awt.event.ActionEvent evt) {
MessageFormat header = createFormat(headerField);
MessageFormat footer = createFormat(footerField);
boolean interactive = interactiveCheck.isSelected();
boolean background = backgroundCheck.isSelected();
829
Try this:
You can find the printing code in the printSelectedPages method. When called, this method first
obtains the amount of pages selected for printing. The code example below shows how the
printSelectedPages method creates a Runnable object for each page and then prints the current
page on a separate thread.
830
for (int i = 0; i < n; i++) {
final PageItem item = (PageItem) pages.getElementAt(i);
// This method is called from EDT. Printing is a time-consuming
// task, so it should be done outside EDT, in a separate thread.
Runnable printTask = new Runnable() {
public void run() {
try {
item.print(
// Two "false" args mean "no print dialog" and
// "non-interactive" (ie, batch-mode printing).
null, null, false, printService, null, false);
} catch (PrinterException pe) {
JOptionPane.showMessageDialog(null,
"Error printing " + item.getPage() + "\n" + pe,
"Print Error", JOptionPane.WARNING_MESSAGE);
}
}
};
new Thread(printTask).start();
This section lists methods defined in the JTextComponent class that allow you to print text
documents.
Method Purpose
When called without arguments, displays a print dialog, and
then prints this text component interactively without a
header or a footer text. Returns true if the user continued
printing and false if the user cancelled printing.
When called with the two MessageFormat arguments,
boolean print()
displays a print dialog, and then prints this text component
boolean print(MessageFormat,
interactively with the specified header and footer text.
MessageFormat)
When called with a full set of arguments, prints this text
boolean print(MessageFormat,
component according to the specified arguments. The two
MessageFormat, boolean,
MessageFormat arguments specify header and footer text.
PrintRequestAttributeSet, boolean,
The first boolean argument defines whether to show a print
PrintService)
dialog or not. Another boolean argument specifies whether
to print interactively or not. With two other arguments you
can specify printing attributes and a print service.
Whenever a PrintService argument is omitted, the default
printer will be used.
Returns a Printable object for printing your text
Printable getPrintable(MessageFormat, component. Override this method to get a customized
MessageFormat) Printable object. You can wrap one Printable object into
another in order to obtain complex reports and documents.
This table lists examples that use text printing and points to where those examples are described.
Where
Example Notes
Described
TextAreaPrintingDemo This page Demonstrates the basics of text printing and provides a rich
GUI. Allows the user to specify header or footer text, turn
831
the print dialog on or off, select printing interactively or non-
interactively, and then print according to the selected options.
TextBatchPrintingDemo This page This demo displays a text component with a list of URLs,
allows the user to view HTML pages, add them to the print
list, and print all selected pages at once on background
threads.
Advertising a product
Indicating to the user that the application is launching during long startup times
Providing information that is only needed once per visit
Java Foundation Classes, both Swing and Abstract Windowing Toolkit (AWT), enable a developer
to create splash screens in Java technology applications. However, because the main purpose of a
splash screen is to provide the user with feedback about the application's startup, the delay between
the application's startup and the moment when the splash screen pops up should be minimal. Before
the splash screen can pop up, the application has to load and initialize the Java™ Virtual Machine
(JVM), AWT, Swing, and sometimes application-dependent libraries as well. The resulting delay of
several seconds has made the use of a Java™ technology-based splash screen less than desirable.
Fortunately, Java™ SE 6 provides a solution that allows the application to display the splash screen
much earlier, even before the virtual machine starts. A Java application launcher is able to decode an
image and display it in a simple non-decorated window.
The splash screen can display any gif, png, or jpeg image, with transparency, translucency, and
animation. The figure below represents an example of the Java application splash screen developed
as an animated gif file.
The SplashScreen class is used to close the splash screen, change the splash-screen image, obtain
the image position or size, and paint in the splash screen. An application cannot create an instance of
this class. Only a single instance created within this class can exist, and this instance can be obtained
using the getSplashScreen() static method. If the application has not created the splash screen at
832
startup through the command-line or manifest-file option, the getSplashScreen method returns
null.
Typically, a developer wants to keep the splash-screen image on the screen and display something
over the image. The splash-screen window has an overlay surface with an alpha channel, and this
surface can be accessed with a traditional Graphics2D interface.
The following code snippet shows how to obtain a SplashScreen object, then how to create a
graphics context with the createGraphics() method:
...
final SplashScreen splash = SplashScreen.getSplashScreen();
if (splash == null) {
System.out.println("SplashScreen.getSplashScreen() returned null");
return;
}
Graphics2D g = splash.createGraphics();
if (g == null) {
System.out.println("g is null");
return;
}
...
Note: The SplashDemo application uses fixed coordinates to display overlay information. These
coordinates are image-dependent and calculated individually for each splash screen.
Command-line argument
Java™ Archive (JAR) file with the specified manifest option
To display a splash screen from the command line use the -splash: command-line argument. This
argument is a Java application launcher option that displays a splash screen:
Try this:
833
9. You will see the following output string:
10. SplashScreen.getSplashScreen() returned null
If your application is packaged in a JAR file, you can use the SplashScreen-Image option in a
manifest file to show a splash screen. Place the image in the JAR file and specify the path in the
option as follows:
Manifest-Version: 1.0
Main-Class: <class name>
SplashScreen-Image: <image name>
Try this:
For more information about JAR files, see Using JAR Files in the Packaging
Programs in JAR Files page.
The SplashScreen class cannot be used to create the splash screen. Only a single instance created
within this class can exist.
Method Purpose
getSplashScreen() Returns the SplashScreen object used for Java startup splash screen control.
Creates a graphics context (as a Graphics2D object) for the splash screen overlay
createGraphics()
image, which allows you to draw over the splash screen.
getBounds() Returns the bounds of the splash screen window as a Rectangle.
close() Closes the splash screen and releases all associated resources.
834
The following table lists the example that uses splash screen.
Example Where Described Notes
SplashDemo This section Shows a splash screen before opening the application window.
The java.awt.SystemTray class introduced in Java™ SE version 6 represents the system tray for a
desktop. The system tray can be accessed by calling the static SystemTray.getSystemTray()
method. Before calling this method, use the static method isSupported() to check that the system
tray is supported. If the system tray is not supported on this platform, the isSupported() method
returns false. If the application attempts to call the getSystemTray() method in such a case, the
method will throw a java.lang.UnsupportedOperationException.
An application cannot create an instance of the SystemTray class. Only a single instance created
within this class can exist, and this instance can be obtained using the getSystemTray() method.
The system tray contains one or more tray icons which are added to the tray using the
add(java.awt.TrayIcon) method. They can be removed when they are no longer needed with the
remove(java.awt.TrayIcon) method.
Note: The add() method can throw an AWTException if the operating system or the Java runtime
determines that the icon cannot be added to the system tray. For example, an AWTException will be
thrown by X-Window desktops if the system tray does not exist on the desktop.
The TrayIcon class functionality goes beyond the icon that is displayed in the tray. It also includes a
text tooltip, a pop-up menu, ballon messages, and a set of listeners associated with it. A TrayIcon
object generates various mouse events and supports the addition of corresponding listeners to receive
notification of these events. The TrayIcon class processes some of the events itself. For example, by
default, when a right-click is performed on the tray icon, it displays the specified pop-up menu.
When a double-click is performed, the TrayIcon object generates an ActionEvent to launch an
application. When the mouse pointer hovers over the tray icon, the tooltip is displayed. The icon
image is automatically resized to fit the space allocated for the image on the tray.
The following demo, developed using the AWT package, demonstrates the features of SystemTray
and TrayIcon classes.
835
Unfortunately, the current implementation of the TrayIcon class provides limited support of the
Swing pop-up menu (the JPopupMenu class) and does not enable an application to use all of the
capabilities of the javax.swing package. The workaround proposal for this issue is described in the
Bug Database, see Bug ID 6285881.
Try this:
1. Place the bulb.gif image file in the image directory. Compile and run the example,
consult the example index.
2. The tray icon will appear in the system tray.
3. Double-click the tray icon to launch the corresponding application. The dialog box
will be displayed.
4. Hover the mouse pointer over the tray icon and click the right mouse button. The pop-
up menu appears.
5. Select the Set auto size checkbox menu item. Notice that the icon appearance is
changed as follows.
6. Select the Set tooltip checkbox menu item. Hover the mouse pointer over the tray
icon. The tooltip appears.
7. Choose the About menu item. The dialog box appears. Close the dialog box.
8. Choose any of the Display submenu items. Each of these items displays a message
dialog box of a particular type: error, warning, info, or standard.
9. Use the Exit menu item to quit the application.
The following code snippet shows how to add a tray icon to the system tray and apply a pop-up
menu:
...
//Check the SystemTray is supported
if (!SystemTray.isSupported()) {
System.out.println("SystemTray is not supported");
return;
}
final PopupMenu pop-up = new PopupMenu();
final TrayIcon trayIcon =
new TrayIcon(createImage("images/bulb.gif", "tray icon"));
final SystemTray tray = SystemTray.getSystemTray();
836
// Create a pop-up menu components
MenuItem aboutItem = new MenuItem("About");
CheckboxMenuItem cb1 = new CheckboxMenuItem("Set auto size");
CheckboxMenuItem cb2 = new CheckboxMenuItem("Set tooltip");
Menu displayMenu = new Menu("Display");
MenuItem errorItem = new MenuItem("Error");
MenuItem warningItem = new MenuItem("Warning");
MenuItem infoItem = new MenuItem("Info");
MenuItem noneItem = new MenuItem("None");
MenuItem exitItem = new MenuItem("Exit");
trayIcon.setPopupMenu(pop-up);
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.out.println("TrayIcon could not be added.");
}
...
The complete code for this demo is available in the TrayIconDemo.java file. This demo also uses
the bulb.gif image file.
Removing the current limitations on applying Swing components will enable developers to add such
components as JMenuItem (with image), JRadioButtonMenuItem, and JCheckBoxMenuItem.
Method Purpose
Adds a tray icon to the system tray. The tray icon becomes visible in the system tray
add once it is added. The order in which icons are displayed in a tray is not specified — it
is platform- and implementation-dependent.
Gets the SystemTray instance that represents the desktop's tray area. This method
always returns the same instance per application. On some platforms the system tray
getSystemTray
may not be supported. Use the isSupported() method to check if the system tray is
supported.
Returns information as to whether the system tray is supported on the current
platform. In addition to displaying the tray icon, minimal system tray support
isSupported includes either a pop-up menu (see the TrayIcon.setPopupMenu(PopupMenu)
method) or an action event (see the
TrayIcon.addActionListener(ActionListener)).
837
The TrayIcon API
A TrayIcon object represents a tray icon that can be added to the system tray. A TrayIcon object
can have a tooltip (text), an image, a pop-up menu, and a set of listeners associated with it.
Method Purpose
Sets the auto-size property. Auto-size determines whether the tray image is
setImageAutoSize automatically sized to fit the space allocated for the image on the tray. By default,
the auto-size property is set to false.
Sets the pop-up menu for this TrayIcon object. If pop-up is null, no pop-up
setPopupMenu
menu will be associated with this TrayIcon object.
Sets the tooltip string for this TrayIcon object. The tooltip is displayed
automatically when the mouse hovers over the icon. Setting the tooltip to null
setToolTip removes any tooltip text. When displayed, the tooltip string may be truncated on
some platforms; the number of characters that may be displayed is platform-
dependent.
The following table lists the example that uses tray icons added to the system tray.
Where
Example Notes
Described
TrayIconDemo This section Creates the tray icon in the system tray, adds a pop-up menu to the
tray icon.
You probably either set the look and feel to an invalid look and feel or set it after the UI manager
loaded the default look and feel. If you are sure that the look and feel you specified is valid and
setting the look and feel is the first thing your program does (at the top of its main method, for
example), check whether you have a static field that references a Swing class. This reference can
cause the default look and feel to be loaded if none has been specified. For more information,
including how to set a look and feel after the GUI has been created, see the look and feel section.
838
Is the component inside of a JWindow object? The focus system requires a JWindow's
owning frame to be visible for any components in the JWindow object to get the focus.
By default, if you do not specify an owning frame for a JWindow object, an invisible
owning frame is created for it. The solution is to either specify a visible and focusable
owning frame when creating the JWindow object or to use JDialog or JFrame objects
instead.
Problem: Why cannot my dialog receive the event generated when the user hits the Escape key?
This worked until I ported to release 1.4.
If your dialog contains a text field, it may be consuming the event. (Prior to release 1.4.0, the text
field did not get the focus.)
If you want to get the Escape event regardless of whether a component consumes it,
you should use a KeyEventDispatcher.
If you want to get the Escape event only if a component has not consumed it, then
register a key binding on any JComponent component in the JDialog object, using
the WHEN_IN_FOCUSED_WINDOW input map. For more information, see the How to Use
Key Bindings page.
Current implementation of the TrayIcon class supports the PopupMenu component, but not its Swing
counterpart JPopupMenu. This limitation narrows capabilities to employ additional Swing features,
for example, menu icons. See the Bug ID 6285881.
A new JTrayIcon class will be created to eliminate this inconvenience. Until then,
use AWT components to add a menu item, checkbox menu item, or submenu.
If you do not find your problem in this section, consult Solving Common Component Problems.
If there is a problem you would like to see mentioned on this page, please send us feedback.
839