Progress Indicators


In the following sections, we discuss three classes for indicating the progress of a slow activity. A JProgressBar is a Swing component that indicates progress. A ProgressMonitor is a dialog box that contains a progress bar. A ProgressMonitorInputStream displays a progress monitor dialog box while the stream is read.

Progress Bars

A progress bar is a simple componentjust a rectangle that is partially filled with color to indicate the progress of an operation. By default, progress is indicated by a string "n%". You can see a progress bar in the bottom right of Figure 6-41.

Figure 6-41. A progress bar


You construct a progress bar much as you construct a slider, by supplying the minimum and maximum value and an optional orientation:

 progressBar = new JProgressBar(0, 1000); progressBar = new JProgressBar(SwingConstants.VERTICAL, 0, 1000); 

You can also set the minimum and maximum with the setMinimum and setMaximum methods.

Unlike a slider, the progress bar cannot be adjusted by the user. Your program needs to call setValue to update it.

If you call

 progressBar.setStringPainted(true); 

the progress bar computes the completion percentage and displays a string "n%". If you want to show a different string, you can supply it with the setString method:

 if (progressBar.getValue() > 900)    progressBar.setString("Almost Done"); 

The program in Example 6-16 shows a progress bar that monitors a simulated time-consuming activity.

The SimulatedActivity class implements a thread that increments a value current 10 times per second. When it reaches a target value, the thread finishes. To terminate the thread before it has reached its target, you interrupt it.

 class SimulatedActivity implements Runnable {    . . .    public void run()    {       try       {          while (current < target && !interrupted())          {             Thread.sleep(100);             current++;          }       }       catch (InterruptedException e)       {       }    }    int current;    int target; } 

When you click the Start button, a new SimulatedActivity tHRead is started. Updating the progress bar would appear to be an easy matterthe simulated activity thread calls the setValue method. But that is not thread safe: Recall that you should call Swing methods only from the event dispatch thread. In practice, the apparent approach is also unrealistic: In general, a worker thread is not aware of the existence of the progress bar. Instead, the example program shows how to launch a timer that periodically polls the thread for a progress status and updates the progress bar.

CAUTION

Remember that a worker thread cannot set the progress bar value directly. A worker thread must use the SwingUtilities.invokeLater method to set the progress bar value in the event dispatch thread.


