The JFC Swing Tutorial: A Guide to Constructing GUIs (2nd Edition)

 < Day Day Up > 

Sometimes a task running within a program might take a while to complete. A user-friendly program provides some indication to the user that the task is occurring, how long the task might take, and how much work has already been done. One way of indicating work, and perhaps the amount of progress, is to use an animated image.

Another way of indicating work is to set the wait cursor, using the Cursor class and the Component -defined setCursor method. For example, the following code causes the wait cursor to be displayed when the cursor is over container (including any components it contains that have no cursor specified):

container.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

To convey how complete a task is, you can use a progress bar like the one in Figure 41.

Figure 41. Standard progress bar.

Sometimes you can't immediately determine the length of a long-running task, or the task might stay stuck at the same state of completion for a long time. As of 1.4, you can show work without measurable progress by putting the progress bar in indeterminate mode. A progress bar in indeterminate mode displays animation to indicate that work is occurring. As soon as the progress bar can display more meaningful information, you should switch it back into its default, determinate mode. In the Java look and feel, indeterminate progress bars look like Figure 42.

Figure 42. Indeterminate progress bar.

Swing provides three classes to help you use progress bars:

JProgressBar

A visible component to graphically display how much of a total task has completed. The next section has information and an example of using a typical progress bar. The section after that, Using Indeterminate Mode (page 303), tells you how to animate a progress bar to show activity before the task's scope is known.

ProgressMonitor

Not a visible component. Instead, an instance of this class monitors the progress of a task and pops up a dialog if necessary. See Using Progress Monitors (page 305) for details and an example of using a progress monitor.

ProgressMonitorInputStream

An input stream with an attached progress monitor, which monitors reading from the stream. You use an instance of this stream like any of the other input streams described in The Java Tutorial trail, "I/O: Reading and Writing (but no 'rithmetic)." [106] You can get the stream's progress monitor with a call to getProgressMonitor and configure it as described in Using Progress Monitors (page 305).

[106] This trail is on the CD at: JavaTutorial/essentials/index.html and on this book's CD.

After you see a progress bar and a progress monitor in action, Deciding Whether to Use a Progress Bar or a Progress Monitor (page 307) can help you figure out which is appropriate for your application.

Using Determinate Progress Bars

Figure 43 is a picture of a small demo application that uses a progress bar to measure the progress of a task that runs in its own thread.

Figure 43. A screenshot of the ProgressBarDemo example. Note the use of the wait cursor (the hourglass).

Try This:

  1. Run ProgressBarDemo using Java Web Start or compile and run the example yourself. [107]

    [107] To run ProgressBarDemo using Java Web Start, click the ProgressBarDemo link on the RunExamples/components.html page on the CD. You can find the source files here: JavaTutorial/uiswing/components/example-1dot4/index.html#ProgressBarDemo .

  2. Push the Start button. The demo puts up a wait cursor and starts updating the progress bar. The task displays its output in the text area at the bottom of the window.

Below is the code from ProgressBarDemo.java that creates and sets up the progress bar:

//Where member variables are declared: JProgressBar progressBar; //...where the GUI is created: progressBar = new JProgressBar(0, task.getLengthOfTask()); progressBar.setValue(0); progressBar.setStringPainted(true);

The constructor that creates the progress bar sets the progress bar's minimum and maximum values. You can also set these values with setMinimum and setMaximum . The minimum and maximum values used in this program are 0 and the length of the task, which is typical of many programs and tasks . However, a progress bar's minimum and maximum values can be any value, even negative. The code snippet also sets the progress bar's current value to 0.

The call to setStringPainted causes the progress bar to display, within its bounds, a textual indication of the percentage of the task that has completed. By default, the progress bar displays the value returned by its getPercentComplete method formatted as a percent, such as 33%. Alternatively, you can replace the default with a different string by calling setString . For example,

if ( /*...half way done...*/ ) progressBar.setString("Half way there!");

You start ProgressBarDemo 's task by clicking the Start button. Once the task has begun, a timer (an instance of the javax.swing.Timer class) fires an action event every second. Here's the actionPerformed method of the timer's action listener:

