| 1.124 Lecture 13 | 10/29/1998 |
layout_user_interface();
while (true) {
// The event loop.
Event e =
get_next_event();
// Get the next event from the event queue.
if (e.type
== QUIT) {
break;
}
else {
process_event(e);
// Respond to the event, according its type.
}
}
In C++, the programmer must often explicitly write an event loop similar
to the one shown above. This can involve a lot of work, so Java attempts
to shield the programmer from the actual event loop, while still providing
a flexible way to specify how events are processed.
The Java event model is based on the notion of event sources and event listeners.
Here is a partial list of event types, their event categories and the
corresponding listener interfaces.
| Event | Event Category | Listener Interface |
|---|---|---|
| Button click, menu selection, text field entry. | ActionEvent | ActionListener |
| Resize, move, show, hide. | ComponentEvent | ComponentListener |
| Mouse button down, mouse button up. | MouseEvent | MouseListener |
| Mouse move, mouse drag. | MouseEvent | MouseMotionListener |
| Key press, key release. | KeyEvent | KeyListener |
| Gain focus, lose focus. | FocusEvent | FocusListener |
| Window closing, window iconified, window deiconified. | WindowEvent | WindowListener |
Suppose we want our user interface to have a single Button, which
causes a message to be displayed every time it is clicked. To receive
button clicks, our event listener must be capable of listening to ActionEvents.
We must therefore make our event listener class implement the ActionListener
interface. The ActionListener interface requires us to implement
a single method named actionPerformed().
MyListener.java
import java.awt.event.*;
public class MyListener implements ActionListener {
// Respond to an ActionEvent.
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked
button.");
}
}
We must now create the Button and register a MyListener
object by calling the button's addActionListener() method.
MyFrame.java
import java.awt.*;
public class MyFrame extends Frame {
Button mButton;
// The event source.
MyListener mListener;
// The event listener.
// The constructor.
public MyFrame() {
mButton = new Button("Click
me.");
mListener = new MyListener();
mButton.addActionListener(mListener);
// Register the listener object with the button.
add(mButton);
// Position the button within the frame.
setSize(300,400);
// Set the size of the frame.
}
// The main function.
public static void main(String[] args) {
MyFrame f = new MyFrame();
f.show();
}
}
Our previous example has only one interesting UI component: a Button. What if we wanted to add a second Button and perhaps a TextArea, so that we can display the message through the UI? We can control the layout of these components within the frame by using a layout manager. Java comes with five layout managers:
Suppose we wish to position our two Buttons side by side, with
the TextArea positioned below them. This is most easily done
by embedding the Buttons within a Panel, and then positioning
the Panel above the TextArea. The Panel thus
serves as a container for the two Buttons. The following code
illustrates this.
MyFrame.java
import java.awt.*;
public class MyFrame extends Frame {
Button mButton1;
// The first event source.
Button mButton2;
// The second event source.
Panel mPanel;
// A container for the buttons.
TextArea mTextArea;
// An area for displaying messages.
MyListener mListener1;
// An event listener for the first button.
MyListener mListener2;
// An event listener for the second button.
// The constructor.
public MyFrame() {
// Create two buttons.
mButton1 = new Button("Button
1");
mButton2 = new Button("Button
2");
// Create a panel and
set its layout manager. In fact, FlowLayout
// happens to be the
default layout manager for a Panel.
mPanel = new Panel();
mPanel.setLayout(new
FlowLayout());
// Position the two buttons
within the panel.
mPanel.add(mButton1);
mPanel.add(mButton2);
// Create a text area
for displaying messages.
mTextArea = new TextArea();
// Set the layout manager
for the frame. In fact, BorderLayout
// happens to be the
default layout manager for a Frame.
setLayout(new BorderLayout());
// Position the panel
and the text area within the frame.
add(mPanel, "North");
add(mTextArea, "South");
// Create the listener
objects and register them with the buttons.
mListener1 = new MyListener(1,
mTextArea);
mButton1.addActionListener(mListener1);
mListener2 = new MyListener(2,
mTextArea);
mButton2.addActionListener(mListener2);
// Set the size of the
frame.
setSize(300,400);
}
// The main function.
public static void main(String[] args) {
MyFrame f = new MyFrame();
f.show();
}
}
MyListener.java
import java.awt.*;
import java.awt.event.*;
public class MyListener implements ActionListener {
int mButtonNumber;
TextArea mTextArea;
// The constructor.
public MyListener(int i, TextArea t) {
mButtonNumber = i;
mTextArea = t;
}
// Respond to an ActionEvent.
public void actionPerformed(ActionEvent e) {
mTextArea.append("Clicked
button " + mButtonNumber + "\n");
}
}
Geometry.java
// This is a Java graphics example that can be run either as an applet
or as an application.
// Created by Kevin Amaratunga 10/17/1997.
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;
import java.util.*;
// In order to run as an applet, class Geometry must be declared
as
// a public class. Note that there cannot be more than one
public
// class in a .java file. Also, the public class must have
the same
// same name as the .java file.
public class Geometry extends Applet {
TextArea mTextArea;
MyCanvas mCanvas;
public Geometry() {
// Choose a layout manager.
BorderLayout is a straightforward one to use.
setLayout(new BorderLayout());
// Create a drawing area and
add it to the center of the applet.
mCanvas = new MyCanvas(this);
add("Center", mCanvas);
// Create a read only text area
to be used for displaying
// information. Add it
to the bottom of the applet.
mTextArea = new TextArea(5,
40);
mTextArea.setEditable(false);
add("South", mTextArea);
}
public TextArea getTextArea() {
return mTextArea;
}
public static void main(String args[]) {
// Create the applet
object.
Geometry mGeomApplet
= new Geometry();
// Create the main window
with the applet embedded in it.
new MainWindow(mGeomApplet,
600, 800);
}
}
// When running as a Java application, we need to create a Frame
within which
// to display the applet.
class MainWindow extends Frame implements WindowListener {
// MainWindow constructor.
MainWindow(Geometry geomApplet, int iWidth, int
iHeight) {
// Set the size of the
frame, and its title.
setSize(iWidth, iHeight);
setTitle(geomApplet.getClass().getName());
// Register the frame to start
receiving window events.
addWindowListener(this);
// Add the applet to the center
of the frame.
add("Center", geomApplet);
// Initialize the applet.
geomApplet.init();
// Make the frame visible.
show();
// Start the applet.
geomApplet.start();
}
// The following methods are all required, since
we implement the
// WindowListener interface. We only care
about handling window
// closing events, so this is the only method
that does anything.
public void windowClosing(WindowEvent e) {
System.out.println("Window
closing");
System.exit(0);
}
public void windowClosed(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e)
{}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e)
{}
}
// We can't just instantiate Canvas, since its default implementation
// gives us nothing interesting to look at or do. So here's
a Canvas
// subclass that draws something slightly interesting.
class MyCanvas extends Canvas implements MouseListener {
// Parent and child widgets.
Geometry mGeomApplet;
// The parent applet.
PopupMenu mPopupMenu;
// Popup menu for creating new objects.
// Object lists.
Vector mPointList;
// List of all Point objects.
Vector mLineList;
// List of all Line objects.
Vector mPolygonList;
// List of all Polygon objects.
// Constants that indicate which kind of object
(if any) is currently
// being created.
static final int NO_OBJECT = 0;
static final int POINT_OBJECT = 1;
static final int LINE_OBJECT = 2;
static final int POLYGON_OBJECT = 3;
// Miscellaneous state variables.
int miLastButton = 0;
// Last button for which an event was received.
int miAcceptingInput = 0;
// Type of object (if any) that we are
// currently creating.
int miPointsEntered = 0;
// Number of points entered for this object
// so far.
Object mCurrentObject = null;
// The object that we are currently creating.
// MyCanvas constructor.
MyCanvas(Geometry geomApplet) {
MenuItem item;
mGeomApplet = geomApplet;
// Set the background
color.
setBackground(Color.white);
// Register the canvas
to start listening to mouse events.
addMouseListener(this);
// Create a popup menu
and make it a child of the canvas, but don't show it just yet.
mPopupMenu = new PopupMenu("New
Object");
mPopupMenu.add("Point");
item = mPopupMenu.getItem(0);
item.addActionListener(new
PointActionListener(this));
mPopupMenu.add("Line");
item = mPopupMenu.getItem(1);
item.addActionListener(new
LineActionListener(this));
mPopupMenu.add("Polygon");
item = mPopupMenu.getItem(2);
item.addActionListener(new
PolygonActionListener(this));
add(mPopupMenu);
// Create the object lists
with a reasonable initial capacity.
mPointList = new Vector(10);
mLineList = new Vector(10);
mPolygonList = new Vector(10);
}
// The paint method.
public void paint(Graphics g) {
int i;
// Draw all objects that
are stored in the object lists.
for (i = 0; i < mPointList.size();
i++) {
Point point = (Point)mPointList.elementAt(i);
g.fillRect(point.x-1, point.y-1, 3, 3);
}
for (i = 0; i < mLineList.size();
i++) {
Line line = (Line)mLineList.elementAt(i);
line.draw(g);
}
for (i = 0; i < mPolygonList.size();
i++) {
Polygon polygon = (Polygon)mPolygonList.elementAt(i);
int j;
g.setColor(Color.red);
g.drawPolygon(polygon);
g.setColor(Color.black);
for (j = 0; j < polygon.npoints; j++) {
g.fillRect(polygon.xpoints[j], polygon.ypoints[j], 3, 3);
}
}
// Draw as much of the
current object as available.
switch (miAcceptingInput)
{
case LINE_OBJECT:
Line line = (Line)mCurrentObject;
if (line.mb1 && !line.mb2)
g.fillRect(line.mEnd1.x-1, line.mEnd1.y-1, 3, 3);
break;
case POLYGON_OBJECT:
Polygon polygon = (Polygon)mCurrentObject;
int j;
g.setColor(Color.red);
g.drawPolyline(polygon.xpoints, polygon.ypoints, polygon.npoints);
g.setColor(Color.black);
for (j = 0; j < polygon.npoints; j++) {
g.fillRect(polygon.xpoints[j], polygon.ypoints[j], 3, 3);
}
break;
default:
break;
}
// Draw some text at the
top of the canvas.
int w = getSize().width;
int h = getSize().height;
g.drawRect(0, 0, w -
1, h - 1);
g.setFont(new Font("Helvetica",
Font.PLAIN, 15));
g.drawString("Canvas",
(w - g.getFontMetrics().stringWidth("Canvas"))/2, 10);
}
// The next five methods are required, since
we implement the
// MouseListener interface. We are only
interested in mouse pressed
// events.
public void mousePressed(MouseEvent e) {
int iX = e.getX();
// The x and y coordinates of the
int iY = e.getY();
// mouse event.
int iModifier = e.getModifiers();
// Workaround for bug
in JDK 1.1.6
if (iModifier < 4)
{
iModifier += 16;
}
if ((iModifier & InputEvent.BUTTON1_MASK)
!= 0) {
miLastButton = 1;
// If we are currently accepting input for a new object,
// then add the current point to the object.
if (miAcceptingInput != NO_OBJECT)
addPointToObject(iX, iY);
}
else if ((iModifier &
InputEvent.BUTTON2_MASK) != 0) {
miLastButton = 2;
// If current object is a polygon, finish it.
if (miAcceptingInput == POLYGON_OBJECT) {
mPolygonList.addElement(mCurrentObject);
String str = "Finished creating polygon object.\n";
mGeomApplet.getTextArea().append(str);
repaint();
miAcceptingInput = NO_OBJECT;
miPointsEntered = 0;
mCurrentObject = null;
}
}
else if ((iModifier &
InputEvent.BUTTON3_MASK) != 0) {
miLastButton = 3;
// Display the popup menu provided we are not accepting
// any input for a new object.
if (miAcceptingInput == NO_OBJECT)
mPopupMenu.show(this, iX, iY);
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void getPointInput() {
miAcceptingInput = POINT_OBJECT;
mCurrentObject = (Object)new
Point();
mGeomApplet.getTextArea().append("New
point object: enter point.\n");
}
public void getLineInput() {
miAcceptingInput = LINE_OBJECT;
mCurrentObject = (Object)new
Line();
mGeomApplet.getTextArea().append("New
line: enter end points.\n");
}
public void getPolygonInput() {
miAcceptingInput = POLYGON_OBJECT;
mCurrentObject = (Object)new
Polygon();
mGeomApplet.getTextArea().append("New
polygon: enter vertices ");
mGeomApplet.getTextArea().append("(click
middle button to finish).\n");
}
void addPointToObject(int iX, int iY) {
String str;
miPointsEntered++;
switch (miAcceptingInput)
{
case POINT_OBJECT:
str = "Point at (" + iX + "," + iY + ")\n";
mGeomApplet.getTextArea().append(str);
Point point = (Point)mCurrentObject;
point.x = iX;
point.y = iY;
mPointList.addElement(mCurrentObject);
str = "Finished creating point object.\n";
mGeomApplet.getTextArea().append(str);
repaint();
miAcceptingInput = NO_OBJECT;
miPointsEntered = 0;
mCurrentObject = null;
break;
case LINE_OBJECT:
if (miPointsEntered <= 2) {
str = "End " + miPointsEntered + " at (" + iX + "," + iY + ")";
str += "\n";
mGeomApplet.getTextArea().append(str);
}
Line line = (Line)mCurrentObject;
if (miPointsEntered == 1) {
line.setEnd1(iX, iY);
repaint();
}
else {
if (miPointsEntered == 2) {
line.setEnd2(iX, iY);
mLineList.addElement(mCurrentObject);
str = "Finished creating line object.\n";
mGeomApplet.getTextArea().append(str);
repaint();
}
miAcceptingInput = NO_OBJECT;
miPointsEntered = 0;
mCurrentObject = null;
}
break;
case POLYGON_OBJECT:
str = "Vertex " + miPointsEntered + " at (" + iX + "," + iY + ")";
str += "\n";
mGeomApplet.getTextArea().append(str);
Polygon polygon = (Polygon)mCurrentObject;
polygon.addPoint(iX, iY);
repaint();
break;
default:
break;
}
// End switch.
}
}
// Action listener to create a new Point object.
class PointActionListener implements ActionListener {
MyCanvas mCanvas;
PointActionListener(MyCanvas canvas) {
mCanvas = canvas;
}
public void actionPerformed(ActionEvent e) {
mCanvas.getPointInput();
}
}
// Action listener to create a new Line object.
class LineActionListener implements ActionListener {
MyCanvas mCanvas;
LineActionListener(MyCanvas canvas) {
mCanvas = canvas;
}
public void actionPerformed(ActionEvent e) {
mCanvas.getLineInput();
}
}
// Action listener to create a new Polygon object.
class PolygonActionListener implements ActionListener {
MyCanvas mCanvas;
PolygonActionListener(MyCanvas canvas) {
mCanvas = canvas;
}
public void actionPerformed(ActionEvent e) {
mCanvas.getPolygonInput();
}
}
// A line class.
class Line {
Point mEnd1, mEnd2;
boolean mb1, mb2;
Line() {
mb1 = mb2 = false;
mEnd1 = new Point();
mEnd2 = new Point();
}
void setEnd1(int iX, int iY) {
mEnd1.x = iX;
mEnd1.y = iY;
mb1 = true;
}
void setEnd2(int iX, int iY) {
mEnd2.x = iX;
mEnd2.y = iY;
mb2 = true;
}
void draw(Graphics g) {
g.fillRect(mEnd1.x-1,
mEnd1.y-1, 3, 3);
g.fillRect(mEnd2.x-1,
mEnd2.y-1, 3, 3);
g.setColor(Color.green);
g.drawLine(mEnd1.x, mEnd1.y,
mEnd2.x, mEnd2.y);
g.setColor(Color.black);
}
}