This document outlines a proposal for 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:
JProgressBar constructors)
will be determinate.
You will be able to 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.
"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.
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 currently performs
all painting for the progress bar will delegate 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'll add a new method to SwingUtilities:
public static Rectangle calculateInnerArea(Component c, Rectangle r)
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).
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 will still work,
but indeterminate mode will look 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.
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,
and
PropertyChangeHandler,
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.
javax.swing.JProgressBar:
public void setIndeterminate(boolean newValue) /** * Sets the indeterminate property of the progress bar. * An indeterminate progress bar continuously displays animation * indicating that an operation of unknown length is occurring. * By default, this property is false. * * See [PENDING: class description and Java Tutorial] * for examples of using indeterminate progress bars. * * @param newValue true if the progress bar * should change to indeterminate mode; * false if it should revert to normal * * @see #isIndeterminate * * @since 1.4 */ public boolean isIndeterminate() /** * Returns the value of the indeterminate property. * By default, the progress bar is determinate * and this method returns false. * * @return true if the progress bar is indeterminate; * otherwise, false * @see #setIndeterminate * * @since 1.4 */
In javax.swing.plaf.basic.BasicProgressBarUI:
protected void paintIndeterminate(Graphics g, JComponent c)
/**
* All-purpose paint method that should do the right thing for almost all
* bouncing-box indeterminate progress bars.
* You can customize the painting by setting a few values in
* the defaults table.
* Naturally, override this if you are making a barber-pole,
* circular, or semi-circular progress bar.
*
* @see #paintDeterminate
*
* @since 1.4
*/
protected void paintDeterminate(Graphics g, JComponent c)
[contains the same code and comment as in the 1.3 paint method]:
/**
* All-purpose paint method that should do the right thing for most
* linear, determinate progress bars. By setting a few values in
* the defaults
* table, things should work just fine to paint your progress bar.
* Naturally, override this if you are making a circular or
* semi-circular progress bar.
*
* @see #paintIndeterminate
*
* @since 1.4
*/
protected int getAnimationIndex()
/**
* Gets the index of the current animation frame.
*
* @since 1.4
*/
protected void setAnimationIndex(int newValue)
/**
* Sets the index of the current animation frame
* to the specified value.
*
* @since 1.4
*/
protected void incrementAnimationIndex()
/**
* Increments the index of the current animation frame,
* making sure that the value is not too large.
* This method is invoked by the default animation thread.
* If the value would be too large,
* this method sets the index to 0.
* Subclasses often need to override this method
* to ensure that the index does not go over
* the number of frames needed for the particular
* progress bar instance.
*
* @since 1.4
*/
protected void startAnimationTimer()
/**
* Starts the animation thread, creating and initializing
* it if necessary. This method is invoked when
* the progress bar changes to
* indeterminate mode.
* If you implement your own animation thread,
* you must override this method.
*
* @since 1.4
*/
protected void stopAnimationTimer()
/**
* Stops the animation thread. This method is invoked when
* the progress bar changes from
* indeterminate to determinate mode
* and when this UI is uninstalled.
* If you implement your own animation thread,
* you must override this method.
*
* @since 1.4
*/
protected Rectangle getBox(Rectangle r)
/**
* Stores in r the position and size of
* the bouncing box that would be painted for the current animation index;
* returns r.
* Subclasses that add to the painting performed
* in this class's implementation of paintIndeterminate --
* to draw an outline around the bouncing box, for example --
* can use this method to get the location of the bouncing
* box that was just painted.
* By overriding this method,
* you have complete control over the size and position
* of the bouncing box,
* without having to reimplement paintIndeterminate.
*
* @param r the Rectangle instance to be modified;
* may be null
* @return the passed-in rectangle (if non-null);
* otherwise, returns a new rectangle
*/