User Interface Programming
User Interface Programming
| HOME
TABLE OF CONTENTS (HIDE)
1. Introduction
2. Programming GUI with AWT
2.1 AWT Packages
2.2 Containers and Components
2.3 AWT Container Classes
2.4 AWT Component Classes
2.5 Example 1: AWTCounter
2.6 Example 2: AWTAccumulator
3. AWT Event-Handling
3.1 Revisit Example 1 AWTCounter: ActionEvent and ActionListener Interface
3.2 Revisit Example 2 AWTAccumulator: ActionEvent and ActionListener Interface
3.3 Example 3: WindowEvent and WindowListener Interface
3.4 Example 4: MouseEvent and MouseListener Interface
3.5 Example 5: MouseEvent and MouseMotionListener Interface
3.6 Example 6: KeyEvent and KeyListener Interface
4. (Advanced) Observer Design Pattern
4.1 Example: Creating Your Own Event, Source and Listener
5. Nested (Inner) Classes
5.1 Example 7: A Named Inner Class as Event Listener
5.2 Example 8: An Anonymous Inner Class as Event Listener
5.3 Example 9: An Anonymous Inner Class for Each Source
5.4 Example 10: Using the Same Listener Instance for All the Buttons
6. (Advanced) More on Nested Classes
6.1 Static vs. Instance Nested Classes
6.2 Local Inner Class Defined Inside a Method
6.3 An Anonymous Inner Class
6.4 Example of Static Nested Class in JDK: Point2D, Point2D.Double, Point2D.Float, Point
6.5 "Cannot refer to a non-final variable inside an inner class defined in a different method"
6.6 Referencing Outer-class's "this" from Inner-class
7. Event Listener's Adapter Classes
7.1 Example 11: WindowAdapter for WindowListener
7.2 Other Event-Listener Adapter Classes
8. Layout Managers and Panel
8.1 FlowLayout
8.2 GridLayout
8.3 BorderLayout
8.4 Using Panels as Sub-Container to Organize Components
8.5 BoxLayout
9. (Advanced) Composite Design Pattern
10. Swing
10.1 Introduction
10.2 Swing's Features
10.3 Using Swing API
10.4 Swing Program Template
10.5 Swing Example 1: SwingCounter
10.6 Swing Example 2: SwingAccumulator
11. Using Visual GUI Builder - NetBeans/Eclipse
11.1 NetBeans
11.2 Eclipse
Java Graphics APIs - AWT and Swing - provide a huge set of reusable GUI components, such as
button, text field, label, choice, panel and frame for building GUI applications. You can simply
reuse these classes rather than re-invent the wheels. I shall start with the AWT classes before
moving into Swing to give you a complete picture. I have to stress that AWT component classes
are now obsoleted by Swing's counterparts.
2.1 AWT Packages
AWT is huge! It consists of 12 packages (Swing is even bigger, with 18 packages as of JDK
1.7!). Fortunately, only 2 packages - java.awt and java.awt.event - are commonly-used.
1. The java.awt package contains the core AWT graphics classes:
o GUI Component classes (such as Button, TextField, and Label),
o GUI Container classes (such as Frame, Panel, Dialog and ScrollPane),
o Layout managers (such as FlowLayout, BorderLayout and GridLayout),
o Custom graphics classes (such as Graphics, Color and Font).
2. The java.awt.event package supports event handling:
o Event classes (such as ActionEvent, MouseEvent, KeyEvent and WindowEvent),
o Event Listener Interfaces (such
as ActionListener, MouseListener, KeyListener and WindowListener),
o Event Listener Adapter classes (such as MouseAdapter, KeyAdapter,
and WindowAdapter).
AWT provides a platform-independent and device-independent interface to develop graphic
programs that runs on all platforms, such as Windows, Mac, and Linux.
2.2 Containers and Components
A Frame pro
vides 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 fromjava.awt.Frame to inherit the main window as follows:
import java.awt.Frame;// Using Frame class in package java.awt
// A GUI program is written as a subclass of Frame - the top-level container
// This subclass inherits all properties from Frame, e.g., title, icon, buttons, content-pane
public class MyGUIProgram extends Frame {
// Constructor to setup the GUI components
public MyGUIProgram() { ...... }
// Other methods
......
......
// The entry main() method
public static void main(String[] args) {
// Invoke the constructor (to setup the GUI) by allocating an instance
new MyGUIProgram();
}
A java.awt.Label provides a text description message. Take note that System.out.println() prints
to the system console, not to the graphics screen. You could use a Label to label another
component (such as text field) or provide a text description.
Check the JDK API specification for java.awt.Label.
Constructors
public Label(String strLabel, int alignment); // Construct a Label with the given text String, of
the text alignment
public Label(String strLabel); // Construct a Label with the given text String
public Label(); // Construct an initially empty Label
The Label class has three constructors:
1. The first constructor constructs a Label object with the given text string in the given
alignment. Note that three static constants Label.LEFT, Label.RIGHT,
and Label.CENTER are defined in the class for you to specify the alignment (rather than
asking you to memorize arbitrary integer values).
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; // Label.LEFT
public static final RIGHT; // Label.RIGHT
public static final CENTER; // 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
Three steps are necessary to create and place a GUI component:
1. Declare the component with an identifier (name);
2. Construct the component by invoking an appropriate constructor via the new operator;
3. Identify the container (such as Frame or Panel) designed to hold this component. The
container can then add this component onto itself
via aContainer.add(aComponent) method. Every container has a add(Component) method.
Take note that it is the container that actively and explicitly adds a component onto itself,
instead of the other way.
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
A java.awt.Button is a GUI component that triggers a certain programmed action upon clicking.
Constructors
public Button(String buttonLabel);
// Construct a Button with the given label
public Button();
// Construct a Button with empty label
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
public String getLabel();
// Get the label of this Button instance
public void setLabel(String buttonLabel);
// Set the label of this Button instance
public void setEnable(boolean enable);
// Enable or disable this Button. Disabled Button cannot be clicked.
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"); // Declare and allocate a Button instance called btnColor
add(btnColor); // "this" Container adds the Button
...
btnColor.setLabel("green"); // Change the button's label
btnColor.getLabel(); // Read the button's label
...
add(Button("Blue")); // Create an anonymous Button. It CANNOT be referenced later
AWT GUI Component: java.awt.TextField
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 action-event.
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 TextField(int columns);
// Construct a TextField instance with the number of columns.
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 non-
editable 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.
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)
3. AWT Event-Handling
Java adopts the so-called "Event-Driven" (or "Event-Delegation") programming model for event-
handling, 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 observable-
observer design pattern.
The sequence of steps is illustrated above:
1. The source object registers its listener(s) for a certain type of event.
Source object fires event event upon triggered. For example, clicking an Button fires
an ActionEvent, mouse-click fires MouseEvent, key-type fires KeyEvent, etc.
How the source and listener understand each other? The answer is via an agreed-upon
interface. For example, if a source is capable of firing an event
called XxxEvent (e.g., MouseEvent) involving various operational modes (e.g., mouse-
clicked, mouse-entered, mouse-exited, mouse-pressed, and mouse-released). Firstly, we
need to declare an interface called XxxListener (e.g., MouseListener) containing the names
of the handler methods. Recall that an interfacecontains only abstract methods without
implementation. For example,
// A MouseListener interface, which declares the signature of the handlers
// for the various operational modes.
publicinterface MouseListener {
public void mousePressed(MouseEvent evt); // Called back upon mouse-button pressed
public void mouseReleased(MouseEvent evt); // Called back upon mouse-button released
public void mouseClicked(MouseEvent evt); // Called back upon mouse-button clicked
(pressed and released)
public void mouseEntered(MouseEvent evt); // Called back when mouse pointer entered
the component
public void mouseExited(MouseEvent evt); // Called back when mouse pointer exited
the component
}
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
theXxxListener 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:
public void addXxxListener(XxxListener l);
public void removeXxxListener(XxxListener l);
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); // aSource registers aListener for 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);
Note that addActionListener() takes an argument of the type ActionListener. "this", which
implements ActionListener interface (i.e., a subclass of ActionListener), is upcasted and
passed to the addActionListener() method.
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:
ActionEvent evt = new ActionEvent( ...... );
In this example,
1. We identify the tfInput (TextField) as the source object.
2. Hitting the "Enter" key on a TextField fires an ActionEvent to all
its ActionEvent listener(s).
3. We choose this object as the ActionEvent listener (for simplicity).
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 e)
// 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 e)
// Called-back the first time a window is made visible.
public void windowClosed(WindowEvent e)
// Called-back when a window has been closed as the result of calling dispose on the window.
public void windowActivated(WindowEvent e)
// Called-back when the Window is set to be the active Window.
public void windowDeactivated(WindowEvent e)
// Called-back when a Window is no longer the active Window.
public void windowIconified(WindowEvent e)
// Called-back when a window is changed from a normal to a minimized state.
public void windowDeiconified(WindowEvent e)
// 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.
1import java.awt.*; // Using AWT containers and components
2import java.awt.event.*; // Using AWT events and listener interfaces
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class WindowEventDemo extends Frame
6implements ActionListener, WindowListener {
7 // This class acts as listener for ActionEvent and WindowEvent
8 // Java supports only single inheritance, where a class can extend
9 // one superclass, but can implement multiple interfaces.
10
11 private TextField tfCount;
12 private Button btnCount;
13 private int count = 0; // Counter's value
14
15/** Constructor to setup the UI components and event handling */
16 public WindowEventDemo() {
17 setLayout(new FlowLayout()); // "super" Frame sets to FlowLayout
18
19 add(new Label("Counter")); // "super" Frame adds an anonymous Label
20
21 tfCount = new TextField("0", 10); // Allocate TextField
22 tfCount.setEditable(false); // read-only
23 add(tfCount); // "super" Frame adds tfCount
24
25 btnCount = new Button("Count"); // Declare and allocate a Button
26 add(btnCount); // "super" Frame adds btnCount
27
28 btnCount.addActionListener(this);
29 // btnCount fires ActionEvent to its registered ActionEvent listener
30 // btnCount adds "this" object as an ActionEvent listener
31
32addWindowListener(this);
33 // "super" Frame fires WindowEvent to its registered WindowEvent listener
34 // "super" Frame adds "this" object as a WindowEvent listener
35
36 setTitle("WindowEvent Demo"); // "super" Frame sets title
37 setSize(250, 100); // "super" Frame sets initial size
38 setVisible(true); // "super" Frame shows
39 }
40
41/** The entry main() method */
42 public static void main(String[] args) {
43 new WindowEventDemo(); // Let the construct do the job
44 }
45
46/** ActionEvent handler */
47 @Override
48 public void actionPerformed(ActionEvent evt) {
49 ++count;
50 tfCount.setText(count + "");
51 }
52
53/** WindowEvent handlers */
54 // Called back upon clicking close-window button
55@Override
56 public void windowClosing(WindowEvent e) {
57 System.exit(0); // Terminate the program
58 }
59
60 // Not Used, but need to provide an empty body
61 @Override
62 public void windowOpened(WindowEvent e) { }
63 @Override
64 public void windowClosed(WindowEvent e) { }
65 @Override
66 public void windowIconified(WindowEvent e) { }
67 @Override
68 public void windowDeiconified(WindowEvent e) { }
69 @Override
70 public void windowActivated(WindowEvent e) { }
71 @Override
72 public void windowDeactivated(WindowEvent e) { }
73}
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(), wind
owDeactivated(),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:
3.4 Example 4: MouseEvent and MouseListener Interface
A MouseEvent is fired to all its registered listeners, when you press, release, or click (press
followed by release) a mouse-button (left or right button) at the source object; or position the
mouse-pointer at (enter) and away (exit) from the source object.
A MouseEvent listener must implement the MouseListener interface, which declares the
following five abstract methods:
public void mouseClicked(MouseEvent e)
// Called-back when the mouse-button has been clicked (pressed followed by released) on the
source.
public void mousePressed(MouseEvent e)
public void mouseReleased(MouseEvent e)
// Called-back when a mouse-button has been pressed/released on the source.
// A mouse-click invokes mousePressed(), mouseReleased() and mouseClicked().
public void mouseEntered(MouseEvent e)
public void mouseExited(MouseEvent e)
// Called-back when the mouse-pointer has entered/exited the source.
1import java.awt.*;
2import java.awt.event.MouseEvent;
3import java.awt.event.MouseListener;
4
5public class MouseEventDemo extends Frame implements MouseListener {
6
7 // Private variables
8 private TextField tfMouseX; // to display mouse-click-x
9 private TextField tfMouseY; // to display mouse-click-y
10
11 // Constructor - Setup the UI
12 public MouseEventDemo() {
13 setLayout(new FlowLayout()); // "super" frame sets layout
14
15 // Label
16 add(new Label("X-Click: ")); // "super" frame adds component
17
18 // TextField
19 tfMouseX = new TextField(10); // 10 columns
20 tfMouseX.setEditable(false); // read-only
21 add(tfMouseX); // "super" frame adds component
22
23 // Label
24 add(new Label("Y-Click: ")); // "super" frame adds component
25
26 // TextField
27 tfMouseY = new TextField(10);
28 tfMouseY.setEditable(false); // read-only
29 add(tfMouseY); // "super" frame adds component
30
31addMouseListener(this);
32 // "super" frame fires the MouseEvent
33 // "super" frame adds "this" object as MouseEvent listener
34
35 setTitle("MouseEvent Demo"); // "super" Frame sets title
36 setSize(350, 100); // "super" Frame sets initial size
37 setVisible(true); // "super" Frame shows
38 }
39
40 public static void main(String[] args) {
41 new MouseEventDemo(); // Let the constructor do the job
42 }
43
44 // MouseEvent handlers
45@Override
46 public void mouseClicked(MouseEvent e) {
47 tfMouseX.setText(e.getX() + "");
48 tfMouseY.setText(e.getY() + "");
49 }
50
51 @Override
52 public void mousePressed(MouseEvent e) { }
53 @Override
54 public void mouseReleased(MouseEvent e) { }
55 @Override
56 public void mouseEntered(MouseEvent e) { }
57 @Override
58 public void mouseExited(MouseEvent e) { }
59}
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 themouseClicked() 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.
1import java.awt.*;
2import java.awt.event.MouseEvent;
3import java.awt.event.MouseListener;
4import java.awt.event.MouseMotionListener;
5
6// An AWT GUI program inherits from the top-level container java.awt.Frame
7public class MouseMotionDemo extends Frame
8implements MouseListener, MouseMotionListener {
9// This class acts as MouseListener and MouseMotionListener
10
11// To display the (x, y) coordinates of the mouse-clicked
12 private TextField tfMouseClickX;
13 private TextField tfMouseClickY;
14// To display the (x, y) coordinates of the current mouse-pointer position
15 private TextField tfMousePositionX;
16 private TextField tfMousePositionY;
17
18/** Constructor to setup the GUI */
19 public MouseMotionDemo() {
20 setLayout(new FlowLayout()); // "this" frame sets to FlowLayout
21
22 add(new Label("X-Click: "));
23 tfMouseClickX = new TextField(10);
24 tfMouseClickX.setEditable(false);
25 add(tfMouseClickX);
26 add(new Label("Y-Click: "));
27 tfMouseClickY = new TextField(10);
28 tfMouseClickY.setEditable(false);
29 add(tfMouseClickY);
30
31 add(new Label("X-Position: "));
32 tfMousePositionX = new TextField(10);
33 tfMousePositionX.setEditable(false);
34 add(tfMousePositionX);
35 add(new Label("Y-Position: "));
36 tfMousePositionY = new TextField(10);
37 tfMousePositionY.setEditable(false);
38 add(tfMousePositionY);
39
40 addMouseListener(this);
41addMouseMotionListener(this);
42// "super" frame fires MouseEvent to all its registered MouseListener and MouseMotionListener
43// "super" frame adds "this" object as MouseListener and MouseMotionListener
44
45 setTitle("MouseMotion Demo"); // "super" Frame sets title
46 setSize(400, 120); // "super" Frame sets initial size
47 setVisible(true); // "super" Frame shows
48 }
49
50/** The entry main() method */
51 public static void main(String[] args) {
52 new MouseMotionDemo(); // Let the constructor do the job
53 }
54
55/** MouseListener handlers */
56// Called back when a mouse-button has been clicked
57 @Override
58 public void mouseClicked(MouseEvent e) {
59 tfMouseClickX.setText(e.getX() + "");
60 tfMouseClickY.setText(e.getY() + "");
61 }
62
63// Not Used, but need to provide an empty body for compilation
64 @Override
65 public void mousePressed(MouseEvent e) { }
66 @Override
67 public void mouseReleased(MouseEvent e) { }
68 @Override
69 public void mouseEntered(MouseEvent e) { }
70 @Override
71 public void mouseExited(MouseEvent e) { }
72
73/** MouseMotionEvent handlers */
74// Called back when the mouse-pointer has been moved
75@Override
76 public void mouseMoved(MouseEvent e) {
77 tfMousePositionX.setText(e.getX() + "");
78 tfMousePositionY.setText(e.getY() + "");
79 }
80
81// Not Used, but need to provide an empty body for compilation
82 @Override
83 public void mouseDragged(MouseEvent e) { }
84}
In this example, we shall illustrate both the MouseListener and MouseMotionListener.
1. We identify super Frame as the source, which fires the MouseEvent to its
registered MouseListener and MouseMotionListener.
2. We select this object as the MouseListener and MouseMotionListner (for simplicity).
3. We register this object as the listener to super Frame via
method addMouseListener(this) and addMouseMotionListener(this).
4. The MouseMotionListener (this class) needs to implement 2 abstract
methods: mouseMoved() and mouseDragged() declared in
the MouseMotionListener interface.
5. We override the mouseMoved() to display the (x, y) position of the mouse pointer. We
ignore the MouseDragged() handler by providing an empty body for compilation.
Try: Include a WindowListener to handle the close-window button.
3.6 Example 6: KeyEvent and KeyListener Interface
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.
1import java.awt.*;
2import java.awt.event.KeyEvent;
3import java.awt.event.KeyListener;
4
5// An AWT GUI program inherits from the top-level container java.awt.Frame
6public class KeyEventDemo extends Frame implements KeyListener {
7// This class acts as KeyEvent Listener
8
9 private TextField tfInput; // single-line TextField to receive tfInput key
10 private TextArea taDisplay; // multi-line TextArea to taDisplay result
11
12/** Constructor to setup the GUI */
13 public KeyEventDemo() {
14 setLayout(new FlowLayout()); // "super" frame sets to FlowLayout
15
16 add(new Label("Enter Text: "));
17 tfInput = new TextField(10);
18 add(tfInput);
19 taDisplay = new TextArea(5, 40); // 5 rows, 40 columns
20 add(taDisplay);
21
22tfInput.addKeyListener(this);
23// tfInput TextField fires KeyEvent to its registered KeyListeners
24// tfInput adds "this" object as a KeyEvent listener
25
26 setTitle("KeyEvent Demo"); // "super" Frame sets title
27 setSize(400, 200); // "super" Frame sets initial size
28 setVisible(true); // "super" Frame shows
29 }
30
31/** The entry main() method */
32 public static void main(String[] args) {
33 new KeyEventDemo(); // Let the constructor do the job
34 }
35
36/** KeyEvent handlers */
37// Called back when a key has been typed (pressed and released)
38@Override
39 public void keyTyped(KeyEvent e) {
40 taDisplay.append("You have typed " + e.getKeyChar() + "\n");
41 }
42
43// Not Used, but need to provide an empty body for compilation
44 @Override
45 public void keyPressed(KeyEvent e) { }
46 @Override
47 public void keyReleased(KeyEvent e) { }
48}
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().
4. (Advanced) Observer Design Pattern
The inner class can access the private variable tfCount and count of the outer class.
Since "this" is no longer a listener, we remove the "implements ActionListener"
from this class' definition.
The inner class is compiled into AWTCount$BtnCountListener.class, in the format
of OuterClassName$InnerClassName.class.
Using an Ordinary (Outer) Class as Listener
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 andcount, 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 e) {
frame.count++;
frame.tfCount.setText(frame.count + "");
}
}
});
// Or
Nn = new N()
btnCount.addActionListener(n);
The above codes create an anonymous instance of an anonymous inner class, and pass it as
the argument for a method. You can also create a named instance of an anonymous inner
class, for example,
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.
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class AWTCounter3Buttons extends Frame {
6 private TextField tfCount;
7 private int count = 0;
8
9/** Constructor to setup the GUI */
10 public AWTCounter3Buttons () {
11 setLayout(new FlowLayout());
12 add(new Label("Counter")); // an anonymous instance of Label
13 tfCount = new TextField("0", 10);
14 tfCount.setEditable(false); // read-only
15 add(tfCount); // "super" Frame adds tfCount
16
17 Button 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 listener
21btnCountUp.addActionListener(new ActionListener() {
22 @Override
23 public void actionPerformed(ActionEvent e) {
24 ++count;
25 tfCount.setText(count + "");
26 }
27 });
28
29 Button btnCountDown = new Button("Count Down");
30 add(btnCountDown);
31btnCountDown.addActionListener(new ActionListener() {
32 @Override
33 public void actionPerformed(ActionEvent e) {
34 count--;
35 tfCount.setText(count + "");
36 }
37 });
38
39 Button btnReset = new Button("Reset");
40 add(btnReset);
41btnReset.addActionListener(new ActionListener() {
42 @Override
43 public void actionPerformed(ActionEvent e) {
44 count = 0;
45 tfCount.setText("0");
46 }
47 });
48
49 setTitle("AWT Counter");
50 setSize(400, 100);
51 setVisible(true);
52 }
53
54/** The entry main method */
55 public static void main(String[] args) {
56 new AWTCounter3Buttons(); // Let the constructor do the job
57 }
58}
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'sgetActionCommonad() method, which returns the button's
label.
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class AWTCounter3Buttons1Listener extends Frame {
6 private TextField tfCount;
7 private int count = 0;
8
9/** Constructor to setup the GUI */
10 public AWTCounter3Buttons1Listener () {
11 setLayout(new FlowLayout());
12 add(new Label("Counter"));
13 tfCount = new TextField("0", 10);
14 tfCount.setEditable(false);
15 add(tfCount);
16
17// Create buttons
18 Button btnCountUp = new Button("Count Up");
19 add(btnCountUp);
20 Button btnCountDown = new Button("Count Down");
21 add(btnCountDown);
22 Button 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 to all the 3 buttons.
28 btnCountUp.addActionListener(listener);
29 btnCountDown.addActionListener(listener);
30 btnReset.addActionListener(listener);
31
32 setTitle("AWT Counter");
33 setSize(400, 100);
34 setVisible(true);
35 }
36
37/** The entry main method */
38 public static void main(String[] args) {
39 new AWTCounter3Buttons1Listener(); // Let the constructor do the job
40 }
41
42/**
43 * BtnListener is a named inner class used as ActionEvent listener for the buttons.
44 */
45private class BtnListener implements ActionListener {
46 @Override
47 public void actionPerformed(ActionEvent e) {
48// Need to determine which button has fired the event.
49// getActionCommand() returns the button's label
50 String btnLabel = e.getActionCommand();
51 if (btnLabel.equals("Count Up")) {
52 ++count;
53 } else if (btnLabel.equals("Count Down")) {
54 --count;
55 } else {
56 count = 0;
57 }
58 tfCount.setText(count + "");
59 }
60 }
61}
Using getSource() of EventObject
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,
1import java.awt.*;
2import java.awt.event.*;
3
4public 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 */
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// Create 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 to all the 3 buttons.
28 btnCountUp.addActionListener(listener);
29 btnCountDown.addActionListener(listener);
30 btnReset.addActionListener(listener);
31
32 setTitle("AWT Counter");
33 setSize(400, 100);
34 setVisible(true);
35 }
36
37/** The entry main method */
38 public static void main(String[] args) {
39 new AWTCounter3ButtonsGetSource(); // Let the constructor do the job
40 }
41
42/**
43 * BtnListener is a named inner class used as ActionEvent listener for the buttons.
44 */
45private class BtnListener implements ActionListener {
46 @Override
47 public void actionPerformed(ActionEvent e) {
48// Need to determine which button has fired the event.
49 Button source = (Button)e.getSource();
50// Get a reference of the source that has fired the event.
51// getSource() returns a java.lang.Object. Downcast back to Button.
52 if (source == btnCountUp) {
53 ++count;
54 } else if (source == btnCountDown) {
55 --count;
56 } else {
57 count = 0;
58 }
59 tfCount.setText(count + "");
60 }
61 }
62}
6. (Advanced) More on Nested Classes
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.
1public class MyOuterClassWithStaticNestedClass {
2// Private "static" member variable of the outer class
3 private static String msgOuter = "Hello from outer class";
4
5// Define a "static" nested class as a member of the outer class
6// It can access private "static" variable of the outer class
7public static class MyStaticNestedClass {
8// Private variable of inner class
9 private String msgInner;
10// Constructor of inner class
11 public MyStaticNestedClass(String msgInner) {
12 this.msgInner = msgInner;
13 System.out.println(msgOuter); // access private member of the 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.
1public class TestStaticNestedClass {
2 public static void main(String[] args) {
3// Construct an instance of static nested class
4// A "static" nested class, like other "static" members, can be accessed via
5// the Classname.membername
6 MyOuterClassWithStaticNestedClass.MyStaticNestedClass aNestedInner =
7 new MyOuterClassWithStaticNestedClass.MyStaticNestedClass("Hi from inner class");
8 aNestedInner.printMessage();
9 }
10}
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
1public 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: " + msgOuter);
19// can access private member variable of outer class
20 System.out.println("Accessing final variable of the method: " + msgMethod);
21// can access final variable of the method
22 }
23// A method of inner class
24 public void printMessage() {
25 System.out.println(msgInner);
26 }
27 }
28
29// Create an instance of inner class and invoke its method
30 MyInnerClass anInner = new MyInnerClass("Hi, from inner class");
31 anInner.printMessage();
32 }
33
34// Test main() method
35 public static void main(String[] args) {
36// Create an instance of the outer class and invoke the method.
37 new MyOuterClassWithLocalInnerClass().doSomething();
38 }
39}
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);
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);
}
});
}
}
7. Event Listener's Adapter Classes
7.1 Example 11: WindowAdapter for WindowListener
Refer to the WindowEventDemo, a WindowEvent listener is required to implement
the WindowListener interface, which declares 7 abstract methods. Although we are only
interested in windowClosing(), we need to provide an empty body to the other 6 methods in
order to compile the program. This is tedious. For example, we can rewrite
the WindowEventDemo using an inner class implementing ActionListener as follows:
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class WindowEventDemoWithInnerClass extends Frame {
6 private TextField tfCount;
7 private int count = 0;
8
9/** Constructor to setup the GUI */
10 public WindowEventDemoWithInnerClass () {
11 setLayout(new FlowLayout());
12 add(new Label("Counter"));
13 tfCount = new TextField("0", 10);
14 tfCount.setEditable(false);
15 add(tfCount);
16
17 Button btnCount = new Button("Count");
18 add(btnCount);
19 btnCount.addActionListener(new ActionListener() {
20 @Override
21 public void actionPerformed(ActionEvent evt) {
22 ++count;
23 tfCount.setText(count + "");
24 }
25 });
26
27// Allocate an anonymous instance of an anonymous inner class
28// that implements WindowListener.
29// "this" Frame adds the instance as WindowEvent listener.
30addWindowListener(new WindowListener() {
31 @Override
32 public void windowClosing(WindowEvent e) {
33 System.exit(0); // terminate the program
34 }
35// Need to provide an empty body for compilation
36 @Override public void windowOpened(WindowEvent e) { }
37 @Override public void windowClosed(WindowEvent e) { }
38 @Override public void windowIconified(WindowEvent e) { }
39 @Override public void windowDeiconified(WindowEvent e) { }
40 @Override public void windowActivated(WindowEvent e) { }
41 @Override public void windowDeactivated(WindowEvent e) { }
42 });
43
44 setTitle("WindowEvent Demo");
45 setSize(250, 100);
46 setVisible(true);
47 }
48
49/** The entry main method */
50 public static void main(String[] args) {
51 new WindowEventDemoWithInnerClass(); // Let the constructor do the job
52 }
53}
An adapter class called WindowAdapter is therefore provided, which implements
the WindowListener interface and provides default implementations to all the
7 abstract methods. You can then derive a subclass from WindowAdapter and override only
methods of interest and leave the rest to their default implementation. For example,
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class WindowEventDemoAdapter extends Frame {
6 private TextField tfCount;
7 private int count = 0;
8
9/** Constructor to setup the GUI */
10 public WindowEventDemoAdapter () {
11 setLayout(new FlowLayout());
12 add(new Label("Counter"));
13 tfCount = new TextField("0", 10);
14 tfCount.setEditable(false);
15 add(tfCount);
16
17 Button btnCount = new Button("Count");
18 add(btnCount);
19 btnCount.addActionListener(new ActionListener() {
20 @Override
21 public void actionPerformed(ActionEvent evt) {
22 ++count;
23 tfCount.setText(count + "");
24 }
25 });
26
27// Allocate an anonymous instance of an anonymous inner class
28// that extends WindowAdapter.
29// "this" Frame adds the instance as WindowEvent listener.
30addWindowListener(new WindowAdapter() {
31 @Override
32 public void windowClosing(WindowEvent e) {
33 System.exit(0); // Terminate the program
34 }
35 });
36
37 setTitle("WindowEvent Demo");
38 setSize(250, 100);
39 setVisible(true);
40 }
41
42/** The entry main method */
43 public static void main(String[] args) {
44 new WindowEventDemoAdapter(); // Let the constructor do the job
45 }
46}
7.2 Other Event-Listener Adapter Classes
Similarly, adapter classes such
as MouseAdapter, MouseMotionAdapter, KeyAdapter, FocusAdapter are available
for MouseListener, MouseMotionListener, KeyListener, and FocusListener, respectively.
There is no ActionAdapter for ActionListener, because there is only one abstract method
(i.e. actionPerformed()) declared in the ActionListener interface. This method has to be
overridden and there is no need for an adapter.
8. Layout Managers and Panel
A container has a so-called layout manager to arrange its components. The layout managers
provide a level of abstraction to map your user interface on all windowing systems, so that the
layout can be platform-independent.
AWT provides the following layout managers (in
package java.awt): FlowLayout, GridLayout, BorderLayout, GridBagLayout, BoxLayout, CardL
ayout, and others. (Swing added more layout manager in package javax.swing, to be described
later.)
Container's setLayout()
A container has a setLayout() method to set its layout manager:
// java.awt.Container
public void setLayout(LayoutManager mgr)
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,
public FlowLayout();
public FlowLayout(int align);
public FlowLayout(int align, int hgap, int vgap);
// align: FlowLayout.LEFT (or LEADING), FlowLayout.RIGHT (or TRAILING), or
FlowLayout.CENTER
// hgap, vgap: horizontal/vertical gap between the components
// By default: hgap=5, vgap=5, align=CENTER
Example
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class AWTFlowLayoutDemo extends Frame {
6 private Button btn1, btn2, btn3, btn4, btn5, btn6;
7
8/** Constructor to setup GUI components */
9 public AWTFlowLayoutDemo () {
10 setLayout(new FlowLayout());
11 // "this" Frame sets layout to FlowLayout, which arranges the components
12 // from left-to-right, and flow from top-to-bottom.
13
14 btn1 = new Button("Button 1");
15 add(btn1);
16 btn2 = new Button("This is Button 2");
17 add(btn2);
18 btn3 = new Button("3");
19 add(btn3);
20 btn4 = new Button("Another Button 4");
21 add(btn4);
22 btn5 = new Button("Button 5");
23 add(btn5);
24 btn6 = new Button("One More Button 6");
25 add(btn6);
26
27 setTitle("FlowLayout Demo"); // "this" Frame sets title
28 setSize(280, 150); // "this" Frame sets initial size
29 setVisible(true); // "this" Frame shows
30 }
31
32/** The entry main() method */
33 public static void main(String[] args) {
34 new AWTFlowLayoutDemo(); // Let the constructor do the job
35 }
36}
8.2 GridLayout
In java.awt.GridLayout, components are arranged in a grid (matrix) of rows and columns inside
the Container. Components are added in a left-to-right, top-to-bottom manner in the order they
are added (via method aContainer.add(aComponent)).
Constructors
Example
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class AWTGridLayoutDemo extends Frame {
6 private Button btn1, btn2, btn3, btn4, btn5, btn6;
7
8/** Constructor to setup GUI components */
9 public AWTGridLayoutDemo () {
10 setLayout(new GridLayout(3, 2, 3, 3));
11 // "this" Frame sets layout to 3x2 GridLayout, horizontal and verical gaps of 3 pixels
12
13 // The components are added from left-to-right, top-to-bottom
14 btn1 = new Button("Button 1");
15 add(btn1);
16 btn2 = new Button("This is Button 2");
17 add(btn2);
18 btn3 = new Button("3");
19 add(btn3);
20 btn4 = new Button("Another Button 4");
21 add(btn4);
22 btn5 = new Button("Button 5");
23 add(btn5);
24 btn6 = new Button("One More Button 6");
25 add(btn6);
26
27 setTitle("GridLayout Demo"); // "this" Frame sets title
28 setSize(280, 150); // "this" Frame sets initial size
29 setVisible(true); // "this" Frame shows
30 }
31
32/** The entry main() method */
33 public static void main(String[] args) {
34 new AWTGridLayoutDemo(); // Let the constructor do the job
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 androws.
8.3 BorderLayout
public BorderLayout();
public BorderLayout(int hgap, int vgap);
// By default hgap=0, vgap=0
Example
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class AWTBorderLayoutDemo extends Frame {
6 private Button btnNorth, btnSouth, btnCenter, btnEast, btnWest;
7
8/** Constructor to setup GUI components */
9 public AWTBorderLayoutDemo () {
10 setLayout(new BorderLayout(3, 3));
11 // "this" Frame sets layout to BorderLayout,
12 // horizontal and vertical gaps of 3 pixels
13
14 // The components are added to the specified zone
15 btnNorth = new Button("NORTH");
16 add(btnNorth, BorderLayout.NORTH);
17 btnSouth = new Button("SOUTH");
18 add(btnSouth, BorderLayout.SOUTH);
19 btnCenter = new Button("CENTER");
20 add(btnCenter, BorderLayout.CENTER);
21 btnEast = new Button("EAST");
22 add(btnEast, BorderLayout.EAST);
23 btnWest = new Button("WEST");
24 add(btnWest, BorderLayout.WEST);
25
26 setTitle("BorderLayout Demo"); // "this" Frame sets title
27 setSize(280, 150); // "this" Frame sets initial size
28 setVisible(true); // "this" Frame shows
29 }
30
31/** The entry main() method */
32 public static void main(String[] args) {
33 new AWTBorderLayoutDemo(); // Let the constructor do the job
34 }
35}
8.4 Using Panels as Sub-Container to Organize Components
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, andpanelButtons is added to the CENTER.
1import java.awt.*;
2import java.awt.event.*;
3
4// An AWT GUI program inherits the top-level container java.awt.Frame
5public class AWTPanelDemo extends Frame {
6 private Button[] btnNumbers = new Button[10]; // Array of 10 numeric buttons
7 private Button btnHash, btnStar;
8 private TextField tfDisplay;
9
10/** Constructor to setup GUI components */
11 public AWTPanelDemo () {
12 // Set up display panel
13 Panel panelDisplay = new Panel(new FlowLayout());
14 tfDisplay = new TextField("0", 20);
15 panelDisplay.add(tfDisplay);
16
17 // Set up button panel
18 Panel panelButtons = new Panel(new GridLayout(4, 3));
19 btnNumbers[1] = new Button("1");
20 panelButtons.add(btnNumbers[1]);
21 btnNumbers[2] = new Button("2");
22 panelButtons.add(btnNumbers[2]);
23 btnNumbers[3] = new Button("3");
24 panelButtons.add(btnNumbers[3]);
25 btnNumbers[4] = new Button("4");
26 panelButtons.add(btnNumbers[4]);
27 btnNumbers[5] = new Button("5");
28 panelButtons.add(btnNumbers[5]);
29 btnNumbers[6] = new Button("6");
30 panelButtons.add(btnNumbers[6]);
31 btnNumbers[7] = new Button("7");
32 panelButtons.add(btnNumbers[7]);
33 btnNumbers[8] = new Button("8");
34 panelButtons.add(btnNumbers[8]);
35 btnNumbers[9] = new Button("9");
36 panelButtons.add(btnNumbers[9]);
37 // Can use a loop for the above statements!
38 btnStar = new Button("*");
39 panelButtons.add(btnStar);
40 btnNumbers[0] = new Button("0");
41 panelButtons.add(btnNumbers[0]);
42 btnHash = new Button("#");
43 panelButtons.add(btnHash);
44
45 setLayout(new BorderLayout()); // "this" Frame sets to BorderLayout
46 add(panelDisplay, BorderLayout.NORTH);
47 add(panelButtons, BorderLayout.CENTER);
48
49 setTitle("BorderLayout Demo"); // "this" Frame sets title
50 setSize(200, 200); // "this" Frame sets initial size
51 setVisible(true); // "this" Frame shows
52 }
53
54/** The entry main() method */
55 public static void main(String[] args) {
56 new AWTPanelDemo(); // Let the constructor do the job
57 }
58}
8.5 BoxLayout
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:
Swing API: for advanced graphical programming.
Accessibility API: provides assistive technology for the disabled.
Java 2D API: for high quality 2D graphics and images.
Pluggable look and feel supports.
Drag-and-drop support between Java and native applications.
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 look-
and-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
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 content-
pane).
3. JApplet: used for the applet's display-area (content-pane) inside a browser’s 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. TheJComponents 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
In summary, to write a Swing application, you have:
1. Use the Swing components with prefix "J" in package javax.swing,
e.g., JFrame, JButton, JTextField, JLabel, etc.
2. A top-level container (such as JFrame or JApplet) is needed. The JComponents should be
added directly onto the top-level container. They shall be added onto the content-pane of
the top-level container. You can retrieve a reference to the content-pane by invoking
method getContentPane() from the top-level container, or set the content-pane to the
main JPanel created in your program.
3. Swing applications uses AWT event-handling classes,
e.g., ActionEvent/ActionListener, MouseEvent/MouseListener, etc.
4. Run the constructor in the Event Dispatcher Thread (instead of Main thread) for thread
safety, as shown in the following program template.
10.4 Swing Program Template
1import java.awt.*;
2import java.awt.event.*;
3import javax.swing.*;
4
5// A Swing GUI application inherits from top-level container javax.swing.JFrame
6public class ...... extends JFrame {
7
8// private variables
9// ......
10
11/** Constructor to setup the GUI components */
12public ......() {
13Container cp = this.getContentPane();
14
15// Content-pane sets layout
16cp.setLayout(new ....Layout());
17
18// Allocate the GUI components
19// .....
20
21// Content-pane adds components
22cp.add(....);
23
24// Source object adds listener
25// .....
26
27setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
28// Exit the program when the close-window button clicked
29 setTitle("......"); // "this" JFrame sets title
30 setSize(300, 150); // "this" JFrame sets initial size (or pack())
31 setVisible(true); // show it
32 }
33
34/** The entry main() method */
35 public static void main(String[] args) {
36// Run GUI codes in Event-Dispatching thread for thread-safety
37SwingUtilities.invokeLater(new Runnable() {
38 @Override
39 public void run() {
40 new ......(); // Let the constructor do the job
41 }
42 });
43 }
44}
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.
1import java.awt.*; // Using AWT containers and components
2import java.awt.event.*; // Using AWT events and listener interfaces
3import javax.swing.*; // Using Swing components and containers
4
5// A Swing GUI application inherits from top-level container javax.swing.JFrame
6public class SwingCounter extends JFrame {
7 private JTextField tfCount; // Use Swing's JTextField instead of AWT's TextField
8 private int count = 0;
9
10/** Constructor to setup the GUI */
11 public SwingCounter () {
12// Retrieve the content-pane of the top-level container JFrame
13// All operations done on the content-pane
14 Container cp = getContentPane();
15 cp.setLayout(new FlowLayout());
16
17 cp.add(new JLabel("Counter"));
18 tfCount = new JTextField("0", 10);
19 tfCount.setEditable(false);
20 cp.add(tfCount);
21
22 JButton btnCount = new JButton("Count");
23 cp.add(btnCount);
24
25// Allocate an anonymous instance of an anonymous inner class that
26// implements ActionListener as ActionEvent listener
27 btnCount.addActionListener(new ActionListener() {
28 @Override
29 public void actionPerformed(ActionEvent e) {
30 ++count;
31 tfCount.setText(count + "");
32 }
33 });
34
35 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit program if close-window button clicked
36 setTitle("Swing Counter"); // "this" JFrame sets title
37 setSize(300, 100); // "this" JFrame sets initial size
38 setVisible(true); // "this" JFrame shows
39 }
40
41/** The entry main() method */
42 public static void main(String[] args) {
43// Run the GUI construction in the Event-Dispatching thread for thread-safety
44 SwingUtilities.invokeLater(new Runnable() {
45 @Override
46 public void run() {
47 new SwingCounter(); // Let the constructor do the job
48 }
49 });
50 }
51}
JFrame's Content-Pane
The JFrams's method getContentPane() returns the content-pane (which is a java.awt.Containter)
of the JFrame. You can then set its layout (the default layout is BorderLayout), and add
components into it. For example,
Container cp = getContentPane(); // Get the content-pane of this JFrame
cp.setLayout(new FlowLayout()); // content-pane sets to FlowLayout
cp.add(new JLabel("Counter")); // content-pane adds a JLabel component
......
cp.add(tfCount); // content-pane adds a JTextField component
......
cp.add(btnCount); // content-pane adds a JButton component
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 constructor will be executed in the so-called "Main-Program" thread. This may cause multi-
threading 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 event-dispatching 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
}
});
}
Note: javax.swing.SwingUtilities.invokeLater() is a cover
for java.awt.EventQueue.invokeLater() (which is used in the NetBeans' Visual GUI Builder).
At times, for example in game programming, the constructor or the main() may contains non-
GUI codes. Hence, it is a common practice to create a dedicated method
called initComponents() (used in NetBeans visual GUI builder) orcreateAndShowGUI() (used in
Swing tutorial) to handle all the GUI codes (and another method called initGame() to handle
initialization of the game's objects). This GUI init method shall be run in the event-dispatching
thread.
Warning Message "The serialization class does not declare a static final
serialVersionUID field of type long"
This warning message is triggered because java.awt.Frame (via its
superclass java.awt.Component) implements the java.io.Serializable interface. This interface
enables the object to be written out to an output stream serially (via method writeObject()); and
read back into the program (via method readObject()). The serialization runtime uses a number
(called serialVersionUID) to ensure that the object read into the program is compatible with the
class definition, and not belonging to another version.
You have these options:
1. Simply ignore this warning message. If a serializable class does not explicitly declare
a serialVersionUID, then the serialization runtime will calculate a
default serialVersionUID value for that class based on various aspects of the class.
2. Add a serialVersionUID (Recommended), e.g.
private static final long serialVersionUID = 1L; // version 1
3. Suppress this particular warning via annotation @SuppressWarmomgs (in
package java.lang) (JDK 1.5):
4. @SuppressWarnings("serial")