Header javaperspective.com
JavaPerspective.com  >   Advanced Tutorials  >   1. Advanced GUI Features  >   1.3. Progress bars

1.3. Progress bars
Last updated: 1 January 2013.

This tutorial will show you how to use progress bars in Java.

A progress bar is an animated component that informs the user about the progress of a task. There are 3 types of progress bars:

1.3.1. How to use determinate progress bars

Displaying a determinate progress bar within a GUI involves 3 steps:
The code below builds a frame containing a single component: a determinate progress bar displaying the progress of a task that starts running right after the frame is visible. The task is a simple loop from 0 to 100. At each iteration, an integer is incremented by one and the call Thread.sleep(30) allows you to see the progress bar's animation.

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public final class ProgressBars extends JFrame {

   
private JProgressBar myProgressBar;


   
// STEP 2 ---------------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------------------------
   
private class MyTask extends SwingWorker<Void, Void> {

         
public Void doInBackground(){
               
for(int i=0; i<=100; ++i){
                     
try{
                           
Thread.sleep(30);

                           
// Update the "progress" property (the value should always be between 0 and 100)
                           
setProgress(i);
                     
}
                     
catch(InterruptedException e){}
                }
               
return null;
         
}

    }
   
// ------------------------------------------------------------------------------------------------------------


   
public ProgressBars(){
         
init();
          addComponents
();

          setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
          setVisible
(true);

         
// Create and run the task when the frame is visible
         
createAndRunTask();
   
}


   
private void addComponents() {
         
// Create the content pane
         
JPanel contentPane = new JPanel();


         
// STEP 1 ----------------------------------------------------------------------------------------------
          // -------------------------------------------------------------------------------------------------------
          // Set up the progress bar
         
myProgressBar = new JProgressBar(0, 100);
          myProgressBar.setValue
(0);
          myProgressBar.setStringPainted
(true);

         
// Add the progress bar to the content pane
         
contentPane.add(myProgressBar);
         
// -------------------------------------------------------------------------------------------------------


          // Add the content pane to the JFrame
         
add(contentPane);
   
}


   
// STEP 3 ---------------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------------------------
   
private void createAndRunTask(){
         
// Create the SwingWorker subclass instance
         
MyTask myTask = new MyTask();

         
// Register a PropertyChangeListener on it
         
myTask.addPropertyChangeListener(new PropertyChangeListener() {
               
public void propertyChange(PropertyChangeEvent e) {
                     
if(e.getPropertyName().equals("progress")){
                           
int progress = (Integer) e.getNewValue();

                           
// Update the progress bar's value with the value of the progress property.
                           
myProgressBar.setValue(progress);
                     
}
                }
          })
;

          // Call the method execute to run the task in a background thread
          myTask.execute
();
   
}
   
// -------------------------------------------------------------------------------------------------------------


   
private void init() {
         
try{
               
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
         
}
         
catch(Exception e){
               
e.printStackTrace();
         
}

         
setTitle("ProgressBars");
          setSize
(250, 100);
          setLocationRelativeTo
(null);
   
}


   
public static void main(String[] args){
         
SwingUtilities.invokeLater(new Runnable() {
               
public void run() {
                     
new ProgressBars();
               
}
          })
;
   
}
}

After creating the JProgressBar instance at step 1, the call myProgressBar.setValue(0) sets the initial value of the progress bar to 0. Next, the call myProgressBar.setStringPainted(true) displays the percentage indicating the task's amount of progress.

At step 2, I only implemented the abstract method doInBackground which runs the task in a background thread. However, you might be interested in overriding the method done as well. The method done is executed after the method doInBackground has returned, that is, when the task is completed or has been cancelled by a call to the method cancel(boolean mayInterruptIfRunning). Typically, the code in the method done acts upon the GUI to update certain components. For example, if you want to reuse the progress bar, you can reset its value in the method done as shown below:

// STEP 2 ---------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------
private class MyTask extends SwingWorker<Void, Void> {

   
public Void doInBackground(){
         
for(int i=0; i<=100; ++i){
               
try{
                     
Thread.sleep(30);

                     
// Update the "progress" property (the value should always be between 0 and 100)
                     
setProgress(i);
               
}
               
catch(InterruptedException e){}
          }
         
return null;
   
}

   
public void done(){
         
// Reset the value of the progress bar
         
myProgressBar.setValue(myProgressBar.getMinimum());
   
}

}
// ------------------------------------------------------------------------------------------------------------


Why use the class SwingWorker?

As you might already know, all the drawing and event handling in Swing is done by a single thread: the event dispatching thread. If the event dispatching thread is performing a complex or time consuming task, the GUI freezes and does not respond to button clicks or any other handled events until the task is completed.

