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
makes the wait cursor 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));
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. 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 this:
Swing provides three classes to help you use progress bars:
JProgressBar
ProgressMonitor
ProgressMonitorInputStream
getProgressMonitor
and configure it as described in
How to Use Progress Monitors.
ProgressBarDemo.java
that creates and sets up the progress bar:
//Where member variables are declared: JProgressBar progressBar; ... //Where the GUI is constructed: progressBar = new JProgressBar(0, task.getLengthOfTask()); progressBar.setValue(0); progressBar.setStringPainted(true);
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!");
When the user clicks Start, an instance of the inner class
Task
is created and executed.
public void actionPerformed(ActionEvent evt) { startButton.setEnabled(false); setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); done = false; task = new Task(); task.addPropertyChangeListener(this); task.execute(); }
Task
is a
subclass of
javax.swing.SwingWorker
. The Task
instance does three important things for
ProgressBarDemo
:
doInBackground
in a
separate thread. This is where the long-running task is actually
executed. Using a background thread instead of the
event-dispatching thread prevents the user interface from freezing
while the task is running.
done
method in the event-dispatching thread.
progress
, that is updated to indicate the progress of
the task. The propertyChange
method is invoked each
time progress
changes.
SwingWorker
.
The background task in ProgressBarDemo
simulates a real
task by reporting random amounts of progress at random intervals. The
propertyChange
method responds to changes in the the
task's progress
property by updating the progress bar:
public void propertyChange(PropertyChangeEvent evt) { if (!done) { int progress = task.getProgress(); progressBar.setValue(progress); taskOutput.append(String.format( "Completed %d%% of task.\n", progress)); }
When the background task is complete, the task's done
method resets the progress bar:
public void done() { //Tell progress listener to stop updating progress bar. done = true; Toolkit.getDefaultToolkit().beep(); startButton.setEnabled(true); setCursor(null); //turn off the wait cursor progressBar.setValue(progressBar.getMinimum()); taskOutput.append("Done!\n"); }
done
method sets the done
field to true
, preventing propertyChange
from making further updates to the progress bar. This is necessary
because the final updates to the progress
property may
occcur after done
is invoked.
ProgressBarDemo2
indeterminate mode is set until
actual progress begins:
public void propertyChange(PropertyChangeEvent evt) { if (!done) { int progress = task.getProgress(); if (progress == 0) { progressBar.setIndeterminate(true); taskOutput.append("No progress yet\n"); } else { progressBar.setIndeterminate(false); progressBar.setString(null); progressBar.setValue(progress); taskOutput.append(String.format( "Completed %d%% of task.\n", progress)); } } }
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 progress
event 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.
Here's the statement that creates the progress monitor:
progressMonitor = new ProgressMonitor(ProgressMonitorDemo.this, "Running a Long Task", "", 0, task.getLengthOfTask());
ProgressMonitor
's only constructor
to create the monitor and
initialize several arguments:
null
for this argument,
the note is omitted from the dialog.
The example updates the note
each time the progress
property changes.
It updates the monitor's current value at the same time:
int progress = task.getProgress(); String message = String.format("Completed %d%%.\n", progress); progressMonitor.setNote(message); progressMonitor.setProgress(progress); taskOutput.append(message);
setMillisToDecidedToPopup
. To adjust the minimum progress
time required for a dialog to appear, invoke
setMillisToPopup
.
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.isDone()) { progressMonitor.close(); Toolkit.getDefaultToolkit().beep(); if (progressMonitor.isCanceled()) { task.cancel(true); taskOutput.append("Task canceled.\n"); } else { taskOutput.append("Task completed.\n"); } startButton.setEnabled(true); }
Use a progress monitor if:
isCanceled
method to find out
if the user pressed the Cancel button.
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.
ProgressMonitorInputStream
class.
JProgressBar
is a subclass of
JComponent
,
other methods you are likely to call on a
JProgressBar
are listed in
The JComponent Class.
Note that
ProgressMonitor
is a subclass of Object
and is not a visual component.
The API for monitoring progress falls into these categories:
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. |
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. |
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.
|
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. |
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 )
|
Gets a progress monitor that monitors reading from an input stream. |
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 popup a dialog. |
Method | Purpose |
---|---|
void close() | Close the progress monitor. This disposes of the dialog. |
boolean isCanceled() | Determine whether the user pressed the Cancel button. |
JProgressBar
or
ProgressMonitor
.
Example | Where Described | Notes |
---|---|---|
ProgressBarDemo
|
This section | 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. |