ProgressMonitorInputStream
I/O operations can be slow, especially when reading data from the network. User-centric programs should always give the user feedback, even if the feedback is no more significant than "No, I haven't frozen; I'm still running." For simple operations, an animated cursor like the Macintosh's spinning beach ball may be sufficient. For longer operations, you should display a progress bar that indicates how much of the operation has been accomplished and how much remains to be done, such as the one shown in Figure 6-1. ProgressMonitorInputStream is a unique filter stream hiding in the javax.swing package that displays progress bars that indicate how much of a stream has been read and how much remains to be read.
Figure 6-1. A Swing progress bar
ProgressMonitorInputStream is a FilterInputStream that you can chain to any other input stream in the usual way. If the data is read in less time than it normally takes for the user to notice a delay (as for a small file read from a disk), no dialog is shown. However, if the input begins to take a noticeable amount of time, Java pops up a progress bar that includes a cancel button. If the user presses that button, the current read( ) method throws an InterruptedIOException. Otherwise, input continues, and the progress bar is updated until the end of the stream is reached.
The primary method you need to be aware of in ProgressMonitorInputStream is the constructor:
public ProgressMonitorInputStream(Component parent, Object message, InputStream in)
The parent argument specifies the parent component of the progress monitor, though it may be null if no such component is available. The message argument is normally a String containing the message shown in the dialog. If some other type is used, its toString( ) method is invoked to provide the message. Finally, in is the underlying InputStream this stream is chained to. For example, this code fragment will use a progress monitor to keep the user updated about how far along it is while it's reading the file lotsofdata.txt:
File f = new File("lotsofdata.txt"); InputStream in = new FileInputStream(fin); ProgressMonitorInputStream pin = new ProgressMonitorInputStream( null, f.getName( ), in);
The only other new method you need to know about in this class is getProgressMonitor( ):
public ProgressMonitor getProgressMonitor( )
This returns a reference to the actual progress monitor so that you can adjust its behavior with the methods of the javax.swing.ProgressMonitor class. For instance, you can change the default time before the progress monitor is shown or the maximum and minimum values used for the monitor. You also use this object to tell the ProgressMonitor how much data is expected through the setMaximum( ) method. For instance, when reading a file, the length( ) method in the File class reveals how many bytes you expect to read. That would be the maximum for the progress bar. For example:
ProgressMonitor pm = pin.getProgressMonitor( ); pm.setMaximum(f.length( ));
Aside from getProgressMonitor( ), ProgressMonitorInputStream has the usual methods of any InputStream class: read( ), close( ), markSupported( ), etc. Progress monitor input streams support marking and resetting only if the underlying stream does.
You read from the stream just as you read from any other stream. If the process takes more than about half a second, and it looks like it will take more than two seconds, Java will pop up a ProgressMonitor showing the user just how much is done and how much remains to be done. You can adjust these times using the methods of the ProgressMonitor class, but the defaults for everything except the maximum value are generally reasonable.
Programs that read data from the network take even longer than programs that read from files. Example 6-5 is a complete program that reads data from a URL given on the command line and copies it to System.out. It uses a ProgressMonitor to keep the user alerted as to its progress. It uses the content-length HTTP header to determine how much data will be sent in order to set the maximum value for the progress bar.
Example 6-5. MonitoredSourceViewer
import java.net.*; import java.io.*; import javax.swing.*; public class MonitoredSourceViewer { public static void main (String[] args) { if (args.length > 0) { try { // Open the URLConnection for reading URL u = new URL(args[0]); URLConnection uc = u.openConnection( ); InputStream in = uc.getInputStream( ); // Chain a ProgressMonitorInputStream to the // URLConnection's InputStream ProgressMonitorInputStream pin = new ProgressMonitorInputStream(null, u.toString( ), in); // Set the maximum value of the ProgressMonitor ProgressMonitor pm = pin.getProgressMonitor( ); pm.setMaximum(uc.getContentLength( )); // Read the data for (int c = pin.read(); c != -1; c = pin.read( )) { System.out.print((char) c); } pin.close( ); } catch (MalformedURLException ex) { System.err.println(args[0] + " is not a parseable URL"); } catch (InterruptedIOException ex) { // User cancelled. Do nothing. } catch (IOException ex) { System.err.println(ex); } } // end if // Since we brought up a GUI, we have to explicitly exit here // rather than simply returning from the main( ) method. System.exit(0); } // end main } // end MonitoredSourceViewer |
Figure 6-1 is the screenshot of a progress monitor taken from this program. ProgressMonitorInputStream is a simple class that's very easy to program with and that will make users' experiences much more pleasant.