|
| |
Note that, for the following examples, I have taken the SwingWorker
class and "cleaned it up" somewhat. In particular, I have added
it to the package oldSwingWorker, because the use
of the default package is to be discouraged. If you want to take a look at
my version of the SwingWorker class, you can
download it from here.
Note that my version of the SwingWorker class
probably does not include some of the fixes that have been applied since I
downloaded it. You should plan on using the new version of SwingWorker
instead.
Here's an example of the use of my "old" version of the SwingWorker
class.
package oldSwingWorker;
/**
* This is an interface for a multi-step progress responder.
*/
public interface MultiStepProgress
{
public void stepComplete(int stepNo);
public void finished();
public void unexpectedAction(String actionName);
}
|
package oldSwingWorker;
/**
* This is a multi-step worker thread
*/
public class MultiStepWorker extends SwingWorker
{
public MultiStepWorker(MultiStepProgress responder)
{
m_responder = responder;
}
public int getTotalSteps()
{
return m_stepCount;
}
public Object construct()
{
try
{
for (int step = 1; step <= m_stepCount; step++)
{
if (Thread.interrupted())
{
throw new InterruptedException();
}
Thread.sleep(50); // Represents a step
m_responder.stepComplete(step);
}
}
catch (InterruptedException ie)
{
m_responder.unexpectedAction("Interrupted");
}
return null; // No interesting value computed in this case
}
public void finished()
{
m_responder.finished(); // Invoke the responder's finished method
}
private MultiStepProgress m_responder;
private static int m_stepCount = 500;
}
|
package oldSwingWorker;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
/**
* This is a frame which shows how we can provide visual responses from each of
* multiple steps of time-consuming work in a background worker thread.
*/
public class MultiStepExample extends JFrame
{
/**
* Constructor
*/
public MultiStepExample()
{
setTitle("Multi-step Work Example");
setSize(200, 200);
Container outerPanel = getContentPane();
outerPanel.setLayout( new BorderLayout() );
// Button panel goes to the top
outerPanel.add( new ButtonPanel(), BorderLayout.NORTH );
// Progress panel goes below it.
JPanel progressPanel = new JPanel( new BorderLayout() );
// Progress bar at top of progress panel
m_progress.setStringPainted(true);
m_progress.setBackground(Color.white);
m_progress.setForeground(Color.blue);
progressPanel.add( m_progress, BorderLayout.NORTH );
// Progress status label below it
m_statusLabel.setOpaque(true);
m_statusLabel.setBackground(Color.white);
m_statusLabel.setForeground(Color.black);
progressPanel.add( m_statusLabel, BorderLayout.SOUTH );
outerPanel.add( progressPanel, BorderLayout.CENTER );
pack();
}
private JProgressBar m_progress = new JProgressBar();
private JLabel m_statusLabel =
new JLabel("To start work, click on Start button", SwingConstants.CENTER);
class ButtonPanel extends JPanel implements ActionListener, MultiStepProgress
{
ButtonPanel()
{
m_startButton.addActionListener(this);
m_startButton.setBackground(Color.green); // For "GO"
m_startButton.setForeground(Color.black);
add(m_startButton);
m_cancelButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
m_workerThread.interrupt(); // Causes an interrupt of thread
}
}
);
m_cancelButton.setEnabled(false); // Disable until appropriate
m_cancelButton.setBackground(Color.red); // For "STOP"
m_cancelButton.setForeground(Color.black);
add(m_cancelButton);
}
public void actionPerformed(ActionEvent ev)
{
m_abnormal = false;
m_startButton.setEnabled(false); // Prevent multiple worker threads
m_cancelButton.setEnabled(true); // Enable cancellation
m_statusLabel.setBackground(Color.white);
m_statusLabel.setText("Working...");
m_workerThread = new MultiStepWorker(this);
m_progress.setMinimum( 0 );
m_progress.setMaximum( m_workerThread.getTotalSteps() );
m_workerThread.start();
}
public void stepComplete(final int stepNo)
{
Runnable runnable = new Runnable()
{
public void run()
{
m_progress.setValue(stepNo);
}
};
SwingUtilities.invokeLater(runnable);
}
public void finished()
{
if (!m_abnormal)
{
m_statusLabel.setText("Work complete.");
}
m_startButton.setEnabled(true);
m_cancelButton.setEnabled(false);
m_workerThread = null; // Release the thread reference
}
public void unexpectedAction(String actionName)
{
m_statusLabel.setBackground(Color.red);
m_statusLabel.setText(actionName);
m_abnormal = true;
}
private JButton m_startButton = new JButton("Start");
private JButton m_cancelButton = new JButton("Cancel");
private MultiStepWorker m_workerThread;
private boolean m_abnormal = false;
}
public static void main(String[] args)
{
EventQueue.invokeLater( new Runnable()
{
public void run()
{
MultiStepExample frame = new MultiStepExample();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
);
}
}
|
Notice:
- The use of an interface,
MultiStepProgress,
- The use of a
MultiStepWorker class, which
extends the SwingWorker class, and accepts an
instance of a class that implements the MultiStepProgress
interface..
- The fact that the
ButtonPanel implements the
MultiStepProgress interface, and a reference
to it is passed into the MultiStepWorker
thread.
This helps to separate the GUI details from the worker thread, and vice versa
-- separation of form and function, which is a very important design approach.
Try It!
Here is an applet that invokes the above code when you click on its
"Start" button
(It requires the use of Java 6.0, so be prepared for errors if your browser
plug-in is at a lower version number):
|
|
After you've brought up the JFrame, click on the
"Start" button, and see what actually happens! |
Results
The program should:
- Change the contents of the label to "Working..."
- Start visible progress in the progress bar
- Disable the Start button, and enable the Cancel button
You can:
- Let it continue until the end, in which case the label will change to say
"Work complete"
or:
- Click on the Cancel button, which will cause the work to stop prematurely;
the label will change to "Interrupted", and its background color
will change to red.
In either case, the Cancel button should be disabled, and the Start button
should be re-enabled.
|