|
| |
If we want to separate out the work from the way it is invoked ("separation of
form vs. function"), we can create ourselves a worker thread which can perform the
necessary long-running work:
package workerThread;
/**
* This is an interface for a status responder.
*/
public interface StatusResponder
{
/**
* Called when the work has started
*/
public void started();
/**
* Called when all the work is complete
*/
public void done();
}
|
package workerThread;
/**
* This class is a worker thread which can be used to perform
* time-consuming work outside of the event dispatch thread.
*/
public class WorkerThread extends Thread
{
public WorkerThread(StatusResponder responder)
{
m_responder = responder;
}
/**
* The run method for the worker thread,
* where the work gets done.
*/
public void run()
{
m_responder.started();
try
{
Thread.sleep(10000); // represents a long operation of some kind.
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
m_responder.done();
}
private StatusResponder m_responder;
}
|
package workerThread;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
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.SwingConstants;
import javax.swing.SwingUtilities;
/**
* This is a frame which solves the problems associated with trying to
* do large amounts of work from within the Event Dispatch Thread.
* It uses a separate WorkerThread class.
*/
public class WorkerThreadExample extends JFrame
{
//Construct the applet
public WorkerThreadExample()
{
setTitle("Worker Thread Example");
setSize(200, 200);
Container contentPane = getContentPane();
contentPane.setLayout(new BorderLayout());
contentPane.add( new ButtonPanel(), BorderLayout.CENTER );
contentPane.add(m_progressLabel, BorderLayout.SOUTH);
}
class ButtonPanel extends JPanel implements ActionListener
{
ButtonPanel()
{
setBackground(Color.blue);
m_clickMeButton.addActionListener(this);
add(m_clickMeButton);
m_nowClickMeButton.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent ev)
{
ButtonPanel.this.setBackground(Color.yellow);
}
}
);
m_nowClickMeButton.setEnabled(false); // Disable for now
add(m_nowClickMeButton);
}
public void actionPerformed(ActionEvent ev)
{
setBackground(Color.red);
m_nowClickMeButton.setEnabled(true);
new WorkerThread( new Responder() ).start();
}
}
public static void main(String[] args)
{
WorkerThreadExample frame = new WorkerThreadExample();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
/**
* Inner responder class
*/
class Responder implements StatusResponder
{
public void started()
{
setStatus(false, "Started work...");
}
public void done()
{
setStatus(true, "Done!");
}
private void setStatus(final boolean enable, final String text)
{
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
m_progressLabel.setText(text);
m_clickMeButton.setEnabled(enable);
}
}
);
}
}
private JButton m_clickMeButton = new JButton("Click me!");
private JButton m_nowClickMeButton = new JButton("Now try to click me!");
private JLabel m_progressLabel = new JLabel(" ", SwingConstants.CENTER);
}
|
The only change we made was to move the work out into the separate WorkerThread's
run() method.
Note that we have provided a StatusResponder to
provide some user feedback.
Try It!
Here is an applet that invokes the above code when you click on its
"Start" button:
|
|
Again, after you've brought the JFrame up, try clicking on the "Click me!"
button, and see what actually happens! |
|