|
Event-Driven Programs
An Introduction with Examples in Java |
|
Prof. David Bernstein |
| Computer Science Department |
| bernstdh@jmu.edu |
Using an Event Queue
Event Listening
Event Bubbling
contentPane.setLayout(null);
label.setBounds(50,50,500,100);
contentPane.add(label);
button.setBounds(450,300,100,50);
contentPane.add(button);
JFrame
main()
import java.util.*;
import javax.swing.*;
/**
* A simple example of a GUI application
*
* Note: This example contains a common mistake made by beginning
* programmers. Specifically, it manipulates GUI elements
* outside of the event dispatch thread
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BadRandomMessageApplication
{
// The pseudo-random number generator
private static Random rng = new Random();
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* The entry point of the application
*
* @param args The command-line arguments
*/
public static void main(String[] args) throws Exception
{
JFrame window;
JLabel label;
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Construct the "window"
window = new JFrame();
window.setSize(600,400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Get the container for all content
contentPane = (JPanel)window.getContentPane();
contentPane.setLayout(null);
// Add a component to the container
label = new JLabel(s, SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
// Make the "window" visible
window.setVisible(true);
}
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
}
import java.util.*;
import javax.swing.*;
/**
* A simple example of a GUI application
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BadRandomMessageSwingApplication
implements Runnable
{
// Attributes
private JLabel label;
// The pseudo-random number generator
private static Random rng = new Random();
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* The entry point of the application
*
* @param args The command-line arguments
*/
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeAndWait(
new BadRandomMessageSwingApplication());
}
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
/**
* The code to be executed in the event dispatch thread
* (required by Runnable)
*/
public void run()
{
JFrame window;
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Construct the "window"
window = new JFrame();
window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
window.setSize(600,400);
// Get the container for all content
contentPane = (JPanel)window.getContentPane();
contentPane.setLayout(null);
// Add a component to the container
label = new JLabel(s, SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
// Make the "window" visible
window.setVisible(true);
}
}
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
/**
* A simple example of a GUI application that responds
* to events (in this case, events generated by a button)
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BadInteractiveRandomMessageSwingApplication
implements ActionListener, Runnable
{
// Attributes
private JLabel label;
// The pseudo-random number generator
private static Random rng = new Random();
// String "constants"
private static final String CHANGE = "Change";
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* The entry point of the application
*
* @param args The command-line arguments
*/
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeAndWait(
new BadInteractiveRandomMessageSwingApplication());
}
/**
* Handle actionPerformed messages
* (required by ActionListener)
*
* @param event The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent event)
{
String actionCommand;
actionCommand = event.getActionCommand();
if (actionCommand.equals(CHANGE))
{
label.setText(createRandomMessage());
}
}
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
/**
* The code to be executed in the event dispatch thread
* (required by Runnable)
*/
public void run()
{
JButton button;
JFrame window;
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Construct the "window"
window = new JFrame();
window.setSize(600,400);
window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// Get the container for all content
contentPane = (JPanel)window.getContentPane();
contentPane.setLayout(null);
// Add the message component to the container
label = new JLabel(s, SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
// Add the button to the container
button = new JButton(CHANGE);
button.setBounds(450,300,100,50);
contentPane.add(button);
button.addActionListener(this);
// Make the "window" visible
window.setVisible(true);
}
}
import java.util.*;
import javax.swing.*;
/**
* A simple example of a GUI JApplet
*
* Note: This example contains a subtle mistake. Specifically, it
* manipulates GUI elements in the init() method which is
* NOT called in the event dispatch thread.
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BadRandomMessageJApplet
extends JApplet
{
// Attributes
private JLabel label;
// The pseudo-random number generator
private static Random rng = new Random();
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* Default Constructor
*/
public BadRandomMessageJApplet()
{
super();
}
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
/**
* Called to indicate that this JApplet has been loaded
*/
public void init()
{
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Get the container for all content
contentPane = (JPanel)getContentPane();
contentPane.setLayout(null);
// Add a component to the container
label = new JLabel(s, SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
}
}
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
/**
* A simple example of a GUI JApplet that responds
* to events (in this case, events generated by a button)
*
* Note: This example contains a subtle mistake. Specifically, it
* manipulates GUI elements in the init() method which is
* NOT called in the event dispatch thread.
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class BadInteractiveRandomMessageJApplet
extends JApplet
implements ActionListener
{
// Attributes
private JLabel label;
// The pseudo-random number generator
private static Random rng = new Random();
// String "constants"
private static final String CHANGE = "Change";
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* Default Constructor
*/
public BadInteractiveRandomMessageJApplet()
{
super();
}
/**
* Handle actionPerformed messages
* (required by ActionListener)
*
* @param event The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent event)
{
String actionCommand;
actionCommand = event.getActionCommand();
if (actionCommand.equals(CHANGE))
{
label.setText(createRandomMessage());
}
}
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
/**
* Called to indicate that this JApplet has been loaded
*/
public void init()
{
JButton button;
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Get the container for all content
contentPane = (JPanel)getContentPane();
contentPane.setLayout(null);
// Add a component to the container
label = new JLabel(s, SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
// Add the button to the container
button = new JButton(CHANGE);
button.setBounds(450,300,100,50);
button.addActionListener(this);
contentPane.add(button);
}
}
JApplication
class that mimics the programming interface and
life-cycle of the JApplet class
JApplication Class
/**
* The actual entry point into this JApplication
* (required by Runnable)
*
* This method is called in the event dispatch thread.
*/
public final void run()
{
constructMainWindow();
init();
mainWindow.setVisible(true);
}
mainWindow = new JFrame();
mainWindow.setTitle("James Madison University");
mainWindow.setResizable(false);
contentPane = (JPanel)mainWindow.getContentPane();
contentPane.setLayout(null);
contentPane.setDoubleBuffered(false);
/**
* This method is called just before the main window
* is first made visible
*/
public abstract void init();
JApplication
import java.util.*;
import javax.swing.*;
import app.JApplication;
public class BadRandomMessageJApplication
extends JApplication
{
// Attributes
private JLabel label;
// The pseudo-random number generator
private static Random rng = new Random();
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* The entry point of the application
*
* @param args The command-line arguments
*/
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeAndWait(
new BadRandomMessageJApplication(600,400));
}
/**
* Explicit Value Constructor
*/
public BadRandomMessageJApplication(int width, int height)
{
super(width, height);
}
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
/**
* Called to indicate that this JApplication has been loaded
*/
public void init()
{
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Get the container for all content
contentPane = (JPanel)getContentPane();
contentPane.setLayout(null);
// Add a component to the container
label = new JLabel(s,SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
}
}
JApplet and JApplication
with the same functionality they will have an enormous
amount of duplicate codeRootPaneContainer for a JApplet
has access to the start-up parameters whereas the
RootPaneContainer for a JApplication
(which is a JFrame) does not.
package app;
import javax.swing.*;
/**
* Requirements of a RootPaneContainer in a MultimediaApp
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public interface MultimediaRootPaneContainer
extends RootPaneContainer
{
/**
* Returns the value of the "named" parameter
*
* @param name The name/index of the parameter
*/
public abstract String getParameter(String name);
}
Correcting the Remaining Problems with the Decorator Pattern
package app;
import javax.swing.*;
/**
* The requirements of a multimedia Applet/application
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public interface MultimediaApp
{
/**
* Called to indicate that this MultimediaApp should destroy any
* resources that it has allocated
*/
public abstract void destroy();
/**
* Called to indicate that this MultimediaApp has been loaded
*/
public abstract void init();
/**
* Set the MultimediaRootPaneContainer for the MultimediaApp
*
* In most cases, the MultimediaRootPaneContainer will be either
* a MultimediaApplication or a MultimediaApplet
*
* @param container The RootPaneContainer for this MultimediaApp
*/
public abstract void setMultimediaRootPaneContainer(
MultimediaRootPaneContainer container);
/**
* Called to indicate that this MultimediaApp has been started
*/
public abstract void start();
/**
* Called to indicate that this MultimediaApp has been stopped
*/
public abstract void stop();
}
MultimediaApplet Classpackage app;
import java.awt.*;
import javax.swing.*;
/**
* A MultimediaApplet is a JApplet that delegates all calls
* to "transition" methods to a MultimediaApp. The calls
* to the MultimediaApp object's "transition" methods will
* be made in the event dispatch thread.
*
* Note: The browser/appletviewer is not supposed to call the
* "transition" methods in the event dispatch thread. This class
* checks to make sure [since SwingUtilities.invokeAndWait() must not
* be called in the event dispatch thread]. While there is a small
* performance penalty incurred as a result, the "transition" methods
* are not called frequently enough for this to matter.
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public abstract class MultimediaApplet
extends JApplet
implements MultimediaRootPaneContainer
{
private MultimediaApp app;
/**
* Explicit Value Constructor
*
* @param app The MultimediaApp to delegate to
*/
public MultimediaApplet(MultimediaApp app)
{
super();
this.app = app;
setLayout(null);
app.setMultimediaRootPaneContainer(this);
}
/**
* Called to indicate that this Applet should destroy any
* resources that it has allocated
*/
public void destroy()
{
if (SwingUtilities.isEventDispatchThread()) app.destroy();
else
{
try {SwingUtilities.invokeAndWait(new DestroyRunnable());}
catch (Exception e) {}
}
}
/**
* Get a reference to the MultimediaApp that is
* being delegated to
*
* @return The MultimediaApp
*/
protected MultimediaApp getMultimediaApp()
{
return app;
}
/**
* Called to indicate that this Applet has been
* loaded
*/
public void init()
{
if (SwingUtilities.isEventDispatchThread()) app.init();
else
{
try {SwingUtilities.invokeAndWait(new InitRunnable());}
catch (Exception e) {}
}
}
/**
* Called to indicate that this Applet has been
* started
*/
public void start()
{
if (SwingUtilities.isEventDispatchThread()) app.start();
else
{
try {SwingUtilities.invokeAndWait(new StartRunnable());}
catch (Exception e) {}
}
}
/**
* Called to indicate that this Applet has been
* stopped
*/
public void stop()
{
if (SwingUtilities.isEventDispatchThread()) app.stop();
else
{
try {SwingUtilities.invokeAndWait(new StopRunnable());}
catch (Exception e) {}
}
}
// --Inner Classes--
/**
* A Runnable that delegates to the MultimediaApp object's
* destroy() method
*/
private class DestroyRunnable implements Runnable
{
public void run()
{
app.destroy();
}
}
/**
* A Runnable that delegates to the MultimediaApp object's
* init() method
*/
private class InitRunnable implements Runnable
{
public void run()
{
app.init();
}
}
/**
* A Runnable that delegates to the MultimediaApp object's
* start() method
*/
private class StartRunnable implements Runnable
{
public void run()
{
app.start();
}
}
/**
* A Runnable that delegates to the MultimediaApp object's
* stop() method
*/
private class StopRunnable implements Runnable
{
public void run()
{
app.stop();
}
}
}
package app;
import java.util.*;
import javax.swing.*;
/**
* A simple example of a MultimediaApp
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class RandomMessageApp
extends AbstractMultimediaApp
{
// Attributes
private JLabel label;
// The pseudo-random number generator
private static Random rng = new Random();
// The messages
private static final String[] MESSAGES =
{
"What a great example!",
"This class is great.",
"I can't wait to do the programming assignments.",
"I wish lectures lasted for 5 hours.",
"I've never had a better Professor."
};
/**
* "Create" a message at random
*
* @return The message
*/
private static String createRandomMessage()
{
return MESSAGES[rng.nextInt(MESSAGES.length)];
}
/**
* Called to indicate that this MultimediaApp has been loaded
*/
public void init()
{
JPanel contentPane;
String s;
// Select a message at random
s = createRandomMessage();
// Get the container for all content
contentPane = (JPanel)rootPaneContainer.getContentPane();
contentPane.setLayout(null);
// Add a component to the container
label = new JLabel(s, SwingConstants.CENTER);
label.setBounds(50,50,500,100);
contentPane.add(label);
}
}
package app;
/**
* A simple example of a MultimediaApplet
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class RandomMessageMultimediaApplet
extends MultimediaApplet
{
/**
* Default Constructor
*/
public RandomMessageMultimediaApplet()
{
super(new RandomMessageApp());
}
}
package app;
import java.util.*;
import javax.swing.*;
/**
* A simple example of a MultimediaApplication
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class RandomMessageMultimediaApplication
extends MultimediaApplication
{
/**
* The entry-point of the application
*
* @param args The command-line arguments
*/
public static void main(String[] args) throws Exception
{
SwingUtilities.invokeAndWait(
new RandomMessageMultimediaApplication(args, 600, 400));
}
/**
* Explicit Value Constructor
*
* @param args The command-line aguments
* @param width The width of the content (in pixels)
* @param height The height of the content (in pixels)
*/
public RandomMessageMultimediaApplication(String[] args,
int width, int height)
{
super(args, new RandomMessageApp(), width, height);
}
}
mainWindow.setDefaultCloseOperation(
JFrame.DO_NOTHING_ON_CLOSE);
mainWindow.addWindowListener(this);
}
/**
* Handle windowOpened messages -- the first time the window is
* made visible (required by WindowListener)
*
* @param event The WindowEvent that generated the message
*/
public void windowOpened(WindowEvent event)
{
resize();
start();
}
/**
* Handle windowDeiconified messages -- when the window is maximized
* (required by WindowListener)
*
* @param event The WindowEvent that generated the message
*/
public void windowDeiconified(WindowEvent event)
{
start();
}
/**
* Handle windowIconified messages -- when the window is minimized
* (required by WindowListener)
*
* @param event The WindowEvent that generated the message
*/
public void windowIconified(WindowEvent event)
{
stop();
}
/**
* Handle windowClosing messages
* (required by WindowListener)
*
* @param event The WindowEvent that generated the message
*/
public void windowClosing(WindowEvent event)
{
exit();
}
/**
* Exist this JApplication (after prompting the user
* for a verification)
*/
private void exit()
{
int response;
response = JOptionPane.showConfirmDialog(mainWindow,
"Exit this application?",
"Exit?",
JOptionPane.YES_NO_OPTION);
if (response == JOptionPane.YES_OPTION)
{
mainWindow.setVisible(false);
stop();
mainWindow.dispose();
}
}
/**
* Handle windowClosed messages -- when the window is disposed
* (required by WindowListener)
*
* @param event The WindowEvent that generated the message
*/
public void windowClosed(WindowEvent event)
{
destroy();
System.exit(0);
}
The Final Design
package io;
import java.io.*;
import java.net.*;
import java.util.*;
/**
* A ResourceFinder is used to find a "resource" either in
* the .jar file (containing the ResourceFinder class)
* or in the local file system
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class ResourceFinder
{
private static final ResourceFinder instance =
new ResourceFinder();
/**
* Create an instance of a ResourceFinder
*
* @return The instance
*/
public static ResourceFinder createInstance()
{
return instance;
}
/**
* Find a resource
*
* @return The InputStream of the resource (or null)
*/
public InputStream findInputStream(String name)
{
Class c;
InputStream is;
// Get the Class for this class
c = this.getClass();
// Get a URL for the resource
is = c.getResourceAsStream(name);
return is;
}
/**
* Find a resource
*
* @return The URL of the resource (or null)
*/
public URL findURL(String name)
{
Class c;
URL url;
// Get the Class for this class
c = this.getClass();
// Get a URL for the resource
url = c.getResource(name);
return url;
}
/**
* Load a list of resource names from a list (e.g., file)
*
* Note: This method does not return an array of InputStream
* objects to conserver resources.
*
* @param listName The name of the list
* @return The resource names
*/
public String[] loadResourceNames(String listName)
{
ArrayList<String> buffer;
BufferedReader in;
InputStream is;
String line;
String[] names;
names = null;
is = findInputStream(listName);
if (is != null)
{
try
{
in = new BufferedReader(new InputStreamReader(is));
buffer = new ArrayList<String>();
while ((line=in.readLine()) != null)
{
buffer.add(line);
}
names = new String[buffer.size()];
buffer.toArray(names);
}
catch (IOException ioe)
{
// Can't read the list
}
}
return names;
}
}
Design of a Simple Metronome
package event;
/**
* The requirements of an object that listens to a Metronome
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public interface MetronomeListener
{
/**
* Handle a Metronome tick
*
* @param millis The number of milliseconds since the Metronome started
*/
public abstract void handleTick(int millis);
}
/**
* Add a MetronomeListener
*
* @param ml The MetronomeListener to add
*/
public synchronized void addListener(MetronomeListener ml)
{
listeners.add(ml);
copyListeners();
}
/**
* Copy the collection of listeners
*
* The copy is used in the event dispatch thread to avoid
* concurrent modification problems.
*
* This method is called infrequently (and, in general,
* before events are dispatched) so it does not cause
* any real efficiency problems.
*/
private void copyListeners()
{
copy = new MetronomeListener[listeners.size()];
listeners.toArray(copy);
}
/**
* Remove a MetronomeListener
*
* @param ml The MetronomeListener to remove
*/
public synchronized void removeListener(MetronomeListener ml)
{
listeners.remove(ml);
copyListeners();
}
/**
* Notify observers in the GUI/event-dispatch thread.
*
* Note: Listeners are notified in the REVERSE order
* in which they are added.
*/
private synchronized void notifyListeners()
{
// Setup the state of the MetronomeTickDispatcher
dispatcher.setup(copy, time);
// Cause the run() method of the dispatcher to be
// called in the GUI/event-dispatch thread
EventQueue.invokeLater(dispatcher);
}
/**
* A MetronomeTickDispatcher is used by a Metronome to
* inform listeners of a tick.
*
* This class implements Runnable because its run()
* method will be called in the GUI/event-dispatch thread.
*/
private class MetronomeTickDispatcher implements Runnable
{
private MetronomeListener[] listeners;
private int time;
/**
* Code to be executed in the event-dispatch thread
* (required by Runnable)
*
* Specifically, notify the listeners
*/
public void run()
{
int n;
n = listeners.length;
for (int i=n-1; i>=0; i--)
{
if (listeners[i] != null)
listeners[i].handleTick(time);
}
}
/**
* Setup this MetronomeTickDispatcher for the next
* dispatch
*
* @param listeners The collection of MetronomeListener objects
* @param time The (relative) time of the tick
*/
public void setup(MetronomeListener[] listeners,
int time)
{
this.listeners = listeners;
this.time = time;
}
}
/**
* Start this Timer
*/
public void start()
{
if (timerThread == null)
{
keepRunning = true;
timerThread = new Thread(this);
timerThread.start();
}
}
/**
* The code that is executed in the timer thread
* (required by Runnable)
*/
public void run()
{
int currentDelay;
long currentTick, drift;
currentDelay = delay;
if (adjusting) lastTick = System.currentTimeMillis();
while (keepRunning)
{
try
{
timerThread.sleep(currentDelay);
time += currentDelay;
if (adjusting)
{
// Compensate for any drift
currentTick = System.currentTimeMillis();
drift = (currentTick - lastTick) - currentDelay;
currentDelay = (int)Math.max(0, delay-drift);
lastTick = currentTick;
}
notifyListeners();
}
catch (InterruptedException ie)
{
// stop() was called
}
}
timerThread = null;
}
package app;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import app.AbstractMultimediaApp;
import event.*;
/**
* A simple MultimediaApp that uses a Metronome
*
* @author Prof. David Bernstein, James Madison University
* @version 1.0
*/
public class StopWatchApp
extends AbstractMultimediaApp
implements ActionListener, MetronomeListener
{
private boolean running;
private JLabel label;
private Metronome metronome;
private static final String START = "Start";
private static final String STOP = "Stop";
/**
* Handle actionPerformed messages
* (required by ActionListener)
*
* @param event The ActionEvent that generated the message
*/
public void actionPerformed(ActionEvent event)
{
String actionCommand;
actionCommand = event.getActionCommand();
if (actionCommand.equals(START))
{
label.setText("0");
metronome.reset();
metronome.start();
running = true;
}
else if (actionCommand.equals(STOP))
{
metronome.stop();
running = false;
}
}
/**
* Respond to handleTick messages
* (required by MetronomeListener)
*
* @param millis The time
*/
public void handleTick(int millis)
{
label.setText(""+millis/1000);
}
/**
* Called to indicate that this MultimediaApp has been loaded
*/
public void init()
{
JButton start, stop;
JPanel contentPane;
running = false;
contentPane = (JPanel)rootPaneContainer.getContentPane();
contentPane.setLayout(null);
label = new JLabel("0");
label.setBounds(250,100,100,100);
contentPane.add(label);
start = new JButton(START);
start.setBounds(50,300,100,50);
start.addActionListener(this);
contentPane.add(start);
stop = new JButton(STOP);
stop.setBounds(450,300,100,50);
stop.addActionListener(this);
contentPane.add(stop);
metronome = new Metronome(1000, true);
metronome.addListener(this);
}
/**
* Called to indicate that this MultimediaApp should start
* (required by MultimediaApp)
*/
public void start()
{
if (running) metronome.start();
}
/**
* Called to indicate that this MultimediaApp should stop
* (required by MultimediaApp)
*/
public void stop()
{
if (running) metronome.stop();
}
}