public void actionPerformed(ActionEvent evt) { progressBar.setValue(task.getCurrent()); String s = task.getMessage(); if (s != null) { taskOutput.append(s + newline); taskOutput.setCaretPosition( taskOutput.getDocument().getLength()); } if (task.isDone()) { Toolkit.getDefaultToolkit().beep(); timer.stop(); startButton.setEnabled(true); setCursor(null); //turn off the wait cursor progressBar.setValue(progressBar.getMinimum()); } }

The boldface line of code gets the amount of work completed by the task and updates the progress bar with that value. Thus, this example's progress bar measures the progress made by the task each second, not the elapsed time. The rest of the code appends a message to the output log (a text area named taskOutput ) and, if the task is done, turns the timer off and resets the other controls and the cursor.

As mentioned, the long-running task in this program executes in a separate thread. Generally, it's a good idea to isolate a potentially long-running task in its own thread so that the task doesn't block the rest of the program. The long-running task is implemented by LongTask.java , which uses a SwingWorker to ensure that the thread runs safely. [108] See Using the SwingWorker Class (page 636) in Chapter 9 for information about the SwingWorker class.

[108] You can find the source files here: JavaTutorial/uiswing/components/example-1dot4/index.html#LongTask .

Using Indeterminate Mode

Adding indeterminate mode to ProgressBarDemo requires just a few lines of code (shown in bold):

//Where the progress bar is created: progressBar.setStringPainted(true); //get space for the string progressBar.setString(""); //but don't paint it . . . //In the actionPerformed method of the timer's action listener: progressBar.setValue(task.getCurrent()); String s = task.getMessage(); if (s != null) { if (progressBar.isIndeterminate()) { progressBar.setIndeterminate(false); progressBar.setString(null); //display % string } taskOutput.append(s + newline); taskOutput.setCaretPosition( taskOutput.getDocument().getLength()); } if (task.isDone()) { ... progressBar.setString(""); //hide % string } . . . //In the event handler that handles Start button clicks: progressBar.setIndeterminate(true);

The most important code additions are calls to the setIndeterminate method. When the user clicks the Start button, setIndeterminate(true) is invoked so that the user can tell that the task has started, even before any meaningful information about the task's progress can be conveyed. Once the progress bar has some concrete status to display, a call to set-Indeterminate(false) switches the progress bar back into its normal mode. The isIndeterminate method is used to test the progress bar's current state.

The other changes in the code are related to string display. A progress bar that displays a string is likely to be taller than one that doesn't, and, as the demo designers, we've arbitarily decided that this progress bar should display a string only when it's in the default, determinate mode. However, we want to avoid the layout ugliness that might result if the progress bar changed height when it changed modes. Thus, the code leaves in the call to setStringPainted(true) but adds a call to setString("") so that no text will be displayed. Later, when the progress bar switches from indeterminate to determinate mode, invoking setString(null) makes the progress bar display its default string.

One change we did not make was removing the call to progressBar.setValue from the timer's action handler. The call doesn't do any harm because an indeterminate progress bar doesn't use its value property, except perhaps to display it in the status string. In fact, keeping the progress bar's data as up to date as possible is a good practice, since some look and feels might not support indeterminate mode.

Try This:

  1. Run ProgressBarDemo2 using Java Web Start or compile and run the example yourself. [109]

    [109] To run ProgressBarDemo2 using Java Web Start, click the ProgressBarDemo2 link on the RunExamples/components.html page on the CD. You can find the source files here: JavaTutorial/uiswing/components/example-1dot4/index.html#ProgressBarDemo2 .

  2. Push the Start button. Note that the progress bar starts animating as soon as the button is pressed and then switches back into determinate mode (like ProgressBarDemo ).

Using Progress Monitors

Now let's rewrite ProgressBarDemo to use a progress monitor instead of a progress bar. (See Figure 44.)

Figure 44. A screenshot of the new demo program, ProgressMonitorDemo .

Try This:

  1. Run ProgressMonitorDemo using Java Web Start or compile and run the example yourself. [110]

    [110] To run ProgressMonitorDemo using Java Web Start, click the ProgressMonitorDemo link on the RunExamples/components.html page on the CD. You can find the source files here: JavaTutorial/uiswing/components/example-1dot4/index.html#ProgressMonitorDemo .

  2. Push the Start button. After a certain amount of time, the program displays a progress dialog.

  3. Click the OK button. Note that the task continues even though the dialog is gone.

  4. Start another task. After the dialog pops up, click the Cancel button. The dialog goes away and the task stops.

A progress monitor cannot be used again, so a new one must be created each time a new task is started. This program creates a progress monitor each time the user starts a new task with the Start button.

Here's the statement that creates the progress monitor:

progressMonitor = new ProgressMonitor(ProgressMonitorDemo.this, "Running a Long Task", "", 0, task.getLengthOfTask());

This code uses ProgressMonitor 's only constructor to create the monitor and initialize several arguments:

  • The first argument provides the parent component to the dialog popped up by the progress monitor.

  • The second argument is a string that describes the nature of the task being monitored . This string is displayed on the dialog. See The Progress Monitoring API (page 308) for details about this argument.

  • The third argument is another string that provides a changeable status note. The example uses an empty string to indicate that the dialog should make space for a changeable status note, but that the note is initially empty. If you provide null for this argument, the note is omitted from the dialog. The example updates the note each time the timer fires an action event. It updates the monitor's current value at the same time:

    progressMonitor.setNote(task.getMessage()); progressMonitor.setProgress(task.getCurrent());

  • The last two arguments provide the minimum and maximum values, respectively, for the progress bar displayed in the dialog.

After the example creates the progress monitor, it configures the monitor further:

progressMonitor.setProgress(0); progressMonitor.setMillisToDecideToPopup(2 * ONE_SECOND);

The first line sets the current position of the progress bar on the dialog. The second tells the progress monitor to wait two seconds before deciding whether to bring up a dialog. If, after two seconds, the progress monitor's progress is less than its maximum, the monitor will bring up the dialog.

By the simple fact that this example uses a progress monitor, it adds a feature that wasn't present in the version of the program that uses a progress bar: The user can cancel the task by clicking the Cancel button on the dialog. Here's the code in the example that checks to see if the user canceled the task or if the task exited normally:

if (progressMonitor.isCanceled() task.done()) { progressMonitor.close(); task.stop(); Toolkit.getDefaultToolkit().beep(); timer.stop(); startButton.setEnabled(true); }

Note that the progress monitor doesn't itself cancel the task. It provides the GUI and API to allow the program to do so easily.

Deciding Whether to Use a Progress Bar or a Progress Monitor

Use a progress bar if:

  • You want more control over the configuration of the progress bar. If you are working directly with a progress bar, you can set it to be indeterminate, make it display vertically, provide a string for it to display, register change listeners on it, and provide it with a bounded range model to control its minimum, maximum, and current values.

  • The program needs to display other components along with the progress bar.

  • You need more than one progress bar. With some tasks, you need to monitor more than one parameter. For example, an installation program might monitor disk space usage in addition to how many files have been successfully installed.

  • You need to reuse the progress bar. A progress bar can be reused; a progress monitor cannot. Once the progress monitor has decided to display a dialog (or not), it cannot do it again.

Use a progress monitor if:

  • You want an easy way to display progress in a dialog.

  • The running task is secondary and the user might not be interested in the progress of the task. A progress monitor provides a way for the user to dismiss the dialog while the task is still running.

  • You want an easy way for the task to be cancelled. A progress monitor provides a GUI for the user to cancel the task. All you have to do is call the progress monitor's isCanceled method to find out if the user pressed the Cancel button.

  • Your task displays a short message periodically while running. The progress monitor dialog provides the setNote method so that the task can provide further information about what it's doing. For example, an installation task might report the name of each file as it's installed.

  • The task might not take a long time to complete. You decide at what point a running task is taking long enough to warrant letting the user know about it. A progress monitor won't pop up a dialog if the task completes within the timeframe you set.

  • If you decide to use a progress monitor and the task you are monitoring is reading from an input stream, use the ProgressMonitorInputStream class.

The Progress Monitoring API

Tables 56 through 61 list the commonly used API for progress bars and progress monitors. Because JProgressBar is a subclass of JComponent , other methods you are likely to call on a JProgressBar are listed in The JComponent Class (page 53). Note that ProgressMonitor is a subclass of Object and is not a visual component. Also see the API documentation for the JProgressBar and ProgressMonitor classes at:

http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JProgressBar.html

http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/ProgressMonitor.html

Table 56. Creating the Progress Bar

Constructor

Purpose

JProgressBar() JProgressBar(int, int)

Create a horizontal progress bar. The no-argument constructor initializes the progress bar with a minimum and initial value of 0 and a maximum of 100. The constructor with two integer arguments specifies the minimum and maximum values.

JProgressBar(int) JProgressBar(int, int, int)

Create a progress bar with the specified orientation, which can be either JProgressBar.HORIZONTAL or JProgressBar.VERTICAL . The optional second and third arguments specify minimum and maximum values.

JProgressBar(BoundedRangeModel)

Create a horizontal progress bar with the specified range model.

Table 57. Setting or Getting the Progress Bar's Constraints/Values

Method

Purpose

void setValue(int) int getValue()

Set or get the current value of the progress bar. The value is constrained by the minimum and maximum values.

double getPercentComplete()

Get the percent complete for the progress bar.

void setMinimum(int) int getMinimum()

Set or get the minimum value of the progress bar.

void setMaximum(int) int getMaximum()

Set or get the maximum value of the progress bar.

void setModel(BoundedRangeModel) BoundedRangeModel getModel()

Set or get the model used by the progress bar. The model establishes the progress bar's constraints and values, so you can use it directly as an alternative to using the individual set/get methods listed above.

Table 58. Controlling the Progress Bar's Appearance

Method

Purpose

void setIndeterminate(boolean)

By specifying true, put the progress bar into indeterminate mode. Specifying false puts the progress bar back into its default, determinate mode. Introduced in 1.4.

void setOrientation(int) int getOrientation()

Set or get whether the progress bar is vertical or horizontal. Acceptable values are JProgressBar.VERTICAL or JProgressBar.HORIZONTAL .

void setBorderPainted(boolean) boolean isBorderPainted()

Set or get whether the progress bar has a border.

void setStringPainted(boolean) boolean isStringPainted()

Set or get whether the progress bar displays a percent string. By default, the value of the percent string is the value returned by getPercentComplete formatted as a percent. You can set the string to be displayed with setString .

void setString(String) String getString()

Set or get the percent string.

Table 59. Creating the Progress Monitor

Method or Constructor

Purpose

ProgressMonitor(Component, Object, String, int, int)

Create a progress monitor. The Component argument is the parent for the monitor's dialog. The Object argument is a message to put on the option pane within the dialog. The value of this object is typically a String . The String argument is a changeable status note. The final two int arguments set the minimum and maximum values, respectively, for the progress bar used in the dialog.

ProgressMonitor getProgressMonitor() (in ProgressMonitorInputStream )

Get a progress monitor that monitors reading from an input stream.

Table 60. Configuring the Progress Monitor

Method

Purpose

void setMinimum(int) int getMinimum()

Set or get the minimum value of the progress monitor. This value is used by the monitor to set up the progress bar in the dialog.

void setMaximum(int) int getMaximum()

Set or get the maximum value of the progress monitor. This value is used by the monitor to set up the progress bar in the dialog.

void setProgress(int)

Update the monitor's progress.

void setNote(String) String getNote()

Set or get the status note. This note is displayed on the dialog. To omit the status note from the dialog, provide null as the third argument to the monitor's constructor.

void setMillisToDecideToPopup(int) int getMillisToDecideToPopup()

Set or get the time after which the monitor should decide whether to pop up a dialog.

Table 61. Terminating the Progress Monitor

Method

Purpose

void close()

Close the progress monitor. This disposes of the dialog.

Boolean isCanceled()

Determine whether the user pressed the Cancel button.

Examples That Monitor Progress

The following examples use JProgressBar or ProgressMonitor .

Example

Where Described

Notes

ProgressBarDemo

This section and How to Use Timers (page 639)

Uses a basic progress bar to show progress on a task running in a separate thread.

ProgressBarDemo2

This section

Uses a basic progress bar to show progress on a task running in a separate thread.

ProgressMonitorDemo

This section

Modification of the previous example that uses a progress monitor instead of a progress bar.

 < Day Day Up > 

Категории