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)