Swing
- Overview
- Components
- Using Layout Managers
- Creating a Layout Manager
- Absolute Positioning
- Layout Problems
- Some Simple Event-Handling Examples
- Event-Handling Rules
- Swing Listeners
- Implementing Listeners
- Listener API
- Event-Handling Problems
- Overview of Custom Painting
- Using Graphics Primitives
- Using Images
- Performing Animation
- Solving Common Graphics Problems
- Why to Convert
- How to Convert
- Conversion Resources
- Conversion Problems
Overview
The Swing API, also know as JFC 1.1, allows developers to build graphical user interfaces (GUIs) using:
- The Swing Components
- Pluggable Look and Feel Support
- Accessibility API
The Swing release comes as a core part of the Java 2 Platform, Standard Edition, starting with version 1.2. If you browser only works with version 1.1, you can download the Java 2 Platform than JDK 1.1 which has the JFC is built-in. You can also add the Swing API as a stand-alone package.
The following table shows some of the important releases containing Swing API. Bold font indicates the releases typically used in shipping products.
Swing JFC 1.1 Java 2 Platform (SE) Comments Swing 1.0.3 JFC 1.1 none Included in Java Plug-in 1.1.1. Swing 1.1 JFC 1.1 v 1.2, v 1.2.1 Java Plug-in 1.1.2 and Java Plug-in 1.2 provide applet support for JDK 1.1 + Swing 1.1 and Java 2 Platform v 1.2, respectively. Swing 1.1.1 JFC 1.1 v 1.2.2 Java Plug-in 1.1.3 and Java Plug-in 1.2.2 provide applet support for JDK 1.1 + Swing 1.1.1 and Java 2 Platform v 1.2.2, respectively. no separate "Swing" version number none v 1.3
no separate "Swing" version number none v 1.4
What Swing Packages Should I Use?
The 1.1 version of the API has 15 public packages:
- javax.accessibility
- javax.swing
- javax.swing.border
- javax.swing.colorchooser
- javax.swing.event
- javax.swing.filechooser
- javax.swing.plaf
- javax.swing.plaf.basic
- javax.swing.plaf.metal
- javax.swing.plaf.multi
- javax.swing.table
- javax.swing.text
- javax.swing.text.html
- javax.swing.tree
- javax.swing.undo
Most of these example programs include:
- javax.swing
- javax.swing.event
How Are Swing Components Different from AWT Components?
The AWT components are those provided by the JDK 1.0 and 1.1 platforms. Although the Java 2 Platform still supports the AWT components, we strongly encourage you to use Swing components instead. You can identify Swing components because their names start with J. The AWT button class, for example, is named Button, while the Swing button class is named JButton. Additionally, the AWT components are in the java.awt package, while the Swing components are in the javax.swing package.
The biggest difference between the AWT components and Swing components is that the Swing components are implemented with absolutely no native code. Since Swing components aren't restricted to the least common denominator -- the features that are present on every platform -- they can have more functionality than AWT components. Because the Swing components have no native code, they can be be shipped as an add-on to JDK 1.1, in addition to being part of the Java 2 Platform.
Even the simplest Swing components have capabilities far beyond what the AWT components offer:
- Swing buttons and labels can display images instead of, or in addition to, text.
- You can easily add or change the borders drawn around most Swing components. For example, it's easy to put a box around the outside of a container or label.
- You can easily change the behavior or appearance of a Swing component by either invoking methods on it or creating a subclass of it.
- Swing components don't have to be rectangular. Buttons, for example, can be round.
- Assistive technologies such as screen readers can easily get information from Swing components. For example, a tool can easily get the text that's displayed on a button or label.
Swing lets you specify which look and feel your program's GUI uses. By contrast, AWT components always have the look and feel of the native platform.
Another interesting feature is that Swing components with state use models to keep the state. A JSlider, for instance, uses a BoundedRangeModel object to hold its current value and range of legal values. Models are set up automatically, so you don't have to deal with them unless you want to take advantage of the power they can give you.
If you're used to using AWT components, you need to be aware of a few gotchas when using Swing components:
- Programs should not, as a rule, use "heavyweight" components alongside Swing components. Heavyweight components include all the ready-to-use AWT components (such as Menu and ScrollPane) and all components that inherit from the AWT Canvas and Panel classes. This restriction exists because when Swing components (and all other "lightweight" components) overlap with heavyweight components, the heavyweight component is always painted on top. For more information, see Mixing Heavy and Light Components, an article in The Swing Connection.
- Swing components aren't thread safe. If you modify a visible Swing component -- invoking its setText method, for example -- from anywhere but an event handler, then you need to take special steps to make the modification execute on the event-dispatching thread. This isn't an issue for many Swing programs, since component-modifying code is typically in event handlers.
- The containment hierarchy for any window or applet that contains Swing components must have a Swing top-level container at the root of the hierarchy. For example, a main window should be implemented as a JFrame instance rather than as a Frame instance.
- You don't add components directly to a top-level container such as a JFrame. Instead, you add components to a container (called the content pane) that is itself contained by the JFrame.
Converting to Swing tells you more about the differences between Swing components and AWT components.
Compiling and Running Swing Programs
This section tells you how to compile and run a Swing application. The compilation instructions work for all Swing programs -- applets, as well as applications. If you're interested in Swing applets, you should also read Running Swing Applets. If you aren't yet interested in compiling and running Swing programs, you can skip directly to A Quick Tour of a Swing Application's Code, which guides you through the code for a simple application.How you compile and run Swing programs depends on whether you're using JDK 1.1 or the Java 2 Platform. Using the Java 2 Platform is a bit simpler because Swing is built into it. Choose the instructions corresponding to the release you're using:
Both instructions tell you how to run a simple program, called SwingApplication, whose GUI looks like this:
.
Running Swing Applets
You can run Swing applets in any browser that has the appropriate version of Java Plug-in installed. Another option is to use a 1.2-compliant browser. Currently, the only 1.2-compliant browser available is the Applet Viewer utility provided with the Java 2 SDK.
To test whether your browser can run applets, go to HelloSwingApplet.html. You should see a box that looks like the following:
You can find the applet's source code in HelloSwingApplet.java, and the HTML code for including the applet by viewing the HTML source for HelloSwingApplet.html. The bad news is that the HTML code for including the applet is rather convoluted. The good news is that you can generate the HTML code automatically from a simple <APPLET> tag. See the Java Plug-in documentation for details on downloading a free HTML converter.
This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.Here is a more complex applet, which uses multiple class and image files.
This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.The applet's source code is in AppletDemo.java. It uses the files images/right.gif, images/middle.gif, and images/left.gif as well.
Step by Step: Running a Swing-Based Applet
- Find a 1.1 or 1.2 browser or download Java Plug-in into a supported browser. Make sure you have the latest version of the browser and plug-in. If nothing else, you can always use Applet Viewer (appletviewer), which is distributed in the JDK. Java Plug-in supports certain versions of Netscape Navigator and Internet Explorer.
- If you're using a 1.1 browser without Java Plug-in, determine how to load the Swing JAR file into your browser. See Setting the Browser's Class Path for an example of putting the Swing JAR file into the Applet Viewer class path. See Make Your Browser Swing in The Swing Connection for examples of doing the same for Internet Navigator and Netscape Navigator.
- Point the browser at HelloSwingApplet.html and then AppletDemo.html. You should be able to see at least the first applet. The second one might not show up, or might show up without images, due to firewall issues with the way it's packaged.
A Quick Tour of a Swing Application's Code
SwingApplication brings up a window that looks like this:
Each time the user clicks the button, the label is updated. You can find the whole program in SwingApplication.java
The code in SwingApplication.java accomplishes the following tasks:
- Importing Swing packages
- Choosing the look and feel
- Setting up the top-level container
- Setting up buttons and labels
- Adding components to containers
- Adding borders around components
- Handling events
- Dealing with thread issues
- Supporting assistive technologies
Importing Swing Packages
The following line imports the main Swing package:import javax.swing.*;Early JFC 1.1 and Java 2 SDK v 1.2 beta releases used different names for the Swing packages. See Swing Package Names for details.
Most Swing programs also need to import the two main AWT packages:
import java.awt.*; import java.awt.event.*;
Choosing the Look and Feel
Swing allows you to specify which look and feel your program uses -- Java look and feel, Windows look and feel, CDE/Motif look and feel, and so on. The bold code in the following snippet shows you how SwingApplication specifies the look and feel:The preceding code essentially says, "I don't care whether the user has chosen a look and feel -- use the cross-platform look and feel (the Java look and feel)." You already know what the Java look and feel looks like, since almost all of our screenshots show it.public static void main(String[] args) { try { UIManager.setLookAndFeel( UIManager.getCrossPlatformLookAndFeelClassName()); } catch (Exception e) { } ...//Create and show the GUI... }
Setting Up the Top-Level Container
Every program that presents a Swing GUI contains at least one top-level Swing container. For most programs, the top-level Swing containers are instances of JFrame, JDialog, or (for applets) JApplet. Each JFrame object implements a single main window, and each JDialog implements a secondary window. Each JApplet object implements an applet's display area within a browser window. A top-level Swing container provides the support that Swing components need to perform their painting and event handling.The SwingApplication example has only one top-level container, a JFrame. When the user closes the frame, the application exits. Here is the code that sets up and shows the frame:
public class SwingApplication { ... public static void main(String[] args) { ... JFrame frame = new JFrame("SwingApplication"); //...create the components to go into the frame... //...stick them in a container named contents... frame.getContentPane().add(contents, BorderLayout.CENTER); //Finish setting up the frame, and show it. frame.addWindowListener(...); frame.pack(); frame.setVisible(true); } }
Setting Up Buttons and Labels
Like most GUIs, the SwingApplication GUI contains a button and a label. (Unlike most GUIs, that's about all that SwingApplication contains.) Here's the code that initializes the button:The first line creates the button. The second sets the I key as the mnemonic that the user can use to simulate a click of the button. For example, in the Java look and feel, typing Alt-i results in a button click. The third line registers an event handler for the button click. You'll see the event-handling code for this program in Handling Events.JButton button = new JButton("I'm a Swing button!"); button.setMnemonic(KeyEvent.VK_I); button.addActionListener(...create an action listener...);Here's the code that initializes and manipulates the label:
The preceding code is pretty straightforward, except for the line that invokes the setLabelFor method. That code exists solely to hint to assistive technologies that the label describes the button....//where instance variables are declared: private static String labelPrefix = "Number of button clicks: "; private int numClicks = 0; ...//in GUI initialization code: ... label.setLabelFor(button); ...//in the event handler for button clicks: label.setText(labelPrefix + numClicks);
Adding Components to Containers
SwingApplication groups the label and button in a container (a JPanel) before adding the components to the frame. Here's the code that initializes the panel:The first line of code creates the panel. The second line adds a border to it. We'll discuss the border later.JPanel pane = new JPanel(); pane.setBorder(BorderFactory.createEmptyBorder(30, 30, 10, 30)); pane.setLayout(new GridLayout(0, 1)); pane.add(button); pane.add(label);The third line of code creates a layout manager that forces the panel's contents to be displayed in a single column. The last lines add the button and label to the panel. Adding the button and label to the panel means they are controlled by the panel's layout manager. Specifically, a container's layout manager determines the size and position of each component that's been added to the container.
Adding Borders Around Components
Here, again, is the code that adds a border to the panel:This border simply provides some empty space around the panel's contents -- 30 extra pixels on the top, left, and right, and 10 extra pixels on the bottom. Borders are a feature that JPanel inherits from the JComponent class.pane.setBorder(BorderFactory.createEmptyBorder( 30, //top 30, //left 10, //bottom 30) //right );
Handling Events
The SwingApplication example contains two event handlers. One handles button clicks (action events); the other handles window closing (window events). Here is the event-handling code from SwingApplication:button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { numClicks++; label.setText(labelPrefix + numClicks); } }); ... frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } });
Dealing with Thread Issues
The SwingApplication program is thread safe. Once its GUI is visible, its only GUI manipulation (updating the label) occurs in an event handler. Because the event handler runs in the same thread that performs all event handling and painting for the application, there's no possibility that two threads will try to manipulate the GUI at once.
Supporting Assistive Technologies
Support for assistive technologies -- devices such as screen readers that provide alternate ways of accessing information in a GUI -- is already included in every Swing component. The only code in SwingApplication that exists solely to support assistive technologies is this:Assistive technologies can get plenty of information from Swing components, without any special code from you. For example, assistive technologies can automatically get the text information set by the following lines of code:label.setLabelFor(button);JButton button = new JButton("I'm a Swing button!"); label = new JLabel(labelPrefix + "0 "); label.setText(labelPrefix + numClicks); JFrame frame = new JFrame("SwingApplication");
Swing Components and the Containment Hierarchy
A Quick Tour of a Swing Application's Code: We use this program to introduce some commonly used Swing components and to show how the components in a GUI fit together into a containment hierarchy.
SwingApplication creates four commonly used Swing components:
- a frame, or main window (JFrame)
- a panel, sometimes called a pane (JPanel)
- a button (JButton)
- a label (JLabel)
The frame is a top-level container. It exists mainly to provide a place for other Swing components to paint themselves. The other commonly used top-level containers are dialogs (JDialog) and applets (JApplet).
The panel is an intermediate container. Its only purpose is to simplify the positioning of the button and label. Other intermediate Swing containers, such as scroll panes (JScrollPane) and tabbed panes (JTabbedPane), typically play a more visible, interactive role in a program's GUI.
The button and label are atomic components -- components that exist not to hold random Swing components, but as self-sufficient entities that present bits of information to the user. Often, atomic components also get input from the user. The Swing API provides many atomic components, including combo boxes (JComboBox), text fields (JTextField), and tables (JTable).
Here is a diagram of the containment hierarchy for the window shown by SwingApplication. This diagram shows each container created or used by the program, along with the components it contains. Note that if we add a window -- a dialog, for instance -- the new window has its own component hierarchy, unattached to the hierarchy shown in this figure.
As the figure shows, even the simplest Swing program has multiple levels in its containment hierarchy. The root of the containment hierarchy is always a top-level container. The top-level container provides a place for its descendent Swing components to paint themselves. To view the containment hierarchy for any frame or dialog, click its border to select it, and then press Control-Shift-F1. A list of the containment hierarchy will be written to the standard output stream.
Every top-level container indirectly contains an intermediate container known as a content pane. For most programs, you don't need to know what's between a top-level container and its content pane.
As a rule, the content pane contains, directly or indirectly, all of the visible components in the window's GUI. The big exception to the rule is that if the top-level container has a menu bar, then by convention the menu bar goes in a special place outside of the content pane.
To add a component to a container, you use one of the various forms of the add method. The add method has at least one argument -- the component to be added. Sometimes an additional argument is required to provide layout information. For example, the last line of the following code sample specifies that the panel should be in the center of the its container (the content pane).
Here is the code that adds the button and label to the panel, and the panel to the content pane:
frame = new JFrame(...); button = new JButton(...); label = new JLabel(...); pane = new JPanel(); pane.add(button); pane.add(label); frame.getContentPane().add(pane, BorderLayout.CENTER);
Layout Management
The following figures show the GUIs of five programs, each of which displays five buttons. The buttons are identical, and the code for the programs is almost identical. So why do they look so different? Because they use different layout managers to control the size and position of the buttons.Layout management is the process of determining the size and position of components. By default, each container has a layout manager -- an object that performs layout management for the components within the container. Components can provide size and alignment hints to layout managers, on the size and position of those components. The Java platform supplies five commonly used layout managers: BorderLayout, BoxLayout, FlowLayout, GridBagLayout, and GridLayout. These layout managers are designed for displaying multiple components at once, and are shown in the preceding figure. A sixth provided class, CardLayout, is a special-purpose layout manager used in combination with other layout managers.
Whenever you use the add method to put a component in a container, you must take the container's layout manager into account. Some layout managers, such as BorderLayout, require you to specify the component's relative position in the container, using an additional argument with the add method. Occasionally, a layout manager such as GridBagLayout requires elaborate setup procedures. Many layout managers, however, simply place components based on the order they were added to their container.
All this probably sounds more complicated than it is. You can usually either copy code from our examples in Using Swing Components or look up the individual layout manager in Using Layout Managers. Generally, you only ever set the layout manager of two types of containers: content panes (which use BorderLayout by default) and JPanels (which use FlowLayout by default).
Setting the Layout Manager
You can easily change the layout manager that a container uses. Just invoke the container's setLayout method. For example, here's the code that makes a panel use BorderLayout:JPanel pane = new JPanel(); pane.setLayout(new BorderLayout());Although we recommend that you use layout managers, you can perform layout without them. By setting a container's layout property to null, you make the container use no layout manager. With this strategy, called absolute positioning, you must specify the size and position of every component within that container. One drawback of absolute positioning is that it doesn't adjust well when the top-level container is resized, nor does it adjust well to differences between users and systems, such as different font sizes.
Providing Hints about a Component
Sometimes you need to customize the size hints that a component provides to its container's layout manager, so that the component will be laid out well. You do this by specifying the minimum, preferred, and maximum sizes of the component. You can either invoke the component's methods for setting size hints -- setMinimumSize, setPreferredSize, and setMaximumSize -- or you can create a subclass of the component that overrides the appropriate getter methods -- getMinimumSize, getPreferredSize, and getMaximumSize. Currently, the only layout manager in the Java platform that pays attention to a component's requested maximum size is BoxLayout.Besides providing size hints, you can also provide alignment hints. For example, you can specify that the top edges of two components should be aligned. You set alignment hints either by invoking the component's setAlignmentX and setAlignmentY methods, or by overriding the component's getAlignmentX and getAlignmentY methods. Currently, BoxLayout is the only layout manager that pays attention to alignment hints.
Putting Space Between Components
Three factors influence the amount of space between visible components in a container:
- The layout manager
- Some layout managers automatically put space between components; others don't. Some let you specify the amount of space between components. See Laying Out Components Within a Container for information about spacing support in each layout manager.
- Invisible components
- You can create lightweight components that perform no painting, but that can take up space in the GUI. Often, you use invisible components in containers controlled by BoxLayout. See BoxLayout for examples of using invisible components.
- Empty borders
- No matter what the layout manager, you can affect the apparent amount of space between components by adding empty borders to components. The best candidates for empty borders are components that typically have no default border, such as panels and labels. Some other components might not work well with borders in some look-and-feel implementations, because of the way their painting code is implemented.
How Layout Management Occurs
Here's an example of a layout management sequence for a frame (JFrame).
- After the GUI is constructed, the pack method is invoked on the JFrame. This specifies that the frame should be at its preferred size.
- To find the frame's preferred size, the frame's layout manager adds the size of the frame's edges to the preferred size of the component directly contained by the frame. This is the sum of the preferred size of the frame's content pane, plus the size of the frame's menu bar, if any.
- The content pane's layout manager is responsible for figuring out the content pane's preferred size. By default, this layout manager is a BorderLayout object. However, let's assume that we replace it with a GridLayout object that's set up to create two columns, as in the bottom right of the preceding snapshot. The interesting thing about grid layout is that it forces all components to be the same size, and it tries to make them as wide as the widest component's preferred width and as high as highest one's preferred height.
First, the grid layout manager queries the content pane for its insets -- the size of the content pane's border, if any. Next, the grid layout manager queries each component in the content pane for its preferred size, noting the largest preferred width and largest preferred height. Then it calculates the content pane's preferred size.
- When each button is asked for its preferred size, the button first checks whether the user specified a preferred size. If so, it reports that size. If not, it queries its look and feel for the preferred size.
The end result is that to determine the best size for the frame, the system determines the sizes of the containers at the bottom of the containment hierarchy. These sizes then percolate up the containment hierarchy, eventually determining the frame's total size. Similar calculations occur when the frame is resized.
Event Handling
Every time the user types a character or pushes a mouse button, an event occurs. Any object can be notified of the event. All it has to do is implement the appropriate interface and be registered as an event listener on the appropriate event source. Swing components can generate many kinds of events. Here are a few examples:
Act that results in the event Listener type User clicks a button, presses Return while typing in a text field, or chooses a menu item ActionListener User closes a frame (main window) WindowListener User presses a mouse button while the cursor is over a component MouseListener User moves the mouse over a component MouseMotionListener Component becomes visible ComponentListener Component gets the keyboard focus FocusListener Table or list selection changes ListSelectionListener Each event is represented by an object that gives information about the event and identifies the event source. Event sources are typically components, but other kinds of objects can also be event sources. As the following figure shows, each event source can have multiple listeners registered on it. Conversely, a single listener can register with multiple event sources.
Whenever you want to detect events from a particular component, first check the how-to section for that component.Caption: Multiple listeners can register to be notified of events of a particular type from a particular source.
How to Implement an Event Handler
Every event handler requires three bits of code:
- In the declaration for the event handler class, code that specifies that the class either implements a listener interface or extends a class that implements a listener interface. For example:
public class MyClass implements ActionListener {
- Code that registers an instance of the event handler class as a listener upon one or more components. For example:
someComponent.addActionListener(instanceOfMyClass);
- Code that implements the methods in the listener interface. For example:
public void actionPerformed(ActionEvent e) { ...//code that reacts to the action... }Let's investigate a typical event-handling scenario by looking at how buttons (JButton) handle mouse clicks. To detect when the user clicks an on-screen button (or does the keyboard equivalent), a program must have an object that implements the ActionListener interface. The program must register this object as an action listener on the button (the event source), using the addActionListener method. When the user clicks the on-screen button, the button fires an action event. This results in the invocation of the action listener's actionPerformed method (the only method in the ActionListener interface). The single argument to the method is an ActionEvent object that gives information about the event and its source.
Caption: When the user clicks a button, the button's action listeners are notified.Event handlers can be instances of any class. Often, an event handler that has only a few lines of code is implemented using an anonymous inner class -- an unnamed class defined inside of another class. Anonymous inner classes can be somewhat confusing at first, but once you're used to them they make code clearer by keeping the implementation of an event handler close to where the event handler is registered.
Threads and Event Handling
Event-handling code executes in a single thread, the event-dispatching thread. This ensures that each event handler will finish executing before the next one executes. For instance, the actionPerformed method in the preceding example executes in the event-dispatching thread. Painting code also executes in the event-dispatching thread. This means that while the actionPerformed method is executing, the program's GUI is frozen -- it won't repaint or respond to mouse clicks, for example.The code in event handlers should execute very quickly! Otherwise, your program's perceived performance will be poor. If you need to perform some lengthy operation as the result of an event, do it by starting up another thread (or somehow sending a request to another thread) to perform the operation.
Painting
You might not need the information in this section at all. However, if your components don't seem to be painting themselves correctly, understanding the concepts in this section might help you figure out what's wrong. If you plan to create custom painting code for a component, this section is required reading.
How Painting Works
When a Swing GUI needs to paint itself -- whether for the first time, in response to becoming unhidden, or because it needs to reflect a change in the program's state -- it starts with the highest component that needs to be repainted and works its way down the containment hierarchy. This process is orchestrated by the AWT painting system, and made more efficient and smooth by the Swing repaint manager and double-buffering code.Swing components generally repaint themselves whenever necessary. When you invoke the setTextg method on a component, for example, the component should automatically repaint itself and, if appropriate, resize itself. If it doesn't, it's a bug. The workaround is to invoke the repaint method on the component to request that the component be scheduled for painting. If the component's size or position needs to change but doesn't do so automatically, you should invoke revalidate upon the component before invoking repaint.
Like event-handling code, painting code executes on the event-dispatching thread. While an event is being handled, no painting will occur. Similarly, if a painting operation takes a long time, no events will be handled during that time.
Programs should paint only when the painting system tells them to because each occurrence of a component painting itself must execute without interruption. Otherwise, unpredictable results could occur, such as a button being painted as half pressed and half unpressed.
For smoothness, Swing painting is double-buffered by default -- performed to an offscreen buffer and then flushed to the screen once finished. It might slightly help performance if you make a Swing component opaque, so that the Swing painting system can know not to paint anything behind the component. To make a Swing component opaque, invoke setOpaque(true)g on the component.
Although their available painting area is always rectangular, non-opaque Swing components can appear to be any shape. A button, for instance, might display itself by painting a filled octagon. The component behind the button (its container, most likely) would then be visible, showing through at the corners of the button's bounds. The button would have to include special hit detection code to avoid acting pressed if the user happens to click on its corners.
An Example of Painting
To illustrate painting, we'll use the SwingApplicationg program. Here is SwingApplicationg's GUI:Here, again, is its containment hierarchy: When the GUI for SwingApplicationg is painted, here's what happens:
In this way, each component paints itself before any of the components it contains. This ensures that the background of a JPanelg, for example, is visible only where it isn't covered by painting performed by one of the components it contains. The following figure illustrates the order in which each component that inherits from JComponentg paints itself:
- The top-level container, JFrameg, paints itself.
- The content pane first paints its background, which is a solid gray rectangle. It then tells the JPanelg to paint itself. The content pane's background rectangle doesn't actually appear in the finished GUI because the content pane is completely obscured by the JPanelg.
It's important that the content pane be opaque. Otherwise, messy repaints will result. Because the JPanelg is opaque, we could make it the content pane (by substituting setContentPaneg for the existing code getContentPane().addg). This would slightly simplify the containment hierarchy and painting by removing an unnecessary container.
- The JPanelg first paints its background, a solid gray rectangle. Next, it paints its border. The border is an EmptyBorderg, which has no effect except for increasing the JPanelg's size by reserving some space at the edge of the panel. Finally, the panel asks its children to paint themselves.
- To paint itself, the JButtong paints its background rectangle, if necessary, and then paints the text that it contains. If the button has the keyboard focus, meaning that any typing goes directly to the button for processing, then the button does some look-and-feel-specific painting to make clear that it has the focus.
- To paint itself, the JLabelg paints its text.
1. background
(if opaque)2. custom
painting
(if any)3. border
(if any)4. children
(if any)
Threads and Swing
If your program creates and refers to its GUI the right way, you might not need to worry about threads. For example, if your program is an applet, it's safe to construct its GUI in the init method. And if your program is an application with the following common pattern, you're also safe://Thread-safe example public class MyApplication { public static void main(String[] args) { JFrame f = new JFrame(...); ...//Add components to the frame here... f.pack(); f.setVisible(true); //Don't do any more GUI work here. } ... //All manipulation of the GUI -- setText, getText, etc. -- //is performed in event handlers such as actionPerformed(). ... }
- The Single-Thread Rule
- Swing components can be accessed by only one thread at a time, generally, the event-dispatching thread.
- Exceptions to the Rule
- A few operations are guaranteed to be thread safe.
- How to Execute Code in the Event-Dispatching Thread
- If you need access to the UI from outside event-handling or painting code, you can use the SwingUtilities invokeLater or invokeAndWait method.
The Single-Thread Rule
The single-thread rule is as follows:
Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
This rule might sound scary, but for many simple programs, you don't have to worry about threads. Before we go into detail about how to write Swing code, let's define the term realized.
Realized means that the component has been painted on-screen, or that it is ready to be painted. A Swing component that's a top-level window is realized by having one of these methods invoked on it: setVisible(true), show, or pack. Once a window is realized, all the components that it contains are realized. Another way to realize a component is to add it to a container that's already realized. You'll see examples of realizing components later.
The show method does the same thing as setVisible(true).
Exceptions to the Rule
There are a few exceptions to the rule that all code that might affect a realized Swing component must run in the event-dispatching thread.
- A few methods are thread safe.
- In the Swing API documentation, thread-safe methods are marked with this text:
This method is thread safe, although most Swing methods are not.- An application's GUI can often be constructed and shown in the main thread.
- As long as no components (Swing or otherwise) have been realized in the current runtime environment, it's fine to construct and show a GUI in the main thread of an application. To help you see why, here's an analysis of the thread safety of the thread-safe example. To refresh your memory, here are the important lines from the example:
public static void main(String[] args) { JFrame f = new JFrame(...); ...//Add components to the frame here... f.pack(); f.setVisible(true); //Don't do any more GUI work here. }
- The example constructs the GUI in the main thread. In general, you can construct (but not show) a GUI in any thread, as long as you don't make any calls that refer to or affect already-realized components.
- The components in the GUI are realized by the pack call.
- Immediately afterward, the components in the GUI are shown with the setVisible (or show) call. Technically, the setVisible call is unsafe because the components have already been realized by the pack call. However, because the program doesn't already have a visible GUI, it's exceedingly unlikely that a paint request will occur before setVisible returns.
- The main thread executes no GUI code after the setVisible call. This means that all GUI work moves from the main thread to the event-dispatching thread, and the example is, in practice, thread safe.
- An applet's GUI can be constructed and shown in the init method.
- Existing browsers don't paint an applet until after its init and start methods have been called. Thus, constructing the GUI in the applet's init method is safe, as long as you never call show() or setVisible(true) on the actual applet object.
- Two JComponent methods are safe to call from any thread: repaint and revalidate.
- These methods queue requests to be executed on the event-dispatching thread.
- Listener lists can be modified from any thread.
- It's always safe to call the addListenerTypeListener and removeListenerTypeListener methods. The add/remove operations have no effect on an event dispatch that's under way.
How to Execute Code in the Event-Dispatching Thread
Most post-initialization GUI work naturally occurs in the event-dispatching thread. Once the GUI is visible, most programs are driven by events such as button actions or mouse clicks, which are always handled in the event-dispatching thread.However, some programs need to perform non-event-driven GUI work after the GUI is visible. Here are two examples:
- Programs that must perform a lengthy initialization operation before they can be used
- This kind of program should generally show some GUI while the initialization is occurring, and then update or change the GUI. The initialization should not occur in the event-dispatching thread; otherwise, repainting and event dispatch would stop. However, after initialization the GUI update/change should occur in the event-dispatching thread, for thread-safety reasons.
- Programs whose GUI must be updated as the result of nonstandard events
- For example, suppose a server program can get requests from other programs that might be running on different machines. These requests can come at any time, and they result in one of the server's methods being invoked in some possibly unknown thread. How can that method update the GUI? By executing the GUI-update code in the event-dispatching thread.
The SwingUtilities class provides two methods to help you run code in the event-dispatching thread:
- invokeLater
- Requests that some code be executed in the event-dispatching thread. This method returns immediately, without waiting for the code to execute.
- invokeAndWait
- Acts like invokeLater, except that this method waits for the code to execute. As a rule, you should use invokeLater rather than this method.
Features that JComponent Provides
Except for the top-level containers, all components that begin with J inherit from the JComponent class. They get many features from JComponent, such as the ability to have borders, tool tips, and a configurable look and feel. They also inherit many convenient methods. For details about what JComponent provides, see The JComponent Class.
Icons
Many Swing components -- notably buttons and labels -- can display images. You specify these images as Icon objects.
Actions
With Action objects, the Swing API provides special support for sharing data and state between two or more components that can generate action events. For example, if you have a button and a menu item that perform the same function, consider using an Action object to coordinate the text, icon, and enabled state for the two components. For details, see Actions.
Pluggable Look & Feel
A single program can have any one of several looks and feels. You can let the user determine the look and feel, or you can specify the look and feel programatically.
Support for Assistive Technologies
Assistive technologies such as screen readers can use the Accessibility API to get information from Swing components. 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. With very little extra effort, however, you can make your program function even more smoothly with assistive technologies, which might well expand its market.
Separate Data and State Models
Most noncontainer Swing components have models. A button (JButton), for example, has a model (ButtonModel) that stores the button's state -- what its keyboard mnemonic is, whether it's enabled, selected, or pressed, and so on. Some components have multiple models. A list (JList), for example, uses a ListModel to hold the list's contents, and a ListSelectionModel to track the list's current selection.You don't usually need to know about the models that a component uses. For example, almost all programs that use buttons deal directly with the JButton object, and don't deal at all with the ButtonModel object.
Why then do separate models exist? Because they give you the ability to work with components more efficiently and to easily share data and state between components. One common case is when a component, such as a list or table, contains lots of data. It can be much faster to manage the data by directly working with a data model than by having the component forward each data request to the model. You can use the component's default model, or you can implement your own.
For information about models, see the how-to pages for individual components. Also, The Anatomy of a Swing-Based Program discusses some custom models used by that section's featured program, Converter.
The Anatomy of a Swing-Based Program
This section picks apart a Swing program, called Converter, that has a GUI. You can see how this program is implemented by looking at its source code, which is mainly in Converter.java. However, this section doesn't talk about individual lines of code. Instead, it concentrates on how the Converter program uses the GUI features provided by the Java platform.Converter is an application that converts distance measurements between metric and U.S. units. To run it, compile the seven source files listed in the Examples Index. Once you've compiled the program, run it by invoking the interpreter on the Converter class. If you need help compiling or running Converter, see Compiling and Running Swing Programs.
Here is an annotated snapshot of Converter's GUI:
This section discusses the following features of Converter:
- Swing components
- The containment hierarchy
- Layout management and borders
- Separate models
- Look and feel
- Event Handling
Swing Components
As the preceding figure shows, Converter has the following visible components:
- One JFrame
- Two custom JPanels
- Two custom JTextFields
- Two JSliders
- Two JComboBoxes
The JFrame is the top-level container; it provides the only window in the application. All the other components in the application are contained by the JFrame.
Except for the top-level container, all the visible components in Converter inherit from JComponent. The JComponent class provides many features, such as support for borders and accessibility. The two custom JPanels shown in the snapshot use the border support to provide titles (for example, "Metric System") and to paint boxes around themselves.
The Containment Hierarchy
The following figure shows the containment hierarchy for the JFrame:This diagram shows three components not labeled in the preceding snapshot because they don't paint anything noticeable on-screen:
- One JPanel that serves as the content pane
- Two custom JPanels that each hold a text field and slider
These three components exist to affect layout. They do this either by simplifying layout or by adding "empty" borders that add space to the layout. Grouping components -- whether in visible or invisible containers -- also provides hints to assistive technologies. For example, grouping a text field and slider in their own container gives assistive technologies the information that the text field and slider might be closely related.
Under the content pane are two ConversionPanels. One of the ConversionPanels holds the components related to metric distances; the other one does the same for U.S. distances.
Each ConversionPanel contains three visible components: a text field, a slider, and a combo box. The text field and slider are grouped together in a JPanel, mainly to make layout simpler.
Layout Management and Borders
The following figure shows a colorized version of Converter. In this version, each container has a background color, so that you can easily see the parts of the containers that aren't covered by other components. Note that all the containers are opaque; otherwise, the background color would not automatically be painted on the container.Converter creates five layout manager objects -- one instance of GridLayout and four of BoxLayout. The first JPanel (the custom content pane) uses GridLayout to make the ConversionPanels exactly equal in size. The code sets up the GridLayout so that it puts the ConversionPanels in a single column (two rows), with five pixels between components. The JPanel is initialized to have an empty border that adds five pixels between the panel and the sides of the frame.
Each ConversionPanel has a compound border. On the outside is a titled border, and on the inside is an empty border. The titled border paints a look-and-feel-specific box around the ConversionPanel and places the panel's title in the box. The empty border puts some more space between the ConversionPanel and its contents.
Each ConversionPanel uses a BoxLayout manager to place its contents, a JPanel and a JComboBox, in a row. By setting the Y alignment of both the panel and combo box, the program aligns the top of the panel with the top of the combo box.
The JPanel that groups the text field and slider is implemented with an unnamed subclass of JPanel. This subclass overrides the getMinimumSize, getPreferredSize, and getMaximumSize methods so that they all return the same value: 150 pixels wide and the preferred height. This is how we ensure that both text-slider groups have the same width, even though they're controlled by different layout managers. We need to create a subclass of JPanel, instead of just calling the setXxxxSize methods, because the preferred height of components is determined at run time, by the layout manager.
The JPanel that groups the text field and slider uses a top-to-bottom BoxLayout manager so that the text field is placed on top of the slider. This panel also has an empty border that adds a bit of space to its right, between it and the combo box.
Separate Models
This program uses three custom models. The first is a data model for the text fields. Text data models are known as document models. The document model parses the value that the user enters into the text field. It also formats the number so that it looks nice.The other two custom models are slider data models. They ensure that the data displayed by the application is kept in only one place -- in the model for the top slider. The top slider's model is an instance of a custom class called ConverterRangeModel. The bottom slider uses a second custom class, FollowerRangeModel, which forwards all requests to get and set data to the top slider's model.
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 we need to store floating-point data. Thus, we implemented ConverterRangeModel as a subclass of Object, checking it against the DefaultBoundedRangeModel source code (distributed with the JFC 1.1 and JDK 1.2 releases), to make sure we implemented the model correctly. We then implemented FollowerRangeModel as a subclass of ConverterRangeModel.
Look and Feel
The Converter program sets itself up to use the Java Look & Feel. By changing the value of its LOOKANDFEEL variable, you can make it use a different look and feel.
Event Handling
The Converter program creates several event handlers:Most of the Converter program's listeners are implemented in anonymous inner classes. Although inner classes might seem hard to read, at first, they actually make code easier to understand, once you're used to them. By keeping an event handler implementation close to where the event handler is registered, inner classes help you, and those who follow you, to easily find the entire implementation of the event handler.
- Action listeners
- Each combo box has an action listener. Whenever the user selects a new unit of measure, the action listener notifies the relevant slider's model and resets the maximum values of both sliders.
Each text field has an action listener that's notified when the user presses Return to indicate that typing has ended. This action listener updates the corresponding slider's model to reflect the text field's value.
- Change listeners
- Each slider model has a custom change listener. Whenever a slider's value changes, the custom change listener updates the appropriate text field. We didn't have to register the sliders as listeners on their own models, since Swing automatically does so. In other words, whenever a program sets a value in a slider's model, that slider is automatically updated to reflect the model's new state.
The model for the bottom slider adds a change listener to the top slider's model. This change listener fires a change event to the bottom slider model's change listeners, which are the bottom slider and the custom change listener described in the previous paragraph. The effect is that when the top slider's value changes, the value displayed in the bottom slider and text field is updated. It isn't necessary to notify the top slider of changes in the bottom slider, since the bottom slider's model forwards all data-setting requests to the top slider's model.
- Window listener
- A window listener on the frame causes the application to exit when the window is closed.
Actions
If you have two or more components that perform the same function, consider using an Action object to implement the function. An Action object is an ActionListener that provides not only action-event handling, but also centralized handling of the text, icon, and enabled state of tool bar buttons or menu items.By adding an Action to a JToolBar, JMenu, or JPopupMenu, you get the following features:
Here's an example of using an Action to create a tool-bar button and menu item that perform the same function:
- A new JButton (for JToolBar) or JMenuItem (for JMenu and JPopupMenu) that is automatically added to the tool bar or menu. The button or menu item automatically uses the icon and text specified by the Action.
- A registered action listener (the Action object) for the button or menu item.
- Centralized handling of the button or menu item's enabled state.
In releases prior to v 1.3, the only way for a button or menu item to get the full benefit of using an Action is to create the component using the add(Action) method of JToolBar, JMenu, or JPopupMenu. This is because the pre-1.3 releases have no API except addActionListener(ActionListener) to connect an Action to an already existing component. Although you can use addActionListener to add an Action object as an action listener to any button, for example, the button won't be notified when the action is disabled.Action leftAction = new <a class that implements Action>(...); JButton button = toolBar.add(leftAction); JMenuItem menuItem = mainMenu.add(leftAction);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:
leftAction = new AbstractAction("Go left", new ImageIcon("images/left.gif")) { public void actionPerformed(ActionEvent e) { displayResult("Action for first button/menu item", e); } };Here's a picture of a demo application that uses actions to implement three features.
Here is what the user sees when the "Go left" action is disabled:
- Compile and run the application. The source file is ActionDemo.java You will also need three image files.
See Getting Started with Swing if you need help compiling or running this application.- 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.- 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 with which the components were created.- 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's the code that disables the "Go left" action:
After you create components using an Action, you might well need to customize them. For example, you might want to set the tool-tip text for a button. Or 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, no text in its buttons, and tool tips for its buttons. Here's the code that accomplishes this:boolean selected = ...//true if the action should be enabled; //false, otherwise leftAction.setEnabled(selected);button = toolBar.add(leftAction); button.setText(""); //an icon-only button button.setToolTipText("This is the left button"); menuItem = mainMenu.add(leftAction); menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
The Action API
The following tables list the commonly used Action constructors and methods. The API for using Action objects falls into two categories:
Constructor or Method Purpose AbstractAction()
AbstractAction(String)
AbstractAction(String, Icon)Create an Action object. Through arguments, you can specify the text and icon to be used in the components that the action controls. top>void setEnabled(boolean)
boolean isEnabled()top>Set or get whether the components the action controls are enabled. Invoking setEnabled(false) disables all the components that the action controls. Similarly, invoking setEnabled(true) enables the action's components.
Method Purpose JMenuItem add(Action)
JMenuItem insert(Action, int)
(in JMenu and JPopupMenu)Create a JMenuItem object and put it in the menu or popup menu. See the discussion in this section and in Menus for details. JButton add(Action)
(in JToolBar)Create a JButton object and put it in the tool bar. See the discussion in this section and in Tool Bars for details.
Examples that Use Actions
The following examples use Action objects.
Example Notes ActionDemo Uses actions to bind buttons and menu items to the same function. TextComponentDemo Uses text actions to create menu items for text editing commands, such as cut, copy, and paste, and to bind key strokes to caret movement. Also implements custom AbstractAction subclasses to implement undo and redo. The text action discussion begins in Concepts: About Editor Kits.
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.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. Here is an example of code that creates a bordered container:
JPanel pane = new JPanel(); pane.setBorder(BorderFactory.createLineBorder(Color.black));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 rest of this page discusses the following topics:
- The BorderDemo Example
- Using the Borders Provided by Swing
- Creating Custom Borders
- Adding a Border to a Bordered Swing Component
- The Border API
- Examples of Using Borders
The BorderDemo example
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.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, then 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. The next picture shows compound borders. With compound borders, you can combine any two borders, which can themselves be compound borders.
Using the Borders Provided by Swing
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. To run the program, you will also need to put an image file in a directory named images: left.gif.
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.//Keep references to the next few borders, for use in titles //and compound borders. Border blackline, etched, raisedbevel, loweredbevel, empty; blackline = BorderFactory.createLineBorder(Color.black); etched = BorderFactory.createEtchedBorder(); raisedbevel = BorderFactory.createRaisedBevelBorder(); loweredbevel = BorderFactory.createLoweredBevelBorder(); empty = BorderFactory.createEmptyBorder(); //Simple borders jComp2.setBorder(blackline); jComp3.setBorder(raisedbevel); jComp4.setBorder(loweredbevel); jComp5.setBorder(empty); //Matte borders ImageIcon icon = new ImageIcon("images/left.gif"); //20x22 jComp6.setBorder(BorderFactory.createMatteBorder( -1, -1, -1, -1, icon)); jComp7.setBorder(BorderFactory.createMatteBorder( 1, 5, 1, 1, Color.red)); jComp8.setBorder(BorderFactory.createMatteBorder( 0, 20, 0, 0, icon)); //Titled borders TitledBorder title1, title2, title3, title4, title5; title1 = BorderFactory.createTitledBorder("title"); jComp9.setBorder(title1); title2 = BorderFactory.createTitledBorder( blackline, "title"); title2.setTitleJustification(TitledBorder.CENTER); jComp10.setBorder(title2); title3 = BorderFactory.createTitledBorder( etched, "title"); title3.setTitleJustification(TitledBorder.RIGHT); jComp11.setBorder(title3); title4 = BorderFactory.createTitledBorder( loweredbevel, "title"); title4.setTitlePosition(TitledBorder.ABOVE_TOP); jComp12.setBorder(title4); title5 = BorderFactory.createTitledBorder( empty, "title"); title5.setTitlePosition(TitledBorder.BOTTOM); jComp13.setBorder(title5); //Compound borders Border compound1, compound2, compound3; Border redline = BorderFactory.createLineBorder(Color.red); //This creates a nice frame. compound1 = BorderFactory.createCompoundBorder( raisedbevel, loweredbevel); jComp14.setBorder(compound1); //Add a red outline to the frame. compound2 = BorderFactory.createCompoundBorder( redline, compound1); jComp15.setBorder(compound2); //Add a title to the red-outlined frame. compound3 = BorderFactory.createTitledBorder( compound2, "title", TitledBorder.CENTER, TitledBorder.BELOW_BOTTOM); jComp16.setBorder(compound3);The Border interface, as well as its Swing-provided implementations, is in the javax.swing.border package. You often don't need to directly use anything in the border package, except when specifying constants that are specific to a particular border class or when referring to the Border type.
Creating Custom Borders
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:
In addition, if your border is opaque, you might be able to decrease component drawing time by overriding the border's isBorderOpaque method so that it returns true. For examples of implementing borders, see the source code for the classes in the javax.swing.border package.
- 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.
Adding a Border to a Bordered Swing Component
Many of the ready-to-use Swing components use borders to draw the outline of the component. If you want to draw an additional border around an already bordered component -- to provide some extra space above a scroll pane, for example -- then you need to add the new border to the existing border. Be sure to test the component to make sure that it works well with your extra border; components that change their border depending on their state probably aren't good candidates for additional borders. Here's an example of adding to an existing border:
aJComponent.setBorder( BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder(20,0,0,0), aJComponent.getBorder()) );The new border returned by createEmptyBorder adds 20 pixels of empty space above any component that uses it. The code uses the createCompoundBorder method to combine the new border with the existing border, which is returned by getBorder.
The Border API
The following tables list the commonly used border methods. The API for using borders falls into two categories:
Method Purpose Border createLineBorder(Color)
Border createLineBorder(Color, int)Create a line border. The first argument is a java.awt.Color object that specifies the color of the line. The optional second argument specifies the width in pixels of the line. Border createEtchedBorder()
Border createEtchedBorder(Color, Color)Create an etched border. The optional arguments specify the highlight and shadow colors to be used. Border createLoweredBevelBorder() Create a border that gives the illusion of the component being lower than the surrounding area. Border createRaisedBevelBorder() Create a border that gives the illusion of the component being higher than the surrounding area. Border createBevelBorder(int, Color, Color)
Border createBevelBorder(int, Color, Color, Color, Color)Create a raised or lowered beveled border, specifying the colors to use. The integer argument can be either RAISED or LOWERED (constants defined in BevelBorder. With the three-argument constructor, you specify the highlight and shadow colors, With the five-argument constructor, you specify the outer highlight, inner highlight, outer shadow, and inner shadow colors, in that order. Border createEmptyBorder()
Border createEmptyBorder(int, int, int, int)Create an invisible border. If you specify no arguments, then the border takes no space, which is useful when creating a titled border with no visible boundary. The optional arguments specify the number of pixels that the border occupies at the top, left, bottom, and right (in that order) of whatever component uses it. MatteBorder createMatteBorder(int, int, int, int, Color)
MatteBorder createMatteBorder(int, int, int, int, Icon)Create a matte border. The integer arguments specify the number of pixels that the border occupies at the top, left, bottom, and right (in that order) of whatever component uses it. The Color argument specifies the color which with the border should fill its area. The Icon argument specifies the icon which with the border should tile its area. TitledBorder createTitledBorder(String)
TitledBorder createTitledBorder(Border)
TitledBorder createTitledBorder(Border, String)
TitledBorder createTitledBorder(Border, String, int, int)
TitledBorder createTitledBorder(Border, String, int, int, Font)
TitledBorder createTitledBorder(Border, String, int, int, Font, Color)Create a titled border. The string argument specifies the title to be displayed. The optional font and color arguments specify the font and color to be used for the title's text. The border argument specifies the border that should be displayed along with the title. If no border is specified, then a look-and-feel-specific default border is used. By default, the title straddles the top of its companion border and is left-justified. The optional integer arguments specify the title's position and justification, in that order. TitledBorder defines these possible positions: ABOVE_TOP, TOP (the default), BELOW_TOP, ABOVE_BOTTOM, BOTTOM, and BELOW_BOTTOM. You can specify the justification as LEFT (the default), CENTER, or RIGHT.
CompoundBorder createCompoundBorder(Border, Border) Combine two borders into one. The first argument specifies the outer border; the second, the inner border.
Method Purpose top>void setBorder(Border)
Border getBorder()top>Set or get the border of the receiving JComponent. top>void setBorderPainted(boolean)
boolean isBorderPainted()
(in AbstractButton, JMenuBar, JPopupMenu, JProgressBar, and JToolBar)top>Set or get whether the border of the component should be painted.
Examples that Use Borders
Many examples in this lesson use borders. The following table lists a few interesting cases.
Example Where Described Notes BorderDemo This section Shows an example of each type of border that BorderFactory can create. Also uses an empty border to add breathing space between each pane and its contents. AlignmentDemo BoxLayout Uses titled borders. BoxLayoutDemo BoxLayout Uses a red line to show where the edge of a container is, so that you can see how the extra space in a box layout is distributed. ComboBoxDemo2 Combo Boxes Uses a compound border to combine a line border with an empty border. The empty border provides space between the line and the component's innards.
Icons
Some Swing components, such as JLabel and JButton, can be decorated with an icon -- a fixed-sized picture. An icon is an object that adheres to the Icon interface. Swing provides a particularly useful implementation of the Icon interface: ImageIcon , which paints an icon from a GIF or JPEG image.Here's a snapshot of an application that decorates two labels with an icon:
The program uses one image icon to contain and paint the yellow splat. One statement creates the image icon and two more statements include the image icon on each of the two labels: The first argument to the ImageIcon constructor specifies the file to load, relative to the directory containing the application's class file. The second argument provides a description of the icon, to be used by assistive technologies. This description might be used, for example, to help a visually impaired user understand what information the icon conveys.ImageIcon icon = new ImageIcon("images/middle.gif", "a pretty but meaningless splat"); ... label1 = new JLabel("Image and Text", icon, JLabel.CENTER); ... label3 = new JLabel(icon);Applets generally load image data from the computer that served up the applet. There are two reasons for this. First, untrusted applets can't read from the file system on which they're running. Second, it just makes sense to put an applet's class and data files together on the server. To load image data from the server, an applet uses a URL as shown in the following example:
If you're writing an applet, you might want to copy the getURL method for use in your applet.public class SomeClass extends JApplet ... { protected String leftButtonFilename = "images/left.gif"; ... public void init() { ... URL leftButtonURL = getURL(leftButtonFilename); ... leftButtonIcon = new ImageIcon(leftButtonURL, "an arrow pointing left"); ... } ... protected URL getURL(String filename) { URL codeBase = getCodeBase(); URL url = null; try { url = new URL(codeBase, filename); } catch (java.net.MalformedURLException e) { System.err.println("Couldn't create image: " + "badly specified URL"); return null; } return url; } ... }
When you specify a filename or URL to an ImageIcon constructor, the constructor returns only after the image data is completely loaded. Thus, you can be sure that the image icon is usable following the call to the constructor. 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 and a MediaTracker object, which is shared by all image icons in the same program, to keep track of the image's loading status. If you're curious about Image objects, image observers, media trackers, and other image topics, see Using Images.
A More Complex Image Icon Example
Here's an applet that uses eight image icons. In the snapshot, you can see three of them: one displays the photograph and two decorate the buttons at the bottom of the applet window with small arrows.
This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.
- Run the applet.
The main source code for the program is IconDemoApplet.java. You will also need a few other source files and several image files. See the examples index for links to all the files required by this example.
- Click the Previous Picture and Next Picture buttons to view the photographs.
- Hold the mouse over a photograph. A tool tip appears that indicates the filename of the current photograph and its width and height.
- To view your own photographs, modify the applet parameters. Here's the applet tag used for the applet running above:
<applet code="IconDemoApplet.class" codebase="example-swing/" archive="icon.jar" width="400" height="360"> <param NAME="IMAGEDIR" VALUE="images"> <param NAME="IMAGE0" VALUE="stickerface.gif"> <param NAME="CAPTION0" VALUE="Sticker Face"> <param NAME="WIDTH0" VALUE="230"> <param NAME="HEIGHT0" VALUE="238"> ... <applet>The IMAGEDIR parameter indicates that the image files should be in a directory named images relative to the applet's codebase. Four parameters are required for each photograph and the applet uses four photographs. The tag shown above shows the parameters for only the first photograph.
Specifying the Image Source
Most often, an image icon's data comes from an image file. You can specify the location of the file with either a filename or a URL object. For applications, the filename or URL is generally relative either to the directory containing the application's class files, or to the class path. Applets generally use a URL that is constructed relative to the applet's code base (the directory containing the applet's class files).
You've already seen how to specify a filename relative to the directory containing the application's class files. To specify a URL relative to an application's class path, you can use the ClassLoader getSystemResource method. Here is an example:
The getSystemResource method looks through the directories and JAR files in the program's class path, returning a URL as soon as it finds the desired file. For example, assume that you put a JAR file named icons.jar in your program's class path. If the JAR file contains images/middle.gif, then the class loader will definitely return a URL for images/middle.gif. However, the URL might not be relative to icons.jar, if another JAR file or directory in the class path contains images/middle.gif. The URL will point to the first JAR file or directory in the class path that contains images/middle.gif.ImageIcon icon = null; URL iconURL = ClassLoader.getSystemResource("images/middle.gif"); if (iconURL != null) { icon = new ImageIcon(iconURL, "a beautiful yet meaningless icon"); }The IconDemoApplet program initializes each of its image icons from GIF files whose locations are specified with URLs. Because IconDemoApplet is designed to be an untrusted applet, we must place the image files under the applet's code base. The following figure shows the locations of files for IconDemoApplet.
Applets are supposed to be able to load images from JAR files. Currently, however, some browsers can't read images from a JAR file, although they do successfully get classes from a JAR file. With our applets, we currently hedge our bets by both putting the image files in the applet's archive file (the JAR file containing the applet's class files) and by putting the image files in the file system on the server. The figure above depicts our setup.
Improving Perceived Performance When Loading Image Icons
Because the photograph images are large and because the applet uses multiple images, IconDemoApplet uses several techniques to improve the performance of the program as perceived by the user.As with all performance-related issues, these techniques work in some situations and not others. These are not general recommendations for all programs, but some techniques you can try to improve the user's experience. Furthermore, the techniques described here are designed to improve the program's perceived performance, but don't necessarily impact its real performance.
- Providing dimmed icons -- The applet provides dimmed versions of the arrows for the buttons:
Without this code, the dimmed versions of the arrows would be computed, which causes a slight delay the first time each button is dimmed. Basically, this technique trades a noticeable delay when the user clicks the buttons for a smaller, less noticeable delay in the init method.imagedir = getParameter("IMAGEDIR"); if (imagedir != null) imagedir = imagedir + "/"; ... ImageIcon dimmedNextIcon = new ImageIcon( getURL(imagedir + "dimmedRight.gif")); ImageIcon dimmedPreviousIcon = new ImageIcon( getURL(imagedir + "dimmedLeft.gif")); ... nextButton.setDisabledIcon(dimmedNextIcon); ... previousButton.setDisabledIcon(dimmedPreviousIcon);This applet uses four separate image files just to display arrows on two buttons. The performance impact of these little images can add up, especially if the browser in which the applet is running uses a separate HTTP connection to load each one. A better alternative is to implement a custom Icon that paints the arrows. See Creating a Custom Icon Implementation for an example.
- Lazy image loading -- The applet's initialization code loads only the first photograph. Each other photograph gets loaded when the user first requests to see it. By loading images if and when needed, the applet avoids a long initialization. The downside is that the user has to wait to see each photograph. We try to make this wait less noticeable by providing feedback about the image loading and allowing the user to use the GUI while the image is loading.
Not all programs can benefit from lazy loading. For example, the TumbleItem.java applet performs an animation, so all of the images are needed up-front. That applet's initialization code causes the images to be loaded in a background thread, so that the applet can present a GUI (a "Loading Images..." label) before the images have loaded.
- Background image loading -- The applet uses a SwingWorker to load each photograph image in a background thread. Because the image is loaded in a separate thread, the user can still click the buttons and otherwise interact with the applet while the image is loading.
Here's the code to load each image:
The construct method, which creates the image icon for the photograph, is invoked by the thread that's created by the SwingWorker constructor and started by the start method. After the image icon is fully loaded, the finished method is called. The finished method is guaranteed to execute on the event-dispatching thread, so it can safely update the GUI to display the photograph.ImageIcon icon = null; public Object construct() { icon = new ImageIcon(getURL(imagePath)); return icon; } public void finished() { Photo pic = (Photo)pictures.elementAt(index); pic.setIcon(icon); if (index == current) updatePhotograph(index, pic); } }; worker.start(); }
- Status updates -- While the image is loading in the background, the applet displays a status message:
This lets the user know that the program is doing something. After the image is loaded, the applet displays the photograph in the viewing area.photographLabel.setIcon(null); photographLabel.setText("Loading image...");
- Caching -- After each photograph is viewed for the first time, the applet caches the image icon for later use. Thus if the user revisits a photograph, the program can use the same image icon and display the photograph quickly.
If you write a program without caching image icons, it may appear that some implicit image caching is going on within the Java platform. However, this is a side effect of the implementation and is not guaranteed. If your program uses one image many times, you can create the image icon once and use the same instance multiple times.
Creating a Custom Icon Implementation
If you use a simple image repeatedly, consider implementing a custom Icon class to paint the image. The really nice thing about a custom icon is that you can easily change the icon's appearance to reflect its host component's state.Look-and-feel implementations often use custom icons. For example, the Metal Look & Feel uses a single MetalCheckBoxIcon object to paint all of the check boxes in the GUI. The MetalCheckBoxIcon paints itself differently depending on whether its host component is enabled, pressed, or selected.
In this section, we'll convert a program called ButtonDemo so that it uses a custom icon to paint these two arrows:
![]()
You can see a picture of ButtonDemo in the Common Button API.
Its source code is in ButtonDemo.java. ButtonDemo uses the following code to load the arrows from GIF files and put the arrows into buttons:
Here is the new code, which uses a custom icon class named ArrowIcon. Only the bold lines have changed. You can find the entire program in CustomIconDemo.java.ImageIcon leftButtonIcon = new ImageIcon("images/right.gif"); ... ImageIcon rightButtonIcon = new ImageIcon("images/left.gif"); b1 = new JButton("Disable middle button", leftButtonIcon); ... b3 = new JButton("Enable middle button", rightButtonIcon);You can find the implementation of the custom icon class in ArrowIcon.java Here are the interesting parts of its code:Icon leftButtonIcon = new ArrowIcon(SwingConstants.RIGHT); ... Icon rightButtonIcon = new ArrowIcon(SwingConstants.LEFT); b1 = new JButton("Disable middle button", leftButtonIcon); ... b3 = new JButton("Enable middle button", rightButtonIcon);Note that the icon sets the current color. If you don't do this, then the icon's painting might not be visible.class ArrowIcon implements Icon, SwingConstants { ... public void paintIcon Component(c, Graphics g, int x, int y) { int length = xPoints.length; int adjustedXPoints[] = new int[length]; int adjustedYPoints[] = new int[length]; for (int i = 0; i < length; i++) { adjustedXPoints[i] = xPoints[i] + x; adjustedYPoints[i] = yPoints[i] + y; } if (c.isEnabled()) { g.setColor(Color.black); } else { g.setColor(Color.gray); } g.fillPolygon(adjustedXPoints, adjustedYPoints, length); } }Using a custom icon to paint the arrows has a few implications:
- Because the icon's appearance is determined dynamically, the icon painting code can use any information -- component and application state, for example -- to determine what to paint.
- Because we specified a non-ImageIcon icon for a button, the button doesn't bother to calculate the dimmed (disabled) version of the icon. Instead, the button lets the icon paint its disabled self. This can reduce computation time and save space that would otherwise be used to hold the dimmed image.
- Depending on the platform, we might get a performance boost with custom icons, since painting polygons is often faster than painting images.
- Instead of loading all the GIF files for the arrows (left and right, and perhaps dimmed left and dimmed right), we load a single class file (ArrowIcon). The performance implications of this depend on factors such as the platform, the size of the files, and the overhead for loading each type of file.
The Image Icon API
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:
- Setting, Getting, and Painting the Image Icon's Image
- Setting or Getting Information about the Image Icon
- Watching the Image Icon's Image Load
Method or Constructor Purpose ImageIcon()
ImageIcon(byte[])
ImageIcon(byte[], String)
ImageIcon(Image)
ImageIcon(Image, String)
ImageIcon(String)
ImageIcon(String, String)
ImageIcon(URL)
ImageIcon(URL, String)Create a ImageIcon instance, initializing it to contain the specified image. The first argument indicates the source -- image, byte array, filename, or URL -- from which the image icon's image should be loaded. The source must be in a format supported by the java.awt.Image class: namely GIF or JPEG. The second argument, when present, provides a description for the image. The description is a short textual description of the image that could be used in a variety of ways, such as alternate text for the image. top>void setImage(Image)
Image getImage()top>Set or get the image displayed by the image icon. top>void paintIcon(Component, Graphics, int, int) top>Paint the image icon's image in the specified graphics context. You would do this only if you're implementing a custom component that performs its own painting. The Component object is used as an image observer. You can rely on the default behavior provided by Component class and pass in any component. The two int argments specify the x and y coordinates, respectively.
Method Purpose top>void setDescription(String)
String getDescription()top>Set or get a description of the image. This description is intended for use by assistive technologies. top>int getIconWidth()
int getIconHeight()Get the width or height of the image icon in pixels.
Method Purpose top>void setImageObserver(ImageObserver)
ImageObserver getImageObserver()top>Set or get an image observer for the image icon. top>int getImageLoadStatus() Get the loading status of the image icon's image. The set of values returned by this method are defined by MediaTracker.
Examples that Use Icons
The following table lists just a few of the many examples that use ImageIcon.
Example Where Described Notes LabelDemo This section and Labels Demonstrates using icons in an application's label, with and without accompanying text. IconDemoApplet This section An applet. Uses a label to show large images; uses buttons that have both images and text. CustomIconDemo This section Uses a custom icon class implemented by ArrowIcon.java. TumbleItem How to Make Applets Uses image icons in an animation. Shows how to use ImageIcon's paintIcon method. ButtonDemo Buttons, Check Boxes, and Radio Buttons Shows how to use icons in an application's buttons.