Enviado para você por Penha através do Google Reader:
by Jennie Hall
In this tip, you'll learn how to use Swing's progress indicator support to monitor and report on the progress of long-running operations. It is a good practice to keep users informed as they interact with an application; one way to do this is with a progress bar. A progress bar is an animated image that indicates the degree of completion of a given task. The animation typically looks like a rectangular bar that fills in as the task becomes more complete.
Swing's Progress Monitoring API consists of three classes that enable the use of progress bars. JProgressBar
subclasses JComponent
and is a graphical component that illustrates the progress of an operation. It can be embedded within other graphical components. ProgressMonitor
subclasses Object
and is not itself a graphical component. It monitors a task and pops a dialog box with a progress bar in it. ProgressMonitorInputStream
is a stream filter with an associated progress monitor. As the stream is read, the progress monitor automatically receives updates on the number of bytes read and displays the percentage of work completed in its dialog box.
The Java Tutorial provides some good rules of thumb that help to determine the appropriate class to use in a given situation. For example, use JProgressBar
when you need more than one progress bar or you would like more control over the configuration of the progress bar. If you need a convenient way to cancel the monitored task or to allow the user to dismiss the dialog box while continuing to run the task in the background, ProgressMonitor
provides for this. ProgressMonitor
also features a modifiable status note in its dialog box that can be updated periodically by your application. The sample application for this tip uses ProgressMonitor
.
The Sample Application
The sample application copies files located in a source directory (in) to a destination directory (out). It has a Swing GUI that allows the user to launch the copy operation by clicking the Copy Files button as shown in Figure 1.
Figure 1: Sample Application
Upon the launch of the copy operation, the application creates a progress monitor that keeps track of the amount of work completed and displays this information in a dialog containing a progress bar. The application also writes output regarding the progress of the operation to the console as shown in Figure 2.
Figure 2: Dialog containing progress bar
As shown above, the GUI displays the number of kilobytes copied, percentage of job completion, and file name of the file currently being copied. The user may cancel the operation at any time by clicking the Cancel button. After the copy operation completes, the GUI appears as shown in Figure 3:
Stepping Through the Sample Application
The sample application consists of a class, ProgressMonitorExample
, that extends javax.swing.JPanel
and implements java.awt.event.ActionListener
and java.beans.PropertyChangeListener
. ProgressMonitorExample
's main()
method tells the event dispatch thread to schedule the execution of a Runnable
that creates the application GUI:
public static void main(String[] args) { // tell the event dispatch thread to schedule the execution // of this Runnable (which will create the example app GUI) // for a later time SwingUtilities.invokeLater(new Runnable() { public void run() { // create example app window JFrame frame = new JFrame("Progress Monitor Example"); // application will exit on close frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); // create example app content pane // ProgressMonitorExample constructor does additional GUI setup JComponent contentPane = new ProgressMonitorExample(); contentPane.setOpaque(true); frame.setContentPane(contentPane); ...
ProgressMonitorExample
contains an inner class, CopyFiles
, that extends javax.swing.SwingWorker
. When the user clicks the Copy Files button, ProgressMonitorExample
's actionPerformed()
method receives the event, creates a new ProgressMonitor
, and starts the file-copying operation on a background thread. Here's the code that creates the ProgressMonitor
:
public void actionPerformed(ActionEvent event) { // make sure there are files to copy srcDir = new File("in"); if (srcDir.exists() && (srcDir.listFiles() != null && srcDir.listFiles().length > 0)) { // create the progress monitor progressMonitor = new ProgressMonitor(ProgressMonitorExample.this, "Operation in progress...", "", 0, 100); progressMonitor.setProgress(0); ...
ProgressMonitor
has a single constructor. The first argument is the parent component to the progress monitor's dialog box. The second argument, of type Object
, is displayed on the dialog box. It should be a string, icon, or component. This example supplies the constructor with a string that lets the user know that the requested operation is underway. The third argument is an optional status note that also appears on the dialog box. This status note can be updated periodically as the monitored task runs. Set this value to null if no status note is necessary. The fourth and fifth arguments are the minimum and maximum values for the progress bar in the progress monitor dialog box.
The code below, also excerpted from actionPerformed()
, creates a new instance of CopyFiles
, adds ProgressMonitorExample
as a property change listener on the instance, and executes the instance:
// schedule the copy files operation for execution on a background thread operation = new CopyFiles(); // add ProgressMonitorExample as a listener on CopyFiles; // we're specifically interested in the progress property operation.addPropertyChangeListener(this); operation.execute(); // we're running our operation; disable copy button copyButton.setEnabled(false);
CopyFiles
subclasses SwingWorker
, so the call to inherited method execute()
schedules CopyFiles
for execution on a background thread and returns immediately. Time-consuming activities should always run on a background thread rather than the event dispatch thread. This way, the GUI remains responsive.
Although the file-copying operation has begun, the progress monitor dialog box doesn't pop up right away. By default, ProgressMonitor
waits for 500 ms before making a decision on whether or not to show the dialog box at all. After this time period has elapsed, if ProgressMonitor
determines that the monitored operation has already completed or is likely to complete before the dialog box can be displayed, ProgressMonitor
does not pop the dialog box. ProgressMonitor
's method setMillisToDecideToPopup()
controls this setting. setMillisToPopup()
sets the estimated amount of time it will take the dialog box to appear; the default value for this property is 2 seconds.
The real work of copying the files occurs in doInBackground()
, an abstract method on SwingWorker
that CopyFiles
overrides. Here's a partial listing:
@Override public Void doInBackground() { int progress = 0; // initialize bound property progress (inherited from SwingWorker) setProgress(0); // get the files to be copied from the source directory File[] files = srcDir.listFiles(); // determine the scope of the task totalBytes = calcTotalBytes(files); while (progress < 100 && !isCancelled()) { // copy the files to the destination directory for (File f : files) { File destFile = new File(destDir, f.getName()); long previousLen = 0; try { InputStream in = new FileInputStream(f); OutputStream out = new FileOutputStream(destFile); byte[] buf = new byte[1024]; int counter = 0; int len; while ((len = in.read(buf)) > 0) { out.write(buf, 0, len); counter += len; bytesCopied += (destFile.length() - previousLen); previousLen = destFile.length(); if (counter > PROGRESS_CHECKPOINT || bytesCopied == totalBytes) { // get % complete for the task progress = (int)((100 * bytesCopied) / totalBytes); // capture the filename of the file currently being copied fileName = f.getName(); counter = 0; // set new progress value on bound property // and fire property change event setProgress(progress); } } in.close(); out.close(); } catch (IOException e) { // ignore } ...
doInBackground()
gets any files located in the in directory and copies them one by one to the out directory. Each time a specified number of bytes have been copied, the application calculates what percentage of the total number of bytes has been copied so far and captures the filename of the file currently being copied. The application then updates the bound property progress with the calculated percentage, firing a property change event in the process.
ProgressMonitorExample
's propertyChange()
method extracts the progress value from the property change event. It then updates the progress monitor by calling its setProgress()
and passing the progress value. propertyChange()
also updates the progress monitor's status note with the number of kilobytes copied so far, the percentage of work completed, and the filename of the file currently being copied. Here's the code:
public void propertyChange(PropertyChangeEvent event) { // get the progress information from the event // and set it on the progress monitor if (event.getPropertyName().equals("progress")) { int progress = ((Integer)event.getNewValue()).intValue(); progressMonitor.setProgress(progress); String progressNote = operation.getKiloBytesCopied() + " of " + operation.getTotalKiloBytes() + " kb copied; job " + progress + "% complete."; String fileNameNote = "Now copying " + operation.getFileName(); // update the progress monitor's status note and // additionally append the note to the console if (progress < 100) { progressMonitor.setNote(progressNote + " " + fileNameNote); console.append(progressNote + "\n" + fileNameNote + "\n"); } else { progressMonitor.setNote(progressNote); console.append(progressNote + "\n"); } // if the operation is finished or has been canceled by // the user, take appropriate action if (progressMonitor.isCanceled() || operation.isDone()) { if (progressMonitor.isCanceled()) { operation.cancel(true); console.append("Copy operation canceled.\n"); } else { console.append("Copy operation completed.\n"); } copyButton.setEnabled(true); } } }
Notice that ProgressMonitor
provides a convenient way to determine if the dialog has been canceled by the user. The sample application responds to a user cancellation by terminating the monitored activity, but in other situations it might be appropriate to allow the user to dismiss the dialog box while the activity continues to run in the background. When its background operation is finished, CopyFiles
sets its own state to done and invokes the done()
method in the event dispatch thread. The sample application's done()
method simply resets the application.
Running the Sample Application
To run the sample application, download the sample code and unzip it. The sample application assumes that there are files to copy in the in directory located under the project root, so add some (preferably large) files of your choice to this directory. Launch NetBeans and select File -> Open Project. In the Open Project dialog box, navigate to the directory where you unzipped the sample code and select the folder progressMonitorExample
. Select the Open as Main Project check box. Click Open Project Folder. Right-click the progressMonitorExample
project and select Build, then right-click the project again and select Run.
References and Resources
Sample code for this tip
The Java Tutorial
About the Author
Jennie Hall is a lead developer working in the financial sector.
Coisas que você pode fazer a partir daqui:
- Inscrever-se no Core Java Technologies Tech Tips usando o Google Reader
- Comece a usar o Google Reader para manter-se facilmente atualizado com todos os seus sites favoritos
0 comentários:
Postar um comentário