To avoid freezing the GUI while a lengthy task is running, you must make use of the abstract class SwingWorker which allows you to run a task in a background thread and still execute code on the event dispatching thread to update the GUI while the task is running and also after its completion.


1.3.2. How to use indeterminate progress bars

An indeterminate progress bar is an instance of the class JProgressBar whose indeterminate property has been set to true by a call to the method setIndeterminate(boolean newValue). As I said earlier, the length of a task may be unknown when the task starts. While calculating the task's length, you can use a progress bar in indeterminate mode and switch to determinate mode when you finish calculating the task's length.

The following code is a modified version of the class ProgressBars I used in the previous section. In this new version, at step 1, the indeterminate property of the progress bar is set to true.

At step 2, the task takes 3 seconds to calculate its length. During that time, the value of the task's progress property is 0. When the task has finished calculating its length, the progress property starts to be incremented.

At step 3, the progress bar is in indeterminate mode as long as the task's length is unknown, that is, as long as the value of the progress property is 0. When the progress property gets positive, the progress bar switches to determinate mode.

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public final class ProgressBars extends JFrame {

   
private JProgressBar myProgressBar;


   
// STEP 2 ---------------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------------------------
   
private class MyTask extends SwingWorker<Void, Void> {

         
public Void doInBackground(){
               
// Take 3 seconds to calculate the task's length (the call "Thread.sleep(3000)" simulates the calculation)
               
try{
                     
Thread.sleep(3000);
               
}
               
catch(InterruptedException e){}


               
// Start incrementing the progress property when the task's length is known
               
for(int i=0; i<=100; ++i){
                     
try{
                           
Thread.sleep(30);

                           
// Update the "progress" property (the value should always be between 0 and 100)
                           
setProgress(i);
                     
}
                     
catch(InterruptedException e){}
                }
               
return null;
         
}

    }
   
// ------------------------------------------------------------------------------------------------------------


   
public ProgressBars(){
         
init();
          addComponents
();

          setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
          setVisible
(true);

         
// Create and run the task when the frame is visible
         
createAndRunTask();
   
}


   
private void addComponents() {
         
// Create the content pane
         
JPanel contentPane = new JPanel();


         
// STEP 1 ----------------------------------------------------------------------------------------------
          // -------------------------------------------------------------------------------------------------------
          // Set up the progress bar
         
myProgressBar = new JProgressBar(0, 100);
          myProgressBar.setValue
(0);
          myProgressBar.setStringPainted
(true);
          myProgressBar.setIndeterminate
(true); // Set the indeterminate property to true
          myProgressBar.setString
(""); // Do not display the percentage

          // Add the progress bar to the content pane
         
contentPane.add(myProgressBar);
         
// -------------------------------------------------------------------------------------------------------


          // Add the content pane to the JFrame
         
add(contentPane);
   
}


   
// STEP 3 ---------------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------------------------
   
private void createAndRunTask(){
         
// Create the SwingWorker subclass instance
         
MyTask myTask = new MyTask();

         
// Register a PropertyChangeListener on it
         
myTask.addPropertyChangeListener(new PropertyChangeListener() {
               
public void propertyChange(PropertyChangeEvent e) {
                     
if(e.getPropertyName().equals("progress")){
                           
int progress = (Integer) e.getNewValue();

                           
if(progress == 0){
                                 
myProgressBar.setIndeterminate(true);
                           
}
                           
else{
                                 
myProgressBar.setIndeterminate(false);
                                  myProgressBar.setString
(null); // Display the percentage

                                  // Update the progress bar's value with the value of the progress property.
                                 
myProgressBar.setValue(progress);
                           
}
                      }
                }
          })
;

         
// Call the method execute to run the task in a background thread
         
myTask.execute();
   
}
   
// -------------------------------------------------------------------------------------------------------------


   
private void init() {
         
try{
               
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
         
}
         
catch(Exception e){
               
e.printStackTrace();
         
}

         
setTitle("ProgressBars");
          setSize
(250, 100);
          setLocationRelativeTo
(null);
   
}


   
public static void main(String[] args){
         
SwingUtilities.invokeLater(new Runnable() {
               
public void run() {
                     
new ProgressBars();
               
}
          })
;
   
}
}



1.3.3. How to use progress monitors

A progress monitor is an instance of the class ProgressMonitor. It has a single constructor that takes, among other arguments, a minimum and maximum values: ProgressMonitor(Component parentComponent, Object message, String note, int min, int max).

Unlike JProgressBar, ProgressMonitor is not a Swing component. Instead, it monitors the progress of a task and pops up a progress dialog after 500 milliseconds by default. You can change that value by calling the method setMillisToDecideToPopup(int millisToDecideToPopup). The dialog will be displayed only if the task is likely to take more than 2000 milliseconds by default. That value can also be changed by calling the method setMillisToPopup(int millisToPopup). Note that a progress monitor can be used only once (unlike instances of JProgressBar).

