Java GUI Tutorial
Java GUI Tutorial
Java GUI Tutorial
Each GUI program has a top-level container. The commonly-used top-level containers in AWT
are Frame, Dialog and Applet:
A Frame
provides the "main window" for the GUI application, which has a title bar (containing an
icon, a title, the minimize, maximize/restore-down and close buttons), an optional menu
bar, and the content display area. To write a GUI program, we typically start with a
subclass extending from java.awt.Frame to inherit the main window as follows:
import java.awt.Frame;
An AWT Applet (in package java.applet) is the top-level container for an applet,
which is a Java program running inside a browser. Applet will be discussed in the later
chapter.
Secondary containers are placed inside a top-level container or another secondary container.
AWT also provide these secondary containers:
Panel:
ScrollPane:
component.
others.
are defined in the class for you to specify the alignment (rather than
asking you to memorize arbitrary integer values).
Label.CENTER
2. The second constructor constructs a Label object with the given text string in default of
left-aligned.
3. The third constructor constructs a Label object with an initially empty string. You could
set the label text via the setText() method later.
Constants
public static final LEFT;
public static final RIGHT;
public static final CENTER;
// Label.LEFT
// Label.RIGHT
// Label.CENTER
These three constants are defined for specifying the alignment of the Label's text.
Public Methods
// Examples
public String getText();
public void setText(String strLabel);
public int getAlignment();
public void setAlignment(int alignment);
The getText() and setText() methods can be used to read and modify the Label's text.
Similarly, the getAlignment() and setAlignment() methods can be used to retrieve and
modify the alignment of the text.
Constructing a Component and Adding the Component into a Container
An Anonymous Instance
You can create a Label without specifying an identifier, called anonymous instance. In the case,
the Java compiler will assign an anonymous identifier for the allocated object. You will not be
able to reference an anonymous instance in your program after it is created. This is usually
alright for a Label instance as there is often no need to reference a Label after it is constructed.
Example
// Allocate an anonymous Label instance. "this" container adds the instance
into itself.
// You CANNOT reference an anonymous instance to carry out further operations.
add(new Label("Enter Name: ", Label.RIGHT));
// Same as
Label lblXxx = new Label("Enter Name: ", Label.RIGHT)); // lblXxx assigned by
compiler
add(lblXxx);
AWT GUI Component: java.awt.Button
The Button class has two constructors. The first constructor creates a Button object with the
given label painted over the button. The second constructor creates a Button object with no
label.
Public Methods
The getLabel() and setLabel() methods can be used to read the current label and modify the
label of a button, respectively.
Note: The latest Swing's JButton replaces getLabel()/setLabel() with
getText()/setText() to be consistent with all the components. We will describe Swing later.
Event
Clicking a button fires a so-called ActionEvent and triggers a certain programmed action. I will
explain event-handling later.
Example
Button btnColor = new Button("Red");
called btnColor
add(btnColor);
...
btnColor.setLabel("green");
btnColor.getLabel();
...
add(Button("Blue"));
// Create an
later
A java.awt.TextField is single-line text box for users to enter texts. (There is a multiple-line
text box called TextArea.) Hitting the "ENTER" key on a TextField object triggers an actionevent.
Constructors
public TextField(String strInitialText, int columns);
// Construct a TextField instance with the given initial text string with
the number of columns.
public TextField(String strInitialText);
// Construct a TextField instance with the given initial text string.
Public Methods
public String getText();
// Get the current text on this TextField instance
public void setText(String strText);
// Set the display text on this TextField instance
public void setEditable(boolean editable);
// Set this TextField to editable (read/write) or non-editable (read-only)
Event
Hitting the "ENTER" key on a TextField fires a ActionEvent, and triggers a certain
programmed action.
Example
TextField tfInput = new TextField(30); // Declare and allocate an TextField
instance called tfInput
add(tfInput);
// "this" Container adds the TextField
TextField tfResult = new TextField(); // Declare and allocate an TextField
instance called tfResult
tfResult.setEditable(false) ;
// Set to read-only
add(tfResult);
// "this" Container adds the TextField
......
// Read an int from TextField "tfInput", square it, and display on "tfResult".
// getText() returns a String, need to convert to int
int number = Integer.parseInt(tfInput.getText());
number *= number;
// setText() requires a String, need to convert the int number to String.
tfResult.setText(number + "");
Take note that getText()/SetText() operates on String. You can convert a String to a
primitive, such as int or double via static method Integer.parseInt() or
Double.parseDouble(). To convert a primitive to a String, simply concatenate the primitive
with an empty String.
2.5 Example 1: AWTCounter
Let's assemble some components together into a simple GUI counter program, as illustrated. It
has a top-level container Frame, which contains three components - a Label "Counter", a noneditable TextField to display the current count, and a "Count" Button. The TextField displays
"0" initially.
Each time you click the button, the counter's value increases by 1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.awt.*;
import java.awt.event.*;
interfaces
btnCount.addActionListener(this);
// btnCount is the source object that fires ActionEvent when
clicked.
// The source add "this" instance as an ActionEvent listener,
which provides
// an ActionEvent handler called actionPerformed().
33
// Clicking btnCount invokes actionPerformed().
34
35
setTitle("AWT Counter"); // "super" Frame sets its title
36
setSize(250, 100);
// "super" Frame sets its initial window
37 size
38
39
// For inspecting the components/container objects
40
// System.out.println(this);
41
// System.out.println(lblCount);
42
// System.out.println(tfCount);
43
// System.out.println(btnCount);
44
45
setVisible(true);
// "super" Frame shows
46
47
// System.out.println(this);
48
// System.out.println(lblCount);
49
// System.out.println(tfCount);
50
// System.out.println(btnCount);
51
}
52
53
// The entry main() method
54
public static void main(String[] args) {
55
// Invoke the constructor to setup the GUI, by allocating an instance
56
AWTCounter app = new AWTCounter();
57
// or simply "new AWTCounter();" for an anonymous instance
58
}
59
60
// ActionEvent handler - Called back upon button-click.
61
@Override
62
public void actionPerformed(ActionEvent evt) {
63
++count; // increase the counter value
64
// Display the counter value on the TextField tfCount
tfCount.setText(count + ""); // convert int to String
}
}
To exit this program, you have to close the CMD-shell (or press "control-c" on the CMD
console); or push the "red-square" close button in Eclipse's Application Console. This is because
we have yet to write the handler for the Frame's close button. We shall do that in the later
example.
Dissecting the AWTCounter.java
The import statements (Lines 1-2) are needed, as AWT container and component classes,
such as Frame, Button, TextField, and Label, are kept in the java.awt package; while
AWT events and event-listener interfaces, such as ActionEvent and ActionListener
are kept in the java.awt.event package.
A GUI program needs a top-level container, and is often written as a subclass of Frame
(Line 5). In other words, this class AWTCounter is a Frame, and inherits all the attributes
and behaviors of a Frame, such as the title bar and content pane.
Lines 12 to 46 define a constructor, which is used to setup and initialize the GUI
components.
In Line 13, the setLayout() (inherited from the superclass Frame) is used to set the
layout of the container. FlowLayout is used which arranges the components in left-toright and flows into next row in a top-to-bottom manner.
A Label, TextField (non-editable), and Button are constructed. We invoke the add()
method (inherited from the superclass Frame) to add these components into container.
In Line 33-34, we invoke the setSize() and the setTitle() (inherited from the
superclass Frame) to set the initial size and the title of the Frame. The setVisible(true)
method (Line 42) is then invoked to show the display.
It is interesting to inspect the GUI objects via the toString(), to gain an insight to these classes.
(Alternatively, use a graphic debugger in Eclipse/NetBeans or study the JDK source code.) For
example, if we insert the following code before and after the setvisible():
System.out.println(this);
System.out.println(lblCount);
System.out.println(tfCount);
System.out.println(btnCount);
setVisible(true);
System.out.println(this);
System.out.println(lblCount);
System.out.println(tfCount);
System.out.println(btnCount);
The output (with my comments) are as follows. You could have an insight of the variables
defined in the class.
// Before setVisible()
AWTCounter[frame0,0,0,250x100,invalid,hidden,layout=java.awt.FlowLayout,title=
AWT Counter,resizable,normal]
// name (assigned by compiler) is "frame0"; top-left (x,y) at (0,0);
width/height is 250x100 (via setSize());
java.awt.Label[label0,0,0,0x0,invalid,align=left,text=Counter]
// name is "Label0"; align is "Label.LEFT" (default); text is "Counter"
(assigned in contructor)
java.awt.TextField[textfield0,0,0,0x0,invalid,text=0,selection=0-0]
// name is "Textfield0"; text is "0" (assigned in contructor)
java.awt.Button[button0,0,0,0x0,invalid,label=Count]
// name is "button0"; label text is "Count" (assigned in contructor)
// Before setVisible(), all components are invalid (top-left (x,y),
width/height are invalid)
// After setVisible(), all components are valid
AWTCounter[frame0,0,0,250x100,layout=java.awt.FlowLayout,title=AWT
Counter,resizable,normal]
// valid and visible (not hidden)
java.awt.Label[label0,20,41,58x23,align=left,text=Counter]
// Top-left (x,y) at (20,41) relative to the parent Frame; width/height
= 58x23
java.awt.TextField[textfield0,83,41,94x23,text=0,selection=0-0]
// Top-left (x,y) at (83,41) relative to the parent Frame; width/height
= 94x23; no text selected (0-0)
java.awt.Button[button0,182,41,47x23,label=Count]
// Top-left (x,y) at (182,41) relative to the parent Frame; width/height
= 47x23
In this example, the top-level container is again the typical java.awt.Frame, which contains 4
components: a Label "Enter an Integer", a TextField for accepting user input, another Label
"The Accumulated Sum is", and another non-editable TextField for displaying the sum. The
components are arranged in FlowLayout.
The program shall accumulate the number entered into the input TextField and display the sum
in the output TextField.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import java.awt.*;
import java.awt.event.*;
interfaces
57
58
3. AWT Event-Handling
Java adopts the so-called "Event-Driven" (or "Event-Delegation") programming model for eventhandling, similar to most of the visual programming languages (such as Visual Basic and
Delphi).
In event-driven programming, a piece of event-handling codes is executed (or called back by the
graphics subsystem) when an event has been fired in response to an user input (such as clicking a
mouse button or hitting the ENTER key). This is unlike the procedural model, where codes are
executed in a sequential manner.
The AWT's event-handling classes are kept in package java.awt.event.
Three objects are involved in the event-handling: a source, listener(s) and an event object.
The source object (such as Button and Textfield) interacts with the user. Upon triggered, it
creates an event object. This event object will be messaged to all the registered listener object(s),
and an appropriate event-handler method of the listener(s) is called-back to provide the response.
In other words, triggering a source fires an event to all its listener(s), and invoke an appropriate
handler of the listener(s).
To express interest for a certain source's event, the listener(s) must be registered with the source.
In other words, the listener(s) "subscribes" to a source's event, and the source "publishes" the
event to all its subscribers upon activation. This is known as subscribe-publish or observableobserver design pattern.
signature of the
Called back upon mouseCalled back upon mouseCalled back upon mouseCalled back when mouse
Called back when mouse
Secondly, all the listeners interested in the XxxEvent must implement the XxxListener
interface. That is, the listeners must provide their own implementations (i.e., programmed
responses) to all the abstract methods declared in the XxxListener interface. In this
way, the listenser(s) can response to these events appropriately. For example,
// An example of MouseListener, which provides implementation to the
handler methods
class MyMouseListener implement MouseListener {
@Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse-button pressed!");
}
@Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse-button released!");
}
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("Mouse-button clicked (pressed and
released)!");
}
@Override
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse-pointer entered the source component!");
}
@Override
public void mouseExited(MouseEvent e)
{
System.out.println("Mouse exited-pointer the source component!");
}
Thirdly, in the source, we need to maintain a list of listener object(s), and define two
methods: addXxxListener() and removeXxxListener() to add and remove a listener
from this list. The signature of the methods are:
Take note that all the listener(s) interested in the XxxEvent must implement the
XxxListener interface. That is, they are sub-type of the XxxListener. Hence, they can
be upcasted to XxxListener and passed as the argument of the above methods.
In summary, we identify the source, the event-listener interface, and the listener object.
The listener must implement the event-listener interface. The source object then registers
listener object via the addXxxListener() method:
aSource.addXxxListener(alistener);
XxxEvent
The source object registers listener via the addActionListener(). In this example, the
source btnCount (Button) adds "this" object as a listener via:
btnCount.addActionListener(this);
Upon button-click, the btnCount creates an ActionEvent object, and calls back the
actionPerformed(ActionEvent) method of all its registered listener(s) with the
ActionEvent object created:
4. The source object tfInput (TextField) registers the listener (this object) via the
tfInput.addActionListener(this).
5. The ActionEvent listener (this class) is required to implement the ActionListener
interface, and override the actionPerformed() method to provide the programmed
response upon activation.
3.3 Example 3: WindowEvent and WindowListener Interface
A WindowEvent is fired (to all its WindowEvent listeners) when a window (e.g., Frame) has been
opened/closed, activated/deactivated, iconified/deiconified via the 3 buttons at the top-right
corner or other means. The source of WindowEvent shall be a top-level window-container such as
Frame.
A WindowEvent listener must implement WindowListener interface, which declares 7 abstract
event-handling methods, as follows. Among them, the windowClosing(), which is called back
upon clicking the window-close button, is the most commonly-used.
public void windowClosing(WindowEvent evt)
// Called-back when the user attempts to close the window by clicking the
window close button.
// This is the most-frequently used handler.
public void windowOpened(WindowEvent evt)
// Called-back the first time a window is made visible.
public void windowClosed(WindowEvent evt)
// Called-back when a window has been closed as the result of calling
dispose on the window.
public void windowActivated(WindowEvent evt)
// Called-back when the Window is set to be the active Window.
public void windowDeactivated(WindowEvent evt)
// Called-back when a Window is no longer the active Window.
public void windowIconified(WindowEvent evt)
// Called-back when a window is changed from a normal to a minimized state.
public void windowDeiconified(WindowEvent evt)
// Called-back when a window is changed from a minimized to a normal state.
The following program added support for "close-window button" to Example 1: AWTCounter.
1 import java.awt.*;
2 import java.awt.event.*;
3 interfaces
4
5 // An AWT GUI program inherits the top-level container java.awt.Frame
6 public class WindowEventDemo extends Frame
7
implements ActionListener, WindowListener {
8
// This class acts as listener for ActionEvent and WindowEvent
9
// A Java class can extend one superclass, but can implement multiple
10 interfaces.
11
12
private TextField tfCount; // Declare a TextField component
13
private Button btnCount;
// Declare a Button component
14
private int count = 0;
// Counter's value
15
16
// Constructor to setup the GUI components and event handlers
17
public WindowEventDemo() {
18
setLayout(new FlowLayout()); // "super" Frame sets to FlowLayout
19
20
add(new Label("Counter"));
// "super" Frame adds an anonymous Label
21
22
tfCount = new TextField("0", 10); // Construct the TextField
23
tfCount.setEditable(false);
// read-only
24
add(tfCount);
// "super" Frame adds TextField
25
26
btnCount = new Button("Count"); // Construct the Button
27
add(btnCount);
// "super" Frame adds Button
28
29
btnCount.addActionListener(this);
30
// btnCount (source object) fires ActionEvent upon clicking
31
// btnCount adds "this" object as an ActionEvent listener
32
33
34
addWindowListener(this);
35
// "super" Frame (source object) fires WindowEvent.
36
// "super" Frame adds "this" object as a WindowEvent listener.
37
38
setTitle("WindowEvent Demo"); // "super" Frame sets title
39
setSize(250, 100);
// "super" Frame sets initial size
40
setVisible(true);
// "super" Frame shows
41
}
42
43
// The entry main() method
44
public static void main(String[] args) {
45
new WindowEventDemo(); // Let the construct do the job
46
}
47
48
/* ActionEvent handler */
49
@Override
50
public void actionPerformed(ActionEvent evt) {
51
++count;
52
tfCount.setText(count + "");
53
}
54
55
/* WindowEvent handlers */
56
// Called back upon clicking close-window button
57
@Override
58
public void windowClosing(WindowEvent evt) {
59
System.exit(0); // Terminate the program
}
60
61
62
63
64
65
66
}
need
void
void
void
void
void
void
In this example, we shall modify the earlier AWTCounter example to handle the WindowEvent.
Recall that pushing the "close-window" button on the AWTCounter has no effect, as it did not
handle the WindowEvent of windowClosing(). We included the WindowEvent handling codes in
this example.
1. We identify super Frame as the source object.
2. The Frame fires the WindowEvent to all its registered WindowEvent listener(s).
3. We select this object as the WindowEvent listener (for simplicity)
4. We register this object as the WindowEvent listener to the source Frame via method
addWindowListener(this).
5. The WindowEvent listener (this class) is required to implement the WindowListener
interface, which declares 7 abstract methods: windowOpened(), windowClosed(),
windowClosing(), windowActivated(), windowDeactivated(), windowIconified()
and windowDeiconified().
6. We override the windowClosing() handler to terminate the program using
System.exit(0). We ignore the other 6 handlers, but required to provide an empty body.
The sequence diagram is as follow:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.awt.*;
import java.awt.event.*;
public class MouseEventDemo extends Frame implements MouseListener {
private TextField tfMouseX; // to display mouse-click-x
private TextField tfMouseY; // to display mouse-click-y
// Constructor - Setup the UI components and event handlers
public MouseEventDemo() {
setLayout(new FlowLayout()); // "super" frame sets its layout to
FlowLayout
// Label (anonymous)
add(new Label("X-Click: ")); // "super" frame adds Label component
// TextField
tfMouseX = new TextField(10); // 10 columns
tfMouseX.setEditable(false); // read-only
add(tfMouseX);
// "super" frame adds TextField
component
// Label (anonymous)
add(new Label("Y-Click: ")); // "super" frame adds Label component
// TextField
tfMouseY = new TextField(10);
tfMouseY.setEditable(false); // read-only
add(tfMouseY);
// "super" frame adds TextField
component
addMouseListener(this);
// "super" frame (source) fires the MouseEvent.
// "super" frame adds "this" object as a MouseEvent listener.
47
48
49
50
51
52
53
54
In this example, we setup a GUI with 4 components (two Labels and two non-editable
TextFields), inside a top-level container Frame, arranged in FlowLayout.
To demonstrate the MouseEvent:
1. We identity super Frame as the source object.
2. The Frame fires a MouseEvent to all its MouseEvent listener(s) when you
click/press/release a mouse-button or enter/exit with the mouse-pointer.
3. We select this object as the MouseEvent listener (for simplicity).
4. We register this object as the MouseEvent listener to super Frame (source) via the
method addMouseListener(this).
5. The listener (this class) is required to implement the MouseListener interface, which
declares 5 abstract methods: mouseClicked(), mousePressed(), mouseReleased(),
mouseEntered(), and mouseExit(). We override the mouseClicked() to display the (x,
y) co-ordinates of the mouse click on the two displayed TextFields. We ignore all the
other handlers (for simplicity - but you need to provide an empty body for compilation).
Try: Include a WindowListener to handle the close-window button.
3.5 Example 5: MouseEvent and MouseMotionListener Interface
A MouseEvent is also fired when you moved and dragged the mouse pointer at the source object.
But you need to use MouseMotionListener to handle the mouse-move and mouse-drag. The
MouseMotionListener interface declares the following two abstract methods:
public void mouseDragged(MouseEvent e)
// Called-back when a mouse-button is pressed on the source component and
then dragged.
public void mouseMoved(MouseEvent e)
// Called-back when the mouse-pointer has been moved onto the source
component but no buttons have been pushed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits from the top-level container java.awt.Frame
public class MouseMotionDemo extends Frame
implements MouseListener, MouseMotionListener {
// This class acts as MouseListener and MouseMotionListener
// To display the
private TextField
private TextField
// To display the
private TextField
private TextField
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
need
void
void
void
void
A KeyEvent is fired (to all its registered KeyListeners) when you pressed, released, and typed
(pressed followed by released) a key on the source object. A KeyEvent listener must implement
KeyListener interface, which declares three abstract methods:
public void keyTyped(KeyEvent e)
// Called-back when a key has been typed (pressed and released).
public void keyPressed(KeyEvent e)
public void keyReleased(KeyEvent e)
// Called-back when a key has been pressed/released.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits from the top-level container java.awt.Frame
public class KeyEventDemo extends Frame implements KeyListener {
// This class acts as KeyEvent Listener
private TextField tfInput;
key
setVisible(true);
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
In this example:
1. We identify the tfInput (TextField) as the source object.
2. The source fires a KeyEvent when you press/release/type a key to all its KeyEvent
listener(s).
3. We select this object as the KeyEvent listener.
4. We register this object as the KeyEvent listener to the source TextField via method
input.addKeyListener(this).
5. The KeyEvent listener (this class) needs to implement the KeyListener interface,
which declares 3 abstract methods: keyTyped(), keyPressed(), keyReleased().
6. We override the keyTyped() to display key typed on the display TextArea. We ignore
the keyPressed() and keyReleased().
Next, we define a LightListener interface to bind the source and its listeners. This
interface specifies the signature of the handlers, lightOn(LightEvent) and
lightOff(LightEvent).
In the source Light, we use an ArrayList to maintain its listeners, and create two
methods: addLightListner(LightListener) and
removeLightListener(LightListener). An method called notifyListener() is
written to invoke the appropriate handlers of each of its registered listeners, whenever the
state of the Light changes.
Event: LightEvent.java
1
2
3
4
5
6
7
/** LightEvent */
import java.util.EventObject;
public class LightEvent extends EventObject {
public LightEvent (Object src) {
super(src);
}
}
/** The Light Source, which maintains a list of listeners and fires
LightEvent to its listeners */
import java.util.*;
public class Light {
// Status - on (true) or off (false)
private boolean on;
// Listener list
private List<LightListener> listeners = new ArrayList<LightListener>();
/** Constructor */
public Light() {
on = false;
// initially off
System.out.println("Light: constructed and off");
}
/** Add the given LightListener */
public void addLightListener(LightListener listener) {
listeners.add(listener);
System.out.println("Light: added a listener");
}
/** Remove the given LightListener */
public void removeLightListener(LightListener listener) {
listeners.remove(listener);
System.out.println("Light: removed a listener");
}
/** Turn on this light */
public void turnOn() {
if (!on) {
on = !on;
30
System.out.println("Light: turn on");
31
notifyListener();
32
}
33
}
34
35
36
/** Turn off this light */
37
public void turnOff() {
38
if (on) {
39
on = !on;
40
System.out.println("Light: turn off");
41
notifyListener();
42
}
43
}
44
45
/** Construct an LightEvent and trigger the appropriate handler on its
46 registered listeners */
47
private void notifyListener() {
48
LightEvent evt = new LightEvent(this);
49
for (LightListener listener : listeners) {
50
if (on) {
51
listener.lightOn(evt);
52
} else {
53
listener.lightOff(evt);
54
}
55
}
56
}
}
A Listener: LightWatcher.java
1 /** A LightEvent listener, which is required to implement LightListener
2 interface. */
3 public class LightWatcher implements LightListener {
4
private int id; // ID of this listener
5
6
/** Constructor */
7
public LightWatcher(int id) {
8
this.id = id;
9
System.out.println("LightWatcher-" + id + ": created");
10
}
11
12
/** LightEvent handlers - Called back with LightEvent is fired */
13
@Override
14
public void lightOn(LightEvent evt) {
15
System.out.println("LightWatcher-" + id
16
+ ": I am notified that light is on");
17
}
18
19
@Override
20
public void lightOff(LightEvent evt) {
21
System.out.println("LightWatcher-" + id
22
+ ": I am notified that light is off");
23
}
}
A Test Driver: TestLight.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
that light is on
that light is on
that light is off
that light is off
that light is off
that light is on
handlers. This is because the event handling methods (in a listener) often require access to the
private variables (e.g., a private TextField) of the outer class.
In this example (modified from Example 1 AWTCounter), instead of using "this" as the
ActionEvent listener for the Button, we define a new class called BtnCountListener, and
create an instance of BtnCountListener as the ActionEvent listener for the btnCount. The
BtnCountListener needs to implement the ActionListener interface, and override the
actionPerformed() handler. Since "this" is no long a ActionListener, we remove the
"implements ActionListener" from "this" class's definition.
BtnCountListener needs to be defined
(count and tfCount) of the outer class.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits from the top-level container java.awt.Frame
public class AWTCounterNamedInnerClass extends Frame {
// This class is NOT a ActionListener, hence, it does not implement
ActionListener interface
// The event-handler actionPerformed() needs to access these "private"
variables
private TextField tfCount;
private Button btnCount;
private int count = 0;
// Constructor to setup the GUI components and event handlers
public AWTCounterNamedInnerClass () {
setLayout(new FlowLayout()); // "super" Frame sets to FlowLayout
add(new Label("Counter"));
// An anonymous instance of Label
tfCount = new TextField("0", 10);
tfCount.setEditable(false);
// read-only
add(tfCount);
// "super" Frame adds tfCount
btnCount = new Button("Count");
add(btnCount);
// "super" Frame adds btnCount
// Construct an anonymous instance of BtnCountListener (a named inner
class).
// btnCount adds this instance as a ActionListener.
btnCount.addActionListener(new BtnCountListener());
setTitle("AWT Counter");
setSize(250, 100);
setVisible(true);
}
// The entry main method
public static void main(String[] args) {
new AWTCounterNamedInnerClass(); // Let the constructor do the job
}
/**
* BtnCountListener is a "named inner class" used as ActionListener.
* This inner class can access private variables of the outer class.
*/
private class BtnCountListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent evt) {
++count;
tfCount.setText(count + "");
}
}
41
42
43
44
45
46
47
48
49
}
btnCount.addActionListener(new BtnCountListener());
The inner class can access the private variable tfCount and count of the outer class.
Try moving the BtnCountListener class outside, and define it as an ordinary class. You would
need to pass a reference of the AWTConnter into the constructor of BtnCountListener, and use
this reference to access variables tfCount and count, through public getters or granting them
to public access.
// An ordinary outer class used as ActionListener for the Button
public class BtnCountListener implements ActionListener {
AWTCounter frame;
public BtnCountListener(AWTCounter frame) {
this.frame = frame;
}
@Override
public void actionPerformed(ActionEvent evt) {
frame.count++;
frame.tfCount.setText(frame.count + "");
}
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits from the top-level container java.awt.Frame
public class AWTCounterAnonymousInnerClass extends Frame {
// This class is NOT a ActionListener, hence, it does not implement
ActionListener interface
// The event-handler actionPerformed() needs to access these private
variables
private TextField tfCount;
private Button btnCount;
private int count = 0;
// Constructor to setup the GUI components and event handlers
public AWTCounterAnonymousInnerClass () {
setLayout(new FlowLayout()); // "super" Frame sets to FlowLayout
add(new Label("Counter"));
// An anonymous instance of Label
tfCount = new TextField("0", 10);
tfCount.setEditable(false);
// read-only
add(tfCount);
// "super" Frame adds tfCount
btnCount = new Button("Count");
add(btnCount);
// "super" Frame adds btnCount
// Construct an anonymous instance of an anonymous class.
// btnCount adds this instance as a ActionListener.
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
++count;
tfCount.setText(count + "");
}
});
setTitle("AWT Counter");
setSize(250, 100);
setVisible(true);
}
// The entry main method
public static void main(String[] args) {
new AWTCounterAnonymousInnerClass(); // Let the constructor do the
job
}
Again, "this" class is NOT used as the ActionEvent listener. Hence, we remove the
"implements ActionListener" from this class' definition.
The anonymous inner class is given a name generated by the compiler, and compiled into
OuterClassName$n.class, where n is a running number of the inner classes of this
outer class.
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
++count;
tfCount.setText(count + "");
}
});
1. The anonymous inner class is define inside a method, instead of a member of the outer
class (class member). It is local to the method and cannot be marked with access modifier
(such as public, private) or static, just like any local variable of a method.
2. An anonymous inner class must always extend a superclass or implement an interface.
The keyword "extends" or "implements" is NOT required in its declaration. An
anonymous inner class must implement all the abstract methods in the superclass or in the
interface.
3. An anonymous inner class always uses the default (no-arg) constructor from its
superclass to create an instance. If an anonymous inner class implements an interface, it
uses the java.lang.Object().
4. An anonymous inner class is compiled into a class named OuterClassName$n.class,
where n is a running number of inner classes within the outer class.
5. An instance of an anonymous inner class is constructed via this syntax:
6. new SuperClassName/InterfaceName() { // extends superclass or
implements interface
7.
// invoke the default no-arg
constructor or Object[]
8.
// Implement abstract methods in superclass/interface
9.
// More methods if necessary
10.
......
}
Let's modify our AWTCounter example to include 3 buttons for counting up, counting down, and
reset the count, respectively. We shall attach an anonymous inner class as the listener to each of
buttons.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class AWTCounter3Buttons extends Frame {
private TextField tfCount;
private Button btnCountUp, btnCountDown, btnReset;
private int count = 0;
// Constructor to setup the GUI
public AWTCounter3Buttons () {
setLayout(new FlowLayout());
add(new Label("Counter"));
tfCount = new TextField("0",
tfCount.setEditable(false);
add(tfCount);
// "super" Frame adds tfCount
16
17
btnCountUp = new Button("Count Up");
18
add(btnCountUp);
19
// Construct an anonymous instance of an anonymous inner class.
20
// The source Button adds the anonymous instance as ActionEvent
21 listener
22
btnCountUp.addActionListener(new ActionListener() {
23
@Override
24
public void actionPerformed(ActionEvent evt) {
25
++count;
26
tfCount.setText(count + "");
27
}
28
});
29
30
btnCountDown = new Button("Count Down");
31
add(btnCountDown);
32
btnCountDown.addActionListener(new ActionListener() {
33
@Override
34
public void actionPerformed(ActionEvent evt) {
35
count--;
36
tfCount.setText(count + "");
37
}
38
});
39
40
btnReset = new Button("Reset");
41
add(btnReset);
42
btnReset.addActionListener(new ActionListener() {
43
@Override
44
public void actionPerformed(ActionEvent evt) {
45
count = 0;
46
tfCount.setText("0");
47
}
48
});
49
50
setTitle("AWT Counter");
51
setSize(400, 100);
52
setVisible(true);
53
}
54
55
// The entry main method
56
public static void main(String[] args) {
57
new AWTCounter3Buttons(); // Let the constructor do the job
58
}
59
}
Dissecting the Program
1. Each of the Buttons uses an anonymous instance of an anonymous inner class as its
ActionEvent listener.
5.4 Example 10: Using the Same Listener Instance for All the Buttons
If you use the same instance as the listener for the 3 buttons, you need to determine which button
has fired the event. It is because all the 3 buttons trigger the same event-handler method.
Using ActionEvent's getActionCommand()
In the following example, we use the same instance of a named inner class as the listener for all
the 3 buttons. The listener needs to determine which button has fired the event. This can be
accomplished via the ActionEvent's getActionCommonad() method, which returns the button's
label.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class AWTCounter3Buttons1Listener extends Frame {
private TextField tfCount;
private Button btnCountUp, btnCountDown, btnReset;
private int count = 0;
// Constructor to setup the GUI components and event handlers
public AWTCounter3Buttons1Listener () {
setLayout(new FlowLayout());
add(new Label("Counter"));
tfCount = new TextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
// Construct Buttons
btnCountUp = new Button("Count Up");
add(btnCountUp);
btnCountDown = new Button("Count Down");
add(btnCountDown);
btnReset = new Button("Reset");
add(btnReset);
// Allocate an instance of named inner class BtnListener.
BtnListener listener = new BtnListener();
// Use the same listener instance to all the 3 Buttons.
btnCountUp.addActionListener(listener);
btnCountDown.addActionListener(listener);
btnReset.addActionListener(listener);
setTitle("AWT Counter");
setSize(400, 100);
setVisible(true);
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
Besides the getActionCommand(), which is only available for ActionEvent, you can use the
getSource() method, which is available to all event objects, to retrieve a reference to the source
object that has fired the event. getSource() returns a java.lang.Object. You may need to
downcast it to the proper type of the source object. For example,
1 import java.awt.*;
2 import java.awt.event.*;
3
4 public class AWTCounter3ButtonsGetSource extends Frame {
5
private TextField tfCount;
6
private Button btnCountUp, btnCountDown, btnReset;
7
private int count = 0;
8
9
// Constructor to setup the GUI components and event handlers
10
public AWTCounter3ButtonsGetSource () {
11
setLayout(new FlowLayout());
12
add(new Label("Counter"));
13
tfCount = new TextField("0", 10);
14
tfCount.setEditable(false);
15
add(tfCount);
16
17
// Construct Buttons
18
btnCountUp = new Button("Count Up");
19
add(btnCountUp);
20
btnCountDown = new Button("Count Down");
21
add(btnCountDown);
22
btnReset = new Button("Reset");
23
add(btnReset);
24
25
// Allocate an instance of inner class BtnListener.
26
BtnListener listener = new BtnListener();
27
// Use the same listener instance to all the 3 Buttons.
28
btnCountUp.addActionListener(listener);
btnCountDown.addActionListener(listener);
btnReset.addActionListener(listener);
29
30
31
setTitle("AWT Counter");
32
setSize(400, 100);
33
setVisible(true);
34
}
35
36
// The entry main method
37
public static void main(String[] args) {
38
new AWTCounter3ButtonsGetSource(); // Let the constructor do the job
39
}
40
41
/**
42
* BtnListener is a named inner class used as ActionEvent listener for
43
all
the Buttons.
44
*/
45
private
class BtnListener implements ActionListener {
46
@Override
47
public void actionPerformed(ActionEvent evt) {
48
// Need to determine which button has fired the event.
49
Button source = (Button)evt.getSource();
50
// Get a reference of the source that has fired the event.
51
// getSource() returns a java.lang.Object. Downcast back to
52
Button.
53
if (source == btnCountUp) {
54
++count;
55
}
else
if (source == btnCountDown) {
56
--count;
57
} else {
58
count = 0;
59
}
60
tfCount.setText(count + "");
61
}
62
}
}
A nested class can be declared static (belonging to the class instead of an instance). Recall that
a static member can be used without instantiating the class and can be referenced via the
classname in the form of Classname.memberName (e.g., Math.PI, Integer.parseInt()).
Similarly, a static nested class can be used without instantiating the outer class and can be
referenced via OuterClassName.InnerClassName.
On the other hand, a non-static nested class belongs to an instance of the outer class, just like
any instance variable or method. It can be referenced via
outerClassInstanceName.innerClassInstanceName. A non-static nested class
is formally called an inner class.
Example of non-static (instance) inner class
In this example, a non-static (instance) inner class called MyInnerClass is defined inside the
outer class. The inner class can access private members (variables/methods) of the outer class.
This outer class also declares and constructs an instance of inner class as its member variable.
public class MyOuterClassWithInnerClass {
// Private member variable of the outer class
1
private String msgOuter = "Hello from outer class";
2
3
// Define an inner class as a member of the outer class
4
// This is merely an definition.
5
// Not instantiation takes place when an instance of outer class is
6
constructed
7
public class MyInnerClass {
8
// Private variable of the inner class
9
private String msgInner;
10
// Constructor of the inner class
11
public MyInnerClass(String msgInner) {
12
this.msgInner = msgInner;
13
System.out.println("Constructing an inner class instance: " +
14
msgOuter);
15
// can access private member variable of outer class
16
}
17
// A method of inner class
18
public void printMessage() {
19
System.out.println(msgInner);
20
}
21
}
22
23
// Declare and construct an instance of the inner class, inside the
24
outer
class
25
MyInnerClass anInner = new MyInnerClass("Hi from inner class");
}
1. Allocates an instance of outer class, which implicitly allocates an inner class (called
anInner) as its member variable. You can access this inner class via
outerClassInstanceName.innerClassInstanceName.
2. Explicitly constructs another instance of the inner class, under the same outer class
instance created in the previous step.
3. Explicitly constructs one more instance of the inner class, under a new instance of outer
class. This new outer class instance also implicitly allocates an inner class instance as its
member, as seen from the output.
public class TestInnerClass {
public static void main(String[] args) {
// Construct an instance of outer class, which create anInner
MyOuterClassWithInnerClass anOuter = new
MyOuterClassWithInnerClass();
// Invoke inner class's method from this outer class instance
anOuter.anInner.printMessage();
1
2
3
4
5
6
7
// Explicitly construct another instance of inner class
8
MyOuterClassWithInnerClass.MyInnerClass inner2
9
= anOuter.new MyInnerClass("Inner class 2");
10
inner2.printMessage();
11
12
// Explicitly construct an instance of inner class, under another
13
instance
of outer class
14
MyOuterClassWithInnerClass.MyInnerClass
inner3
15
= new MyOuterClassWithInnerClass().new MyInnerClass("Inner class
16
17 3");
inner3.printMessage();
18
}
}
Constructing an inner class instance: Hello from outer class
Hi from inner class
Constructing an inner class instance: Hello from outer class
Inner class 2
Constructing an inner class instance: Hello from outer class
Constructing an inner class instance: Hello from outer class
Inner class 3
An inner class definition is merely a definition of a class. The outer class does not create an inner
class instance, when it is instantiated. Nonetheless, you could declare it as member of the outer
class, as illustrated in the above example. In many situations, we declare the inner class private.
In this cases, the inner class can only be used (declare and construct) within the outer class.
You can set the inner class to private access. In this case, the inner class can only be accessed
within the outer class, and not by other classes.
Example of static nested class
In this example, a static nested class is defined inside the outer class, which can access the
private static variables of the outer class.
public class MyOuterClassWithStaticNestedClass {
1
// Private "static" member variable of the outer class
2
private static String msgOuter = "Hello from outer class";
3
4
// Define a "static" nested class as a member of the outer class
5
// It can access private "static" variable of the outer class
6
public static class MyStaticNestedClass {
7
// Private variable of inner class
8
private String msgInner;
9
// Constructor of inner class
10
public MyStaticNestedClass(String msgInner) {
11
this.msgInner = msgInner;
12
System.out.println(msgOuter); // access private member of the
13
outer
class
14
}
15
// A method of inner class
16
public void printMessage() {
17
System.out.println(msgInner);
18
}
19
}
20
}
You can access the static nested class via the outer classname, in the form of
OuterClassName.NestedClassName, just like any static variables/methods (e.g.,
Math.PI, Integer.parseInt()). You can instantiate a static nested class without instantiate
the outer class, as static members are associated with the class, instead of instances.
public class TestStaticNestedClass {
1
public static void main(String[] args) {
2
// Construct an instance of static nested class
3
// A "static" nested class, like other "static" members, can be
4 accessed via
5
// the Classname.membername
6
MyOuterClassWithStaticNestedClass.MyStaticNestedClass aNestedInner =
7
new MyOuterClassWithStaticNestedClass.MyStaticNestedClass("Hi
8 from inner class");
9
aNestedInner.printMessage();
10
}
}
Hello from outer class
Hi from inner class
As seen from the example, a static nested class is really like a top-level class with a modified
name (OuterClassname.InnerClassname). It can be used as an extension to package for
namespace management.
6.2 Local Inner Class Defined Inside a Method
Java allows you to define an inner class inside a method, just like defining a method's local
variable. Like local variable, a local inner class does not exist until the method is invoked, and
goes out of scope when the method exits.
A local inner class has these properties:
1. A local inner class cannot have access modifier (such as private or public). It also
cannot be declared static.
2. A local inner class can access all the variables/methods of the enclosing outer class.
3. A local inner class can have access to the local variables of the enclosing method only if
they are declared final (to prevent undesirable side-effects).
Example
1 public class MyOuterClassWithLocalInnerClass {
2
// Private member variable of the outer class
3
private String msgOuter = "Hello from outer class";
4
5
// A member method of the outer class
6
public void doSomething() {
7
8
// A local variable of the method
9
final String msgMethod = "Hello from method";
10
11
// Define a local inner class inside the method
12
class MyInnerClass {
13
// Private variable of the inner class
14
private String msgInner;
15
// Constructor of the inner class
16
public MyInnerClass(String msgInner) {
17
this.msgInner = msgInner;
18
System.out.println("Constructing an inner class instance: " +
19 msgOuter);
20
// can access private member variable of outer class
21
System.out.println("Accessing final variable of the method: " +
22 msgMethod);
23
// can access final variable of the method
24
}
25
// A method of inner class
26
public void printMessage() {
27
System.out.println(msgInner);
28
}
29
}
30
31
// Create an instance of inner class and invoke its method
32
MyInnerClass anInner = new MyInnerClass("Hi, from inner class");
33
anInner.printMessage();
34
}
35
36
// Test main() method
37
38
39
}
Constructing an inner class instance: Hello from outer class
Accessing final variable of the method: Hello from method
Hi, from inner class
Clearly, you can only create one instance for each anonymous inner class.
6.4 Example of Static Nested Class in JDK: Point2D, Point2D.Double, Point2D.Float, Point
The abstract class Point2D (in package java.awt.geom of Java 2D API), which models a 2D
point, declares abstract methods such as getX() and getY(). The Point2D cannot be
instantiated. Point2D does not define any instance variable, in particular, the x and y location of
the point. This is because it is not sure about the type of x and y (which could be int, float, or
double). The instance variables, therefore, are left to the implementation subclasses.
Three subclasses were implemented for types of int, float and double, respectively. Point2D
cannot be designed as a pure abstract-method-only interface, as it contains non-abstract
methods.
The subclass Point defines instance variables x and y in int precision and provides
implementation to abstract methods such as getX() and getY(). Point (of int-precision) is a
straight-forward implementation of inheritance and polymorphism. Point is a legacy class (since
JDK 1.1) and retrofitted when Java 2D was introduced.
Two subclasses Point2D.Float and Point2D.Double define instance variables x and y in float
and double precision, respectively. These two subclasses, are also declared as public static
nested class of the outer class Point2D. Since they are static, they can be referenced as
Point2D.Double and Point2D.Float. They are implemented as nested static subclasses
within the Point2D outer class to keep the codes together and for namespace management. There
is no access-control (of private variables of the outer class) involved.
package java.awt.geom;
abstract public class Point2D
// abstract methods
abstract public double getX();
abstract public double getY();
abstract public void setLocation(double x, double y);
public double distance(double x, double y) { ... }
public double distance(Point2D p) { ... }
public static double distance(double x1, double y1, double x2, double y2) {
... }
......
}
package java.awt.geom;
public class Point extends Point2D {
public int x;
public int y;
public Point(int x, int y) { ... }
@Override public double getX() { return x; }
@Override public double getY() { return y; }
@Override public void setLocation(double x, double y) { ... }
......
}
and Point2D.Float are public static classes. In other words, they can be
used directly without instantiating the outer class, just like any static variable or method
(which can be referenced directly via the classname, e.g., Math.PI, Math.sqrt() and
Integer.parseInt()). Since they are subclass of Point2D, they can be upcast to Point2D.
Point2D.Double
Note: These classes were designed before the introduction of generic in JDK 1.5, which supports
the passing of type as argument.
6.5 "Cannot refer to a non-final variable inside an inner class defined in a different
method"
Java specification 8.1.3: "Any local variable, formal method parameter or exception handler
parameter used but not declared in an inner class must be declared final."
By allowing inner class to access non-final local variables inside a method, the local variable
could be modified by the inner class, and causes a strange side-effect.
Solution:
1. Declare the variable final if permissible.
2. Declare the variable outside the method, e.g., as member variables of the class, instead of
a local variable within a method. Both the method and the inner class could access the
variable.
3. Use a wrapper class to wrap the variable inside a class. Declare the instance final.
6.6 Referencing Outer-class's "this" from Inner-class
Inside the inner class, "this" refers to the inner class. To refer to the "this" of the outer class,
use "OuterClassName.this". But you can reference outer class's members directly without this
clumsy syntax. For example,
......
public class MyOuterClassName {
private String msg = "Hello";
......
public MyOuterClassName() { // constructor
......
Button btn = new Button("TEST");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// Need OuterClassName.this to refer to the outer class.
// But can reference outer class members (e.g., msg) directly.
JOptionPane.showMessageDialog(MyOuterClassName.this, msg);
}
});
}
}
methods in order to compile the program. This is tedious, e.g., we can rewrite the
WindowEventDemo using an inner class implementing ActionListener as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class WindowEventDemoWithInnerClass extends Frame {
private TextField tfCount;
private Button btnCount;
private int count = 0;
// Constructor to setup the GUI components and event handlers
public WindowEventDemoWithInnerClass () {
setLayout(new FlowLayout());
add(new Label("Counter"));
tfCount = new TextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
btnCount = new Button("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
++count;
tfCount.setText(count + "");
}
});
// Allocate an anonymous instance of an anonymous inner class
// that implements WindowListener.
// "super" Frame adds this instance as WindowEvent listener.
addWindowListener(new WindowListener() {
@Override
public void windowClosing(WindowEvent evt) {
System.exit(0); // terminate the program
}
// Need to provide an empty body for compilation
@Override public void windowOpened(WindowEvent evt) { }
@Override public void windowClosed(WindowEvent evt) { }
@Override public void windowIconified(WindowEvent evt) { }
@Override public void windowDeiconified(WindowEvent evt) { }
@Override public void windowActivated(WindowEvent evt) { }
@Override public void windowDeactivated(WindowEvent evt) { }
});
setTitle("WindowEvent Demo");
setSize(250, 100);
setVisible(true);
job
54
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class WindowEventDemoAdapter extends Frame {
private TextField tfCount;
private Button btnCount;
private int count = 0;
// Constructor to setup the GUI components and event handlers
public WindowEventDemoAdapter () {
setLayout(new FlowLayout());
add(new Label("Counter"));
tfCount = new TextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
btnCount = new Button("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
++count;
tfCount.setText(count + "");
}
});
// Allocate an anonymous instance of an anonymous inner class
// that extends WindowAdapter.
// "super" Frame adds the instance as WindowEvent listener.
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent evt) {
System.exit(0); // Terminate the program
}
});
setTitle("WindowEvent Demo");
setSize(250, 100);
setVisible(true);
45
46
47
To set up the layout of a Container (such as Frame, JFrame, Panel, or JPanel), you have to:
1. Construct an instance of the chosen layout object, via new and constructor, e.g., new
FlowLayout())
2. Invoke the setLayout() method of the Container, with the layout object created as the
argument;
3. Place the GUI components into the Container using the add() method in the correct
order; or into the correct zones.
For example,
// Allocate a Panel (container)
Panel p = new Panel();
// Allocate a new Layout object. The Panel container sets to this layout.
p.setLayout(new FlowLayout());
// The Panel container adds components in the proper order.
p.add(new JLabel("One"));
p.add(new JLabel("Two"));
p.add(new JLabel("Three"));
......
Container's getLayout()
(and Swing's JPanel) provides a constructor to set its initial layout manager. It is because
a primary function of Panel is to layout a group of component in a particular layout.
Panel
8.1 FlowLayout
In the java.awt.FlowLayout, components are arranged from left-to-right inside the container in
the order that they are added (via method aContainer.add(aComponent)). When one row is
filled, a new row will be started. The actual appearance depends on the width of the display
window.
Constructors
public FlowLayout();
public FlowLayout(int align);
public FlowLayout(int align, int hgap, int vgap);
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class AWTFlowLayoutDemo extends Frame {
private Button btn1, btn2, btn3, btn4, btn5, btn6;
// Constructor to setup GUI components and event handlers
public AWTFlowLayoutDemo () {
setLayout(new FlowLayout());
// "super" Frame sets layout to FlowLayout, which arranges the
components
// from left-to-right, and flow from top-to-bottom.
btn1 = new
add(btn1);
btn2 = new
add(btn2);
btn3 = new
add(btn3);
btn4 = new
add(btn4);
btn5 = new
add(btn5);
btn6 = new
add(btn6);
Button("Button 1");
Button("This is Button 2");
Button("3");
Button("Another Button 4");
Button("Button 5");
Button("One More Button 6");
8.2 GridLayout
Example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class AWTGridLayoutDemo extends Frame {
private Button btn1, btn2, btn3, btn4, btn5, btn6;
// Constructor to setup GUI components and event handlers
public AWTGridLayoutDemo () {
setLayout(new GridLayout(3, 2, 3, 3));
// "super" Frame sets layout to 3x2 GridLayout, horizontal and
verical gaps of 3 pixels
// The components are added from left-to-right, top-to-bottom
btn1 = new Button("Button 1");
add(btn1);
btn2 = new Button("This is Button 2");
add(btn2);
btn3 = new Button("3");
add(btn3);
btn4 = new Button("Another Button 4");
add(btn4);
btn5 = new Button("Button 5");
add(btn5);
btn6 = new Button("One More Button 6");
add(btn6);
setTitle("GridLayout Demo"); // "super" Frame sets title
setSize(280, 150);
// "super" Frame sets initial size
setVisible(true);
// "super" Frame shows
}
32
33
34
35
36
If rows or cols is 0, but not both, then any number of components can be placed in that column
or row. If both the rows and cols are specified, the cols value is ignored. The actual cols is
determined by the actual number of components and rows.
8.3 BorderLayout
In java.awt.BorderLayout, the container is divided into 5 zones: EAST, WEST, SOUTH, NORTH,
and CENTER. Components are added using method aContainer.add(acomponent,
aZone), where azone is either BorderLayout.NORTH (or PAGE_START), BorderLayout.SOUTH
(or PAGE_END), BorderLayout.WEST (or LINE_START), BorderLayout.EAST (or LINE_END), or
BorderLayout.CENTER. The method aContainer.add(aComponent) without specifying
the zone adds the component to the CENTER.
You need not add components to all the 5 zones. The NORTH and SOUTH components may be
stretched horizontally; the EAST and WEST components may be stretched vertically; the CENTER
component may stretch both horizontally and vertically to fill any space left over.
Constructors
public BorderLayout();
public BorderLayout(int hgap, int vgap);
// By default hgap=0, vgap=0
Example
1
2
3
4
5
6
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class AWTBorderLayoutDemo extends Frame {
private Button btnNorth, btnSouth, btnCenter, btnEast, btnWest;
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 }
An AWT Panel is a rectangular pane, which can be used as sub-container to organized a group
of related components in a specific layout (e.g., FlowLayout, BorderLayout). Panels are
secondary containers, which shall be added into a top-level container (such as Frame), or another
Panel.
For example, the following figure shows a Frame (in BorderLayout) containing two Panels,
panelResult in FlowLayout and panelButtons in GridLayout. panelResult is added to the
NORTH, and panelButtons is added to the CENTER.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.awt.*;
import java.awt.event.*;
// An AWT GUI program inherits the top-level container java.awt.Frame
public class AWTPanelDemo extends Frame {
private Button[] btnNumbers; // Array of 10 numeric Buttons
private Button btnHash, btnStar;
private TextField tfDisplay;
// Constructor to setup GUI components and event handlers
public AWTPanelDemo () {
// Set up display panel
Panel panelDisplay = new Panel(new FlowLayout());
tfDisplay = new TextField("0", 20);
panelDisplay.add(tfDisplay);
// Set up button panel
Panel panelButtons = new Panel(new GridLayout(4, 3));
btnNumbers = new Button[10]; // Construct an array of 10 numeric
Buttons
btnNumbers[1] = new Button("1"); // Construct Button "1"
panelButtons.add(btnNumbers[1]); // The Panel adds this Button
btnNumbers[2] = new Button("2");
panelButtons.add(btnNumbers[2]);
btnNumbers[3] = new Button("3");
panelButtons.add(btnNumbers[3]);
btnNumbers[4] = new Button("4");
panelButtons.add(btnNumbers[4]);
btnNumbers[5] = new Button("5");
panelButtons.add(btnNumbers[5]);
btnNumbers[6] = new Button("6");
panelButtons.add(btnNumbers[6]);
btnNumbers[7] = new Button("7");
panelButtons.add(btnNumbers[7]);
btnNumbers[8] = new Button("8");
panelButtons.add(btnNumbers[8]);
btnNumbers[9] = new Button("9");
panelButtons.add(btnNumbers[9]);
// You should use a loop for the above statements!!!
btnStar = new Button("*");
panelButtons.add(btnStar);
btnNumbers[0] = new Button("0");
panelButtons.add(btnNumbers[0]);
btnHash = new Button("#");
panelButtons.add(btnHash);
setLayout(new BorderLayout()); // "super" Frame sets to BorderLayout
add(panelDisplay, BorderLayout.NORTH);
add(panelButtons, BorderLayout.CENTER);
setTitle("BorderLayout Demo"); // "super" Frame sets title
52
53
54
55
56
57
58
59
setSize(200, 200);
setVisible(true);
8.5 BoxLayout
BoxLayout arrange components in a single row or column. It respects components' requests on
the minimum sizes.
[TODO] Example and diagram
As mentioned earlier, there are two groups of classes in the AWT hierarchy: containers and
components. A container (e.g., Frame, Panel, Dialog, java.applet.Applet) holds components
(e.g., Label, Button, TextField). A container (e.g., Frame and Panel) can also hold subcontainers (e.g. Panel). Hence, we have a situation that "a container can contain containers or
components".
This is quite a common problem: e.g., a directory contains (sub)directories or files; a group
contains (sub)groups or elementary elements; the tree structure. A design pattern has been
proposed for this problem. A design pattern is a proven and possibly the best solution for a
specific class of problems.
As shown in the class diagram, there are two sets of relationship between Container and
Component classes.
1. One-to-many aggregation: A Container contains zero or more Components. Each
Component is contained in exactly one Container.
2. Generalization (or Inheritance): Container is a subclass of Component. In other words,
a Container is a Component, which possesses all the properties of Component and can be
substituted in place of a Component.
Combining both relationships, we have: A Container contains Components. Since a Container
is a Component, a Container can also contain Containers. Consequently, a Container can
contain Containers and Components.
The Gof calls this recursive composition class design "composite design pattern", which is
illustrated as follows:
10. Swing
10.1 Introduction
Swing is part of the so-called "Java Foundation Classes (JFC)" (have you heard of MFC?), which
was introduced in 1997 after the release of JDK 1.1. JFC was subsequently included as an
integral part of JDK since JDK 1.2. JFC consists of:
The goal of Java GUI programming is to allow the programmer to build GUI that looks good on
ALL platforms. JDK 1.0's AWT was awkward and non-object-oriented (using many
event.getSource()). JDK 1.1's AWT introduced event-delegation (event-driven) model, much
clearer and object-oriented. JDK 1.1 also introduced inner class and JavaBeans a component
programming model for visual programming environment (similar to Visual Basic and Dephi).
Swing appeared after JDK 1.1. It was introduced into JDK 1.1 as part of an add-on JFC (Java
Foundation Classes). Swing is a rich set of easy-to-use, easy-to-understand JavaBean GUI
components that can be dragged and dropped as "GUI builders" in visual programming
environment. Swing is now an integral part of Java since JDK 1.2.
10.2 Swing's Features
Swing is huge (consists of 18 API packages as in JDK 1.7) and has great depth. Compared with
AWT, Swing provides a huge and comprehensive collection of reusable GUI components, as
shown in the Figure below (extracted form Swing Tutorial).
The main features of Swing are (extracted from the Swing website):
1. Swing is written in pure Java (except a few classes) and therefore is 100% portable.
2. Swing components are lightweight. The AWT components are heavyweight (in terms of
system resource utilization). Each AWT component has its own opaque native display,
and always displays on top of the lightweight components. AWT components rely heavily
on the underlying windowing subsystem of the native operating system. For example, an
AWT button ties to an actual button in the underlying native windowing subsystem, and
relies on the native windowing subsystem for their rendering and processing. Swing
components (JComponents) are written in Java. They are generally not "weight-down" by
complex GUI considerations imposed by the underlying windowing subsystem.
3. Swing components support pluggable look-and-feel. You can choose between Java lookand-feel and the look-and-feel of the underlying OS (e.g., Windows, UNIX or Mac). If the
later is chosen, a Swing button runs on the Windows looks like a Windows' button and
feels like a Window's button. Similarly, a Swing button runs on the UNIX looks like a
UNIX's button and feels like a UNIX's button.
4. Swing supports mouse-less operation, i.e., it can operate entirely using keyboard.
5. Swing components support "tool-tips".
6. Swing components are JavaBeans a Component-based Model used in Visual
Programming (like Visual Basic). You can drag-and-drop a Swing component into a
"design form" using a "GUI builder" and double-click to attach an event handler.
7. Swing application uses AWT event-handling classes (in package java.awt.event).
Swing added some new classes in package javax.swing.event, but they are not
frequently used.
8. Swing application uses AWT's layout manager (such as FlowLayout and BorderLayout
in package java.awt). It added new layout managers, such as Springs, Struts, and
BoxLayout (in package javax.swing).
9. Swing implements double-buffering and automatic repaint batching for smoother screen
repaint.
10. Swing introduces JLayeredPane and JInternalFrame for creating Multiple Document
Interface (MDI) applications.
11. Swing supports floating toolbars (in JToolBar), splitter control, "undo".
12. Others - check the Swing website.
10.3 Using Swing API
If you understood the AWT programming (such as container/component, event-handling, layout
manager), switching over to Swing (or any other Graphics packages) is straight-forward.
Swing's Components
Compared with the AWT classes (in package java.awt), Swing component classes (in package
javax.swing) begin with a prefix "J", e.g., JButton, JTextField, JLabel, JPanel, JFrame, or
JApplet.
The above figure shows the class hierarchy of the swing GUI classes. Similar to AWT, there are
two groups of classes: containers and components. A container is used to hold components. A
container can also hold containers because it is a (subclass of) component.
As a rule, do not mix heavyweight AWT components and lightweight Swing components in the
same program, as the heavyweight components will always be painted on top of the lightweight
components.
Swing's Top-Level and Secondary Containers
Just like AWT application, a Swing application requires a top-level container. There are three
top-level containers in Swing:
1. JFrame: used for the application's main window (with an icon, a title,
minimize/maximize/close buttons, an optional menu-bar, and a content-pane), as
illustrated.
2. JDialog: used for secondary pop-up window (with a title, a close button, and a contentpane).
3. JApplet: used for the applet's display-area (content-pane) inside a browsers window.
Similarly to AWT, there are secondary containers (such as JPanel) which can be used to group
and layout relevant components.
The Content-Pane of Swing's Top-Level Container
However, unlike AWT, the JComponents shall not be added onto the top-level container (e.g.,
JFrame, JApplet) directly because they are lightweight components. The JComponents must be
added onto the so-called content-pane of the top-level container. Content-pane is in fact a
java.awt.Container that can be used to group and layout components.
You could:
1. get the content-pane via getContentPane() from a top-level container, and add
components onto it. For example,
2. public class TestGetContentPane extends JFrame {
3.
// Constructor
4.
public TestGetContentPane() {
5.
// Get the content-pane of this JFrame, which is a
java.awt.Container
6.
// All operations, such as setLayout() and add() operate on the
content-pane
7.
Container cp = this.getContentPane();
8.
cp.setLayout(new FlowLayout());
9.
cp.add(new JLabel("Hello, world!"));
10.
cp.add(new JButton("Button"));
11.
......
12.
}
13.
.......
}
14. set the content-pane to a JPanel (the main panel created in your application which holds
all your GUI components) via JFrame's setContentPane().
15. public class TestSetContentPane extends JFrame {
16.
// Constructor
17.
public TestSetContentPane() {
18.
// The "main" JPanel holds all the GUI components
19.
JPanel mainPanel = new JPanel(new FlowLayout());
20.
mainPanel.add(new JLabel("Hello, world!"));
21.
mainPanel.add(new JButton("Button"));
22.
23.
// Set the content-pane of this JFrame to the main JPanel
24.
this.setContentPane(mainPanel);
25.
......
26.
}
27.
.......
}
Notes: If a component is added directly into a JFrame, it is added into the content-pane of
JFrame instead, i.e.,
// "this" is a JFrame
add(new JLabel("add to JFrame directly"));
// is executed as
getContentPane().add(new JLabel("add to JFrame directly"));
Event-Handling in Swing
Swing uses the AWT event-handling classes (in package java.awt.event). Swing introduces a
few new event-handling classes (in package javax.swing.event) but they are not frequently
used.
Writing Swing Applications
import java.awt.*;
import java.awt.event.*;
interfaces
import javax.swing.*;
34
35
36
37
38
39
40
41
42
43
44
45
setSize(300, 150);
setVisible(true);
Let's convert the earlier AWT application example into Swing. Compare the two source files and
note the changes (which are highlighted). The display is shown below. Note the differences in
look and feel between the AWT GUI components and Swing's.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.awt.*;
// Using AWT layouts
import java.awt.event.*; // Using AWT event classes and listener interfaces
import javax.swing.*;
// Using Swing components and containers
// A Swing GUI application inherits from top-level container
javax.swing.JFrame
public class SwingCounter extends JFrame {
// JFrame instead of Frame
private JTextField tfCount; // Use Swing's JTextField instead of AWT's
TextField
private JButton btnCount;
// Using Swing's JButton instead of AWT's
Button
private int count = 0;
// Constructor to setup the GUI components and event handlers
public SwingCounter () {
// Retrieve the content-pane of the top-level container JFrame
// All operations done on the content-pane
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
// The content-pane sets its layout
cp.add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
cp.add(tfCount);
23
btnCount = new JButton("Count");
24
cp.add(btnCount);
25
26
// Allocate an anonymous instance of an anonymous inner class that
27
// implements ActionListener as ActionEvent listener
28
btnCount.addActionListener(new ActionListener() {
29
@Override
30
public void actionPerformed(ActionEvent evt) {
31
++count;
32
tfCount.setText(count + "");
33
}
34
});
35
36
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit program if
37
close-window
button clicked
38
setTitle("Swing
Counter"); // "super" JFrame sets title
39
setSize(300,
100);
// "super" JFrame sets initial size
40
setVisible(true);
// "super" JFrame shows
41
}
42
43
// The entry main() method
44
public static void main(String[] args) {
45
// Run the GUI construction in the Event-Dispatching thread for
46
thread-safety
47
SwingUtilities.invokeLater(new Runnable() {
48
@Override
49
public void run() {
50
new SwingCounter(); // Let the constructor do the job
51
}
52
});
}
}
JFrame's Content-Pane
You can also use the JFrame's setContentPane() method to directly set the content-pane to a
JPanel (or a JComponent). For example,
JPanel displayPanel = new JPanel();
this.setContentPane(displayPanel);
// "this" JFrame sets its content-pane to a JPanel directly
.....
// The above is different from:
this.getContentPane().add(displayPanel);
// Add a JPanel into the content-pane. Appearance depends on the
JFrame's layout.
JFrame's setDefaultCloseOperation()
In the previous examples, we invoke the constructor directly in the entry main() method to setup
the GUI components. For example,
// The entry main method
public static void main(String[] args) {
// Invoke the constructor (by allocating an instance) to setup the GUI
new SwingCounter();
}
The constructor will be executed in the so-called "Main-Program" thread. This may cause multithreading issues (such as unresponsive user-interface and deadlock).
It is recommended to execute the GUI setup codes in the so-called "Event-Dispatching" thread,
instead of "Main-Program" thread, for thread-safe operations. Event-dispatching thread, which
processes events, should be used when the codes updates the GUI.
To run the constructor on the event-dispatching thread, invoke static method
SwingUtilities.invokeLater() to asynchronously queue the constructor on the eventdispatching thread. The codes will be run after all pending events have been processed. For
example,
public static void main(String[] args) {
// Run the GUI codes in the Event-dispatching thread for thread-safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new SwingCounter(); // Let the constructor do the job
}
});
}
// version 1
import java.awt.*;
// Using layouts
import java.awt.event.*; // Using AWT event classes and listener interfaces
import javax.swing.*;
// Using Swing components and containers
// A Swing GUI application inherits the top-level container
javax.swing.JFrame
public class SwingAccumulator extends JFrame {
private JTextField tfInput, tfOutput;
private int sum = 0;
// accumulated sum, init to 0
// Constructor to setup the GUI components and event handlers
public SwingAccumulator() {
// Retrieve the content-pane of the top-level container JFrame
// All operations done on the content-pane
Container cp = getContentPane();
13
cp.setLayout(new GridLayout(2, 2, 5, 5)); // The content-pane sets
14 its layout
15
16
add(new JLabel("Enter an Integer: "));
17
tfInput = new JTextField(10);
18
add(tfInput);
19
add(new JLabel("The Accumulated Sum is: "));
20
tfOutput = new JTextField(10);
21
tfOutput.setEditable(false); // read-only
22
add(tfOutput);
23
24
// Allocate an anonymous instance of an anonymous inner class that
25
// implements ActionListener as ActionEvent listener
26
tfInput.addActionListener(new ActionListener() {
27
@Override
28
public void actionPerformed(ActionEvent evt) {
29
// Get the String entered into the input TextField, convert to
30
int
31
int numberIn = Integer.parseInt(tfInput.getText());
32
sum += numberIn;
// accumulate numbers entered into sum
33
tfInput.setText(""); // clear input TextField
34
tfOutput.setText(sum + ""); // display sum on the output
35
TextField
36
}
37
});
38
39
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit program if
40
close-window
button clicked
41
setTitle("Swing
Accumulator"); // "super" Frame sets title
42
setSize(350,
120);
// "super" Frame sets initial size
43
setVisible(true);
// "super" Frame shows
44
}
45
46
// The entry main() method
47
public static void main(String[] args) {
48
// Run the GUI construction in the Event-Dispatching thread for
49
50 thread-safety
SwingUtilities.invokeLater(new Runnable() {
51
@Override
52
public void run() {
53
new SwingAccumulator(); // Let the constructor do the job
54
}
});
}
}
If you have a complicated layout for your GUI application, you should use a GUI Builder, such
as NetBeans or Eclipse to layout your GUI components in a drag-and-drop manner, similar to the
popular visual languages such as Visual Basic and Dephi.
11.1 NetBeans
For using NetBeans GUI Builder, read my "Writing Java GUI (AWT/Swing) Application in
NetBeans"; or Swing Tutorial's "Learning Swing with the NetBeans IDE".
11.2 Eclipse
For using Eclipse GUI Builder, read "Writing Swing Applications using Eclipse GUI Builder".
LINK TO JAVA REFERENCES & RESOURCES
MORE REFERENCES & RESOURCES
1. "Creating a GUI With JFC/Swing" (aka "The Swing Tutorial") @
http://docs.oracle.com/javase/tutorial/uiswing/.
2. JFC Demo (under JDK demo "jfc" directory).
3. Java2D Tutorial @ http://docs.oracle.com/javase/tutorial/2d/index.html.
4. JOGL (Java Binding on OpenGL) @ http://java.net/projects/jogl/.
5. Java3D (@ http://java3d.java.net/).
Sumber :
http://www3.ntu.edu.sg/home/ehchua/programming/java/j4a_gui.html