This document describes the changes we made to implement indeterminate progress bars -- GUI components that look similar to normal progress bars and, like normal progress bars, use animation to show that a time-consuming operation is occurring. Unlike normal progress bars, indeterminate progress bars do not show the degree of completeness of the operation. This document has the following sections:
By default, every progress bar (created using one of several
JProgressBar
constructors) is determinate. You may make any
JProgressBar
indeterminate using the setIndeterminate
method:
pb.setIndeterminate(true);
An indeterminate progress bar animates constantly. You can stop the animation and clear the progress bar by making the progress bar determinate and setting the current value to the minimum. For example:
pb.setValue(pb.getMinimum()); pb.setIndeterminate(false);
You can switch from determinate to indeterminate mode, and vice
versa, at any time. You can check whether the progress bar is
indeterminate using the isIndeterminate
method.
When a progress bar is indeterminate, it ignores its model (a
BoundedRangeModel
). However, the model should exist
and contain legal data, since L&Fs that haven't been updated
for indeterminate progress bars might use the model.
To give developers control over the progress bar's speed of animation, we've added two new UI defaults:
"ProgressBar.repaintInterval"
"ProgressBar.cycleTime"
The "ProgressBar.cycleTime"
UI default
lets look and feel implementers (as well as other
developers)specify how many milliseconds each animation cycle
takes. For example, a cycle time of 500 means that the
indeterminate-mode progress bar animation repeats twice per second.
The default drawing code uses this value --along with the repaint
interval, box length, and component inner area --to determine how
far to move the bouncing box each time it's drawn. This value must
be an even multiple of the repaint interval. For example, if the
repaint interval is 100, then valid cycle times are 200, 1000, and
1200, but not 100 or 500. If the user specifies an invalid cycle
time,the indeterminate progress bar code automatically increases
the cycle time to a suitable value.
To set the repaint interval and cycle time defaults:
UIManager.put("ProgressBar.repaintInterval", new Integer(250)); UIManager.put("ProgressBar.cycleTime", new Integer(6000));
To get the repaint interval and cycle time:
int interval = UIManager.getInt("ProgressBar.repaintInterval"); int totalTime = UIManager.getInt("ProgressBar.cycleTime");
In the basic, JLF, Motif, and Windows implementations provided by Sun, these defaults are checked when (and only when) the progress bar is switching into indeterminate mode.
To help programmers implement look-and-feel code for progress
bars,we have added the following API to the
BasicProgressBarUI
class.
Methods related to painting:
protected void paintIndeterminate(Graphics g, JComponent
c)
protected void paintDeterminate(Graphics g, JComponent
c)
protected Rectangle getBox(Rectangle)
Methods for setting and getting the index of the current frame of animation:
protected int getAnimationIndex()
protected void setAnimationIndex(int
newValue)
protected void incrementAnimationIndex()
Methods for starting and stopping custom animation threads:
protected void startAnimationTimer()
protected void stopAnimationTimer()
The paint
method that previously performed all
painting for the progress bar now delegates all its drawing to
either paintDeterminate
or
paintIndeterminate
, depending on the value of the
progress bar's indeterminate property. When the progress bar is in
indeterminate mode, the paint
method (and thus the
paintIndeterminate
method) executes every repaint
interval milliseconds.The paintIndeterminate
method should paint the progress bar to match the animation state,
which is specified by the getAnimationIndex
method.
The getBox
method allows implementations to
customize bouncing box painting. For example,
MetalProgressBarUI
invokes getBox
in its
paintIndeterminate
method to get the box drawn by the
superclass (BasicProgressBarUI
) and then to add an
outline to the box. By overriding the getBox
method,
an implementer gets full control over the bouncing box's size and
position without needing to reimplement
paintIndeterminate
.
If you override paintIndeterminate
or
getBox
, you might also have to override
incrementAnimationIndex
so that it cycles correctly
through the valid values. The first value (representing the first
drawing) is 0. By convention, the second is 1, the third is 2, and
so on. The last legal value is, by convention, the total number of
drawings in the animation cycle, minus one. To determine the total
number of drawings, you may need to take into account the repaint
interval and perhaps the component size. As the "by
convention" implies, you can implement the animation index to
have any meaning and values you wish, as long as zero indicates the
beginning of the animation cycle.
If you don't want to use the animation thread we provide, you
must override the two xxxAnimationTimer
methods. You can then provide your own implementation that
periodically increments the animation index and invokes
repaint
on the progress bar.
As a side effect of the progress bar work, we've added a new
method to SwingUtilities
:
public static Rectangle calculateInnerArea(Component c, Rectangle
r)
The calculateInnerArea
method is useful to all
classes that perform drawing. It returns the area in which the
component can draw -- that is, the rectangle (in the component's
coordinate system) that includes all of the component except its
border area (the component's insets).
Converting an existing L&F to indeterminate progress bars is
relatively straightforward. If the L&F's progress bar UI class
doesn't override paint
(or does but also invokes
super.paint
), then support for indeterminate progress
bars is automatic. WindowsProgressBarUI
,
MotifProgressBarUI
, and
MetalProgressBarUI
are in this lucky camp.
If the L&F's progress bar UI class is a subclass of
BasicProgressBarUI
and overrides paint
without invoking the superclass version, then determinate mode
still works, but indeterminate mode looks the same as determinate
mode.
Existing drawing code should be moved out of the
paint
method and into the new
paintDeterminate
method. Code for indeterminate
painting should go in the new paintIndeterminate
method. If at all possible, the paint
method should
not be overridden unless it invokes super.paint
. The
reason: the BasicProgressBarUI
implementation of the
paint
method may work with the default animation
thread to enhance performance and behavior.
The Mac look and feel (both the no-longer-maintained Sun version
and the Apple version) is an example of a look and feel that
overrides paint
without invoking the superclass
version.
If you already have a thread scheme for indeterminate painting,
you can continue to use that scheme by overriding
startAnimationTimer
and
stopAnimationTimer
. Or you can just delete your thread
code and use our scheme.
The BasicProgressBarUI
class contains most of our
implementation of indeterminate progress bars. Aside from the
drawing code, most of the code is in two private inner classes:
Animator
, which implements the animation thread,
andPropertyChangeHandler
, which listens for changes to
and from indeterminate mode.
The Animator
implements the default animation
thread, using the Swing Timer
class. An Animator
instance is created if necessary by
the BasicProgressBarUI.startAnimationTimer
method,
which the property handler invokes when the progress bar switches
to indeterminate mode. When the progress bar is indeterminate, the
Animator
timer fires an action event once every
repaint interval milliseconds. Animator
's
action event handler invokes incrementAnimationIndex
,
followed by repaint
(which causes
paintIndeterminate
to run). Repaint interval
is specified by the ProgressBar.repaintInterval
UI
default, which is checked by startAnimationTimer
.
The PropertyChangeHandler
registers itself as a
property listener on the progress bar. When it detects the
"indeterminate" property changing,the handler notes the
change and invokes either stopAnimationTimer
or
startAnimationTimer
.
public void
setIndeterminate(boolean newValue)public boolean
isIndeterminate()
In
javax.swing.plaf.basic.BasicProgressBarUI
:
protected void
paintIndeterminate(Graphics g, JComponent c)protected void
paintDeterminate(Graphics g, JComponent c)protected int
getAnimationIndex()protected void
setAnimationIndex(int newValue)protected void
incrementAnimationIndex()protected void
startAnimationTimer()protected void
stopAnimationTimer()protected Rectangle
getBox(Rectangle r)
In javax.swing.SwingUtilities
:
public static Rectangle
calculateInnerArea(JComponent c, Rectangle r)