Recall that a Swing timer calls the actionPerformed method of its listeners and that these calls occur in the event dispatch thread. That means it is safe to update Swing components in the timer callback. Here is the timer callback from the example program. The current value of the simulated activity is displayed both in the text area and the progress bar. If the end of the simulation has been reached, the timer is stopped and the Start button is reenabled.

 public void actionPerformed(ActionEvent event) {    int current = activity.getCurrent();    // show progress    textArea.append(current + "\n");    progressBar.setValue(current);    // check if task is completed    if (current == activity.getTarget())    {       activityMonitor.stop();       startButton.setEnabled(true);    } } 

JDK 1.4 adds support for an indeterminate progress bar that shows an animation indicating some kind of progress, without giving an indication of the percentage of completion. That is the kind of progress bar that you see in your browserit indicates that the browser is waiting for the server and has no idea how long the wait may be. To display the "indeterminate wait" animation, call the setIndeterminate method.

Example 6-16 shows the full program code.

Example 6-16. ProgressBarTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.util.*;   4. import javax.swing.*;   5. import javax.swing.event.*;   6. import javax.swing.Timer;   7.   8. /**   9.    This program demonstrates the use of a progress bar  10.    to monitor the progress of a thread.  11. */  12. public class ProgressBarTest  13. {  14.    public static void main(String[] args)  15.    {  16.       JFrame frame = new ProgressBarFrame();  17.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  18.       frame.setVisible(true);  19.    }  20. }  21.  22. /**  23.    A frame that contains a button to launch a simulated activity,  24.    a progress bar, and a text area for the activity output.  25. */  26. class ProgressBarFrame extends JFrame  27. {  28.    public ProgressBarFrame()  29.    {  30.       setTitle("ProgressBarTest");  31.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  32.  33.       // this text area holds the activity output  34.       textArea = new JTextArea();  35.  36.       // set up panel with button and progress bar  37.  38.       JPanel panel = new JPanel();  39.       startButton = new JButton("Start");  40.       progressBar = new JProgressBar();  41.       progressBar.setStringPainted(true);  42.       panel.add(startButton);  43.       panel.add(progressBar);  44.  45.       checkBox = new JCheckBox("indeterminate");  46.       checkBox.addActionListener(new  47.          ActionListener()  48.          {  49.             public void actionPerformed(ActionEvent event)  50.             {  51.                progressBar.setIndeterminate(checkBox.isSelected());  52.             }  53.          });  54.       panel.add(checkBox);  55.       add(new JScrollPane(textArea), BorderLayout.CENTER);  56.       add(panel, BorderLayout.SOUTH);  57.  58.       // set up the button action  59.  60.       startButton.addActionListener(new  61.          ActionListener()  62.          {  63.             public void actionPerformed(ActionEvent event)  64.             {  65.                progressBar.setMaximum(1000);  66.                activity = new SimulatedActivity(1000);  67.                new Thread(activity).start();  68.                activityMonitor.start();  69.                startButton.setEnabled(false);  70.             }  71.          });  72.  73.  74.       // set up the timer action  75.  76.       activityMonitor = new Timer(500, new  77.          ActionListener()  78.          {  79.             public void actionPerformed(ActionEvent event)  80.             {  81.                int current = activity.getCurrent();  82.  83.                // show progress  84.                textArea.append(current + "\n");  85.                progressBar.setStringPainted(!progressBar.isIndeterminate());  86.                progressBar.setValue(current);  87.  88.                // check if task is completed  89.                if (current == activity.getTarget())  90.                {  91.                   activityMonitor.stop();  92.                   startButton.setEnabled(true);  93.                }  94.             }  95.          });  96.    }  97.  98.    private Timer activityMonitor;  99.    private JButton startButton; 100.    private JProgressBar progressBar; 101.    private JCheckBox checkBox; 102.    private JTextArea textArea; 103.    private SimulatedActivity activity; 104. 105.    public static final int DEFAULT_WIDTH = 400; 106.    public static final int DEFAULT_HEIGHT = 200; 107. } 108. 109. /** 110.    A simulated activity runnable. 111. */ 112. class SimulatedActivity implements Runnable 113. { 114.    /** 115.       Constructs the simulated activity thread object. The 116.       thread increments a counter from 0 to a given target. 117.       @param t the target value of the counter. 118.    */ 119.    public SimulatedActivity(int t) 120.    { 121.       current = 0; 122.       target = t; 123.    } 124. 125.    public int getTarget() 126.    { 127.       return target; 128.    } 129. 130.    public int getCurrent() 131.    { 132.       return current; 133.    } 134. 135.    public void run() 136.    { 137.       try 138.       { 139.          while (current < target) 140.          { 141.             Thread.sleep(100); 142.             current++; 143.          } 144.       } 145.       catch(InterruptedException e) 146.       { 147.       } 148.    } 149. 150.    private volatile int current; 151.    private int target; 152. } 

Progress Monitors

A progress bar is a simple component that can be placed inside a window. In contrast, a ProgressMonitor is a complete dialog box that contains a progress bar (see Figure 6-42). The dialog box contains a Cancel button. If you click it, the monitor dialog box is closed. In addition, your program can query whether the user has canceled the dialog box and terminate the monitored action. (Note that the class name does not start with a "J".)

Figure 6-42. A progress monitor dialog box


You construct a progress monitor by supplying the following:

  • The parent component over which the dialog box should pop up;

  • An object (which should be a string, icon, or component) that is displayed on the dialog box;

  • An optional note to display below the object;

  • The minimum and maximum values.

However, the progress monitor cannot measure progress or cancel an activity by itself. You still need to periodically set the progress value by calling the setProgress method. (This is the equivalent of the setValue method of the JProgressBar class.) As you update the progress value, you should also call the isCanceled method to see if the program user has clicked the Cancel button.

When the monitored activity has concluded, call the close method to dismiss the dialog box. You can reuse the same dialog box by calling start again.

The example program looks very similar to that of the preceding section. We still need to launch a timer to watch over the progress of the simulated activity and update the progress monitor. Here is the timer callback.

 public void actionPerformed(ActionEvent event) {    int current = activity.getCurrent();    // show progress    textArea.append(current + "\n");    progressDialog.setProgress(current);    // check if task is completed or canceled    if (current == activity.getTarget() || progressDialog.isCanceled())    {       activityMonitor.stop();       progressDialog.close();       activity.interrupt();       startButton.setEnabled(true);    } } 

Note that there are two conditions for termination. The activity might have completed, or the user might have canceled it. In each of these cases, we close down

  • the timer that monitored the activity;

  • the progress dialog box;

  • the activity itself (by interrupting the thread).

If you run the program in Example 6-17, you can observe an interesting feature of the progress monitor dialog box. The dialog box doesn't come up immediately. Instead, it waits for a short interval to see if the activity has already been completed or is likely to complete in less time than it would take for the dialog box to appear.

You control the timing as follows. Use the setMillisToDecideToPopup method to set the number of milliseconds to wait between the construction of the dialog object and the decision whether to show the pop-up at all. The default value is 500 milliseconds. The setMillisToPopup is your estimation of the time the dialog box needs to pop up. The Swing designers set this value to a default of 2 seconds. Clearly they were mindful of the fact that Swing dialogs don't always come up as snappily as we all would like. You should probably not touch this value.

Example 6-17 shows the progress monitor in action, again measuring the progress of a simulated activity. As you can see, the progress monitor is convenient to use and only requires that you periodically query the thread that you want to monitor.

Example 6-17. ProgressMonitorTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.util.*;   4. import javax.swing.*;   5. import javax.swing.event.*;   6. import javax.swing.Timer;   7.   8. /**   9.    A program to test a progress monitor dialog.  10. */  11. public class ProgressMonitorTest  12. {  13.    public static void main(String[] args)  14.    {  15.       JFrame frame = new ProgressMonitorFrame();  16.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  17.       frame.setVisible(true);  18.    }  19. }  20.  21. /**  22.    A frame that contains a button to launch a simulated activity  23.    and a text area for the activity output.  24. */  25. class ProgressMonitorFrame extends JFrame  26. {  27.    public ProgressMonitorFrame()  28.    {  29.       setTitle("ProgressMonitorTest");  30.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  31.  32.       // this text area holds the activity output  33.       textArea = new JTextArea();  34.  35.       // set up a button panel  36.       JPanel panel = new JPanel();  37.       startButton = new JButton("Start");  38.       panel.add(startButton);  39.  40.       add(new JScrollPane(textArea), BorderLayout.CENTER);  41.       add(panel, BorderLayout.SOUTH);  42.  43.       // set up the button action  44.  45.       startButton.addActionListener(new  46.          ActionListener()  47.          {  48.             public void actionPerformed(ActionEvent event)  49.             {  50.                // start activity  51.                activity = new SimulatedActivity(1000);  52.                activityThread = new Thread(activity);  53.                activityThread.start();  54.  55.                // launch progress dialog  56.                progressDialog = new ProgressMonitor(ProgressMonitorFrame.this,  57.                   "Waiting for Simulated Activity", null, 0, activity.getTarget());  58.  59.                // start timer  60.                activityMonitor.start();  61.  62.                startButton.setEnabled(false);  63.             }  64.          });  65.  66.       // set up the timer action  67.  68.       activityMonitor = new Timer(500, new  69.          ActionListener()  70.          {  71.             public void actionPerformed(ActionEvent event)  72.             {  73.                int current = activity.getCurrent();  74.  75.                // show progress  76.                textArea.append(current + "\n");  77.                progressDialog.setProgress(current);  78.  79.                // check if task is completed or canceled  80.                if (current == activity.getTarget() || progressDialog.isCanceled())  81.                {  82.                   activityMonitor.stop();  83.                   progressDialog.close();  84.                   activityThread.interrupt();  85.                   startButton.setEnabled(true);  86.                }  87.             }  88.          });  89.    }  90.  91.    private Timer activityMonitor;  92.    private JButton startButton;  93.    private ProgressMonitor progressDialog;  94.    private JTextArea textArea;  95.    private Thread activityThread;  96.    private SimulatedActivity activity;  97.  98.    public static final int DEFAULT_WIDTH = 300;  99.    public static final int DEFAULT_HEIGHT = 200; 100. } 101. 102. /** 103.    A simulated activity runnable. 104. */ 105. class SimulatedActivity implements Runnable 106. { 107.    /** 108.       Constructs the simulated activity thread object. The 109.       thread increments a counter from 0 to a given target. 110.       @param t the target value of the counter. 111.    */ 112.    public SimulatedActivity(int t) 113.    { 114.       current = 0; 115.       target = t; 116.    } 117. 118.    public int getTarget() 119.    { 120.       return target; 121.    } 122. 123.    public int getCurrent() 124.    { 125.       return current; 126.    } 127. 128.    public void run() 129.    { 130.       try 131.       { 132.          while (current < target) 133.          { 134.             Thread.sleep(100); 135.             current++; 136.          } 137.       } 138.       catch(InterruptedException e) 139.       { 140.       } 141.    } 142. 143.    private volatile int current; 144.    private int target; 145. } 

Monitoring the Progress of Input Streams

The Swing package contains a useful stream filter, ProgressMonitorInputStream, that automatically pops up a dialog box that monitors how much of the stream has been read.

This filter is extremely easy to use. You sandwich in a ProgressMonitorInputStream between your usual sequence of filtered streams. (See Volume 1, Chapter 12 for more information on streams.)

For example, suppose you read text from a file. You start out with a FileInputStream:

 FileInputStream in = new FileInputStream(f); 

Normally, you would convert in to an InputStreamReader:

 InputStreamReader reader = new InputStreamReader(in); 

However, to monitor the stream, first turn the file input stream into a stream with a progress monitor:

 ProgressMonitorInputStream progressIn = new ProgressMonitorInputStream(parent, caption, in); 

You supply the parent component, a caption, and, of course, the stream to monitor. The read method of the progress monitor stream simply passes along the bytes and updates the progress dialog box.

You now go on building your filter sequence:

 InputStreamReader reader = new InputStreamReader(progressIn); 

That's all there is to it. When the file is read, the progress monitor automatically pops up (see Figure 6-43). This is a very nice application of stream filtering.

Figure 6-43. A progress monitor for an input stream


CAUTION

The progress monitor stream uses the available method of the InputStream class to determine the total number of bytes in the stream. However, the available method only reports the number of bytes in the stream that are available without blocking. Progress monitors work well for files and HTTP URLs because their length is known in advance, but they don't work with all streams.


The program in Example 6-18 counts the lines in a file. If you read in a large file (such as "The Count of Monte Cristo" on the CD), then the progress dialog box pops up.

Note that the program doesn't use a very efficient way of filling up the text area. It would be faster to first read the file into a StringBuffer and then set the text of the text area to the string buffer contents. However, in this example program, we actually like this slow approachit gives you more time to admire the progress dialog box.

To avoid flicker, we do not display the text area while it is filling up.

Example 6-18. ProgressMonitorInputStreamTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.io.*;   4. import java.util.*;   5. import javax.swing.*;   6. import javax.swing.event.*;   7.   8. /**   9.    A program to test a progress monitor input stream.  10. */  11. public class ProgressMonitorInputStreamTest  12. {  13.    public static void main(String[] args)  14.    {  15.       JFrame frame = new TextFrame();  16.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  17.       frame.setVisible(true);  18.    }  19. }  20.  21. /**  22.    A frame with a menu to load a text file and a text area  23.    to display its contents. The text area is constructed  24.    when the file is loaded and set as the content pane of  25.    the frame when the loading is complete. That avoids flicker  26.    during loading.  27. */  28. class TextFrame extends JFrame  29. {  30.    public TextFrame()  31.    {  32.       setTitle("ProgressMonitorInputStreamTest");  33.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  34.  35.       // set up menu  36.  37.       JMenuBar menuBar = new JMenuBar();  38.       setJMenuBar(menuBar);  39.       JMenu fileMenu = new JMenu("File");  40.       menuBar.add(fileMenu);  41.       openItem = new JMenuItem("Open");  42.       openItem.addActionListener(new  43.          ActionListener()  44.          {  45.             public void actionPerformed(ActionEvent event)  46.             {  47.                try  48.                {  49.                   openFile();  50.                }  51.                catch(IOException exception)  52.                {  53.                   exception.printStackTrace();  54.                }  55.             }  56.          });  57.  58.       fileMenu.add(openItem);  59.       exitItem = new JMenuItem("Exit");  60.       exitItem.addActionListener(new  61.          ActionListener()  62.          {  63.             public void actionPerformed(ActionEvent event)  64.             {  65.                System.exit(0);  66.             }  67.          });  68.       fileMenu.add(exitItem);  69.    }  70.  71.    /**  72.       Prompts the user to select a file, loads the file into  73.       a text area, and sets it as the content pane of the frame.  74.    */  75.    public void openFile() throws IOException  76.    {  77.       JFileChooser chooser = new JFileChooser();  78.       chooser.setCurrentDirectory(new File("."));  79.       chooser.setFileFilter(  80.          new javax.swing.filechooser.FileFilter()  81.             {  82.                public boolean accept(File f)  83.                {  84.                   String fname = f.getName().toLowerCase();  85.                   return fname.endsWith(".txt") || f.isDirectory();  86.                }  87.                public String getDescription()  88.                {  89.                   return "Text Files";  90.                }  91.             });  92.  93.       int r = chooser.showOpenDialog(this);  94.       if (r != JFileChooser.APPROVE_OPTION) return;  95.       final File f = chooser.getSelectedFile();  96.  97.       // set up stream and reader filter sequence  98.  99.       FileInputStream fileIn = new FileInputStream(f); 100.       ProgressMonitorInputStream progressIn 101.          = new ProgressMonitorInputStream(this, "Reading " + f.getName(), fileIn); 102.       final Scanner in = new Scanner(progressIn); 103. 104.       // the monitored activity must be in a new thread. 105. 106.       Runnable readRunnable = new 107.          Runnable() 108.          { 109.             public void run() 110.             { 111.                final JTextArea textArea = new JTextArea(); 112. 113.                while (in.hasNextLine()) 114.                { 115.                   String line = in.nextLine(); 116.                   textArea.append(line); 117.                   textArea.append("\n"); 118.                } 119.                in.close(); 120. 121.                // set content pane in the event dispatch thread 122.                EventQueue.invokeLater(new 123.                   Runnable() 124.                   { 125.                      public void run() 126.                      { 127.                         setContentPane(new JScrollPane(textArea)); 128.                         validate(); 129.                      } 130.                   }); 131. 132.             } 133.          }; 134. 135.       Thread readThread = new Thread(readRunnable); 136.       readThread.start(); 137.    } 138. 139.    private JMenuItem openItem; 140.    private JMenuItem exitItem; 141. 142.    public static final int DEFAULT_WIDTH = 300; 143.    public static final int DEFAULT_HEIGHT = 200; 144. } 


 javax.swing.JProgressBar 1.2 

  • JProgressBar()

  • JProgressBar(int direction)

  • JProgressBar(int min, int max)

  • JProgressBar(int direction, int min, int max)

    construct a slider with the given direction, minimum, and maximum.

    Parameters:

    direction

    One of SwingConstants.HORIZONTAL or SwingConstants.VERTICAL. The default is horizontal

     

    min, max

    The minimum and maximum for the progress bar values. Defaults are 0 and 100


  • int getMinimum()

  • int getMaximum()

  • void setMinimum(int value)

  • void setMaximum(int value)

    get and set the minimum and maximum values.

  • int getValue()

  • void setValue(int value)

    get and set the current value.

  • String getString()

  • void setString(String s)

    get and set the string to be displayed in the progress bar. If the string is null, then a default string "n%" is displayed.

  • boolean isStringPainted()

  • void setStringPainted(boolean b)

    get and set the "string painted" property. If this property is true, then a string is painted on top of the progress bar. The default is false; no string is painted.

  • boolean isIndeterminate() 1.4

  • void setIndeterminate(boolean b) 1.4

    get and set the "indeterminate" property. If this property is TRue, then the progress bar becomes a block that moves backward and forward, indicating a wait of unknown duration. The default is false.


 javax.swing.ProgressMonitor 1.2 

  • ProgressMonitor(Component parent, Object message, String note, int min, int max)

    constructs a progress monitor dialog box.

    Parameters:

    parent

    The parent component over which this dialog box pops up

     

    message

    The message object to display in the dialog box

     

    note

    The optional string to display under the message. If this value is null, then no space is set aside for the note, and a later call to setNote has no effect

     

    min, max

    The minimum and maximum values of the progress bar


  • void setNote(String note)

    changes the note text.

  • void setProgress(int value)

    sets the progress bar value to the given value.

  • void close()

    closes this dialog box.

  • boolean isCanceled()

    returns true if the user canceled this dialog box.


 javax.swing.ProgressMonitorInputStream 1.2 

  • ProgressMonitorInputStream(Component parent, Object message, InputStream in)

    constructs an input stream filter with an associated progress monitor dialog box.

    Parameters:

    parent

    The parent component over which this dialog box pops up

     

    message

    The message object to display in the dialog box

     

    in

    The input stream that is being monitored




    Core JavaT 2 Volume II - Advanced Features
    Building an On Demand Computing Environment with IBM: How to Optimize Your Current Infrastructure for Today and Tomorrow (MaxFacts Guidebook series)
    ISBN: 193164411X
    EAN: 2147483647
    Year: 2003
    Pages: 156
    Authors: Jim Hoskins

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net