The class ProgressMonitor has a progress property which must be regularly updated by calling the method setProgress(int nv) while the task is running. In fact, the progress property and the maximum value you specified in the constructor are used to estimate the remaining time before termination.

To illustrate the use of progress monitors, I have modified the steps 1 and 3 in the class ProgressBars. I have also enlarged the frame (setSize(700, 400) in the method init) so that it remains visible when the progress dialog pops up. The code follows:

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JFrame;
import javax.swing.ProgressMonitor;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public final class ProgressBars extends JFrame {

   
private ProgressMonitor myProgressMonitor;


   
// STEP 2 ---------------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------------------------
   
private class MyTask extends SwingWorker<Void, Void> {

         
public Void doInBackground(){
               
for(int i=0; i<=100; ++i){
                     
try{
                           
Thread.sleep(30);

                           
// Update the "progress" property (the value should always be between 0 and 100)
                           
setProgress(i);
                     
}
                     
catch(InterruptedException e){}
                }
               
return null;
         
}

    }
   
// ------------------------------------------------------------------------------------------------------------


   
public ProgressBars(){
         
init();
          addComponents
();

          setDefaultCloseOperation
(JFrame.EXIT_ON_CLOSE);
          setVisible
(true);

         
// Create and run the task when the frame is visible
         
createAndRunTask();
   
}


   
private void addComponents() {
         
// STEP 1 ----------------------------------------------------------------------------------------------
          // Create the progress monitor
         
myProgressMonitor = new ProgressMonitor(this, "Task in progress", null, 0, 100);
   
}


   
// STEP 3 ---------------------------------------------------------------------------------------------------
    // ------------------------------------------------------------------------------------------------------------
   
private void createAndRunTask(){
         
// Create the SwingWorker subclass instance
         
MyTask myTask = new MyTask();

         
// Register a PropertyChangeListener on it
         
myTask.addPropertyChangeListener(new PropertyChangeListener() {
               
public void propertyChange(PropertyChangeEvent e) {
                     
if(e.getPropertyName().equals("progress")){
                           
int progress = (Integer) e.getNewValue();

                           
// Update the progress monitor's value with the value of the progress property.
                           
myProgressMonitor.setProgress(progress);

                           
// Update the progress monitor's note with a percentage
                           
myProgressMonitor.setNote(String.valueOf(progress) + "%");
                     
}
                }
          })
;

         
// Call the method execute to run the task in a background thread
         
myTask.execute();
   
}
   
// -------------------------------------------------------------------------------------------------------------


   
private void init() {
         
try{
               
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
         
}
         
catch(Exception e){
               
e.printStackTrace();
         
}

         
setTitle("ProgressBars");
          setSize
(700, 400);
          setLocationRelativeTo
(null);
   
}


   
public static void main(String[] args){
         
SwingUtilities.invokeLater(new Runnable() {
               
public void run() {
                     
new ProgressBars();
               
}
          })
;
   
}
}

Last but not least, when the user clicks the progress monitor's Cancel button, the task is not cancelled but the method isCanceled provided by the class ProgressMonitor returns true. For example, to interrupt the task when the user clicks the Cancel button, I can modify the code at step 2 like this:

// STEP 2 ---------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------------------
private class MyTask extends SwingWorker<Void, Void> {

   
public Void doInBackground(){
         
for(int i=0; i<=100; ++i){
               
try{
                     
Thread.sleep(30);

                     
// Update the "progress" property (the value should always be between 0 and 100)
                     
setProgress(i);

                     
// Interrupt the task if the user clicks the progress monitor's "Cancel" button
                     
if(myProgressMonitor.isCanceled())
                           
break;
               
}
               
catch(InterruptedException e){}
          }
         
return null;
   
}

}
// ------------------------------------------------------------------------------------------------------------

As you can see, I added a break statement to exit the loop and terminate the task if the user clicks the Cancel button. Instead, I could have called the method cancel(boolean mayInterruptIfRunning) provided by the class SwingWorker which attempts to cancel the ongoing task. However, a call to that method is not recommended for two reasons: Firstly, the call may fail for a number of reasons. Secondly, if the call succeeds, brutally interrupting a task may lead to data inconsistency if your code was writing information into a file or database, which is why you should always write appropriate code to interrupt tasks gracefully.


You are here :  JavaPerspective.com  >   Advanced Tutorials  >   1. Advanced GUI Features  >   1.3. Progress bars
Next tutorial :  JavaPerspective.com  >   Advanced Tutorials  >   1. Advanced GUI Features  >   1.4. Split panes

Copyright © 2013. JavaPerspective.com. All rights reserved.  ( Terms | Contact | About ) 
Java is a trademark of Oracle Corporation
Image 1 Image 2 Image 3 Image 4 Image 5 Image 6 Image 7