Java GUI Programming with AWT Guide
Java GUI Programming with AWT Guide
| HOME
So far, we have covered the basic programming constructs (such as variables, data types, 3.5 Example 5: MouseEvent and M
decision, loop, array and method) and introduced the important concept of Object-Oriented 3.6 Example 6: KeyEvent and Key
Programming (OOP). As discussed, OOP permits higher level of abstraction than traditional 4. Nested (Inner) Classes
Procedural-Oriented Languages (such as C). You can create high-level abstract data types called 4.1 Without Inner classes
classes to mimic real-life things. These classes are self-contained and are reusable. 4.2 What are Inner classes?
4.3 Example 7: A Named Inner Cla
In this article, I shall show you how you can reuse the graphics classes provided in JDK for
4.4 Example 8: An Anonymous Inn
constructing your own Graphical User Interface (GUI) applications. Writing your own graphics
4.5 Example 9: An Anonymous Inn
classes (and re-inventing the wheels) is mission impossible! These graphics classes, developed by
4.6 Example 10: Using the Same L
expert programmers, are highly complex and involve many advanced design patterns. However,
5. Event Listener's Adapter Classes
re-using them are not so difficult, if you follow the API documentation, samples and templates
5.1 Example 11: WindowAdapter f
provided.
5.2 Other Event-Listener Adapter
I shall assume that you have a good grasp of OOP, including composition, inheritance, 6. [SKIP] The Legacy "this" Listene
polymorphism, abstract class and interface; otherwise, read the earlier articles. I will describe
7. Layout Managers and Panel
another important OO concept called nested class (or inner class) in this article.
7.1 FlowLayout
There are current three sets of Java APIs for graphics programming: AWT (Abstract Windowing 7.2 GridLayout
Toolkit), Swing and JavaFX. 7.3 BorderLayout
1. AWT API was introduced in JDK 1.0. Most of the AWT UI components have become 7.4 Using Panels as Sub-Containe
obsolete and should be replaced by newer Swing UI components. 7.5 GridBagLayout
2. Swing API, a much more comprehensive set of graphics libraries that enhances the AWT, 7.6 Example 1
was introduced as part of Java Foundation Classes (JFC) after the release of JDK 1.1. JFC 7.7 BoxLayout
consists of Swing, Java2D, Accessibility, Internationalization, and Pluggable Look-and-Feel 8. Swing
Support APIs. JFC has been integrated into core Java since JDK 1.2. 8.1 Introduction
8.2 Swing's Features
3. The latest JavaFX, which was integrated into JDK 8, was meant to replace Swing. JavaFX
8.3 Using Swing API
was moved out from the JDK in JDK 11, but still available as a separate module.
8.4 Swing Program Template
Other than AWT/Swing/JavaFX graphics APIs provided in JDK, other organizations/vendors have 8.5 Swing Example 1: SwingCount
also provided graphics APIs that work with Java, such as Eclipse's Standard Widget Toolkit (SWT) 8.6 Swing Example 2: SwingAccum
(used in Eclipse), Google Web Toolkit (GWT) (used in Android), 3D Graphics API such as Java 9. Using Visual GUI Builder - NetBe
bindings for OpenGL (JOGL), Java3D, and etc. Furthermore, developers have moved to use 9.1 NetBeans
technologies such as HTML5 as the basis of webapps. 9.2 Eclipse
You need to refer to the "JDK API documentation" for the AWT/Swing APIs (under module
[Link]) while reading this chapter. The best online reference for Graphics programming
is the "Swing Tutorial" @ [Link] For advanced 2D
graphics programming, read "Java 2D Tutorial" @ [Link] For 3D graphics, read my 3D
articles.
AWT provides a platform-independent and device-independent interface to develop graphic programs that runs on all platforms, including
Windows, macOS, and Unixes.
In the above figure, there are three containers: a Frame and two Panels. A Frame is the top-level container of an AWT program. A Frame
has a title bar (containing an icon, a title, and the minimize/maximize/close buttons), an optional menu bar and the content display area. A
Panel is a rectangular area used to group related GUI components in a certain layout. In the above figure, the top-level Frame contains
two Panels. There are five components: a Label (providing description), a TextField (for users to enter text), and three Buttons (for user
to trigger certain programmed actions).
In a GUI program, a component must be kept (or added) in a container. You need to identify a container to hold the components. Every
container has a method called add(Component c). A container (say aContainer) can invoke [Link](aComponent) to add
aComponent into itself. For example,
GUI components are also called controls (e.g., Microsoft ActiveX Control), widgets (e.g., Eclipse's Standard Widget Toolkit, Google Web
Toolkit), which allow users to interact with (or control) the application.
A Frame provides the "main window" for your GUI application. It 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 [Link] to inherit the main window as follows:
import [Link]; // Using Frame class in package [Link]
// private variables
......
An AWT Dialog is a "pop-up window" used for interacting with the users. A Dialog
has a title-bar (containing an icon, a title and a close button) and a content display
area, as illustrated.
An AWT Applet (in package [Link]) is the top-level container for an applet,
which is a Java program running inside a browser. Applet is no longer supported in
most of the browsers.
As illustrated, a Container has a LayoutManager to layout the components in a certain pattern, e.g., flow, grid.
2.4 AWT Component Classes
AWT provides many ready-made and reusable GUI components in package [Link]. The frequently-used are: Button, TextField,
Label, Checkbox, CheckboxGroup (radio buttons), List, and Choice, as illustrated below.
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
These three constants are defined for specifying the alignment of the Label's text, as used in the above constructor.
Public Methods
// Examples
public String getText();
public void setText(String strLabel);
public int getAlignment();
public void setAlignment(int alignment); // [Link], [Link], [Link]
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.
Example
Label lblInput; // Declare an Label instance called lblInput
lblInput = new Label("Enter ID"); // Construct by invoking a constructor via the new operator
add(lblInput); // [Link](lblInput) - "this" is typically a subclass of Frame
[Link]("Enter password"); // Modify the Label's text string
[Link](); // Retrieve the Label's text string
Example
// Same as
Label xxx = new Label("Enter Name: ", [Link])); // xxx assigned by compiler
add(xxx);
Constructors
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"); // Declare and allocate a Button instance called btnColor
add(btnColor); // "this" Container adds the Button
...
[Link]("Green"); // Change the button's label
[Link](); // Read the button's label
Public Methods
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
[Link](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 = [Link]([Link]());
number *= number;
// setText() requires a String, need to convert the int number to String.
[Link](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 [Link]() or [Link](). To convert a primitive to a String, simply concatenate the primitive with an
empty String.
To exit this program, you have to close the CMD-shell (or press "control-c" on the CMD console); or push the "red" 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.
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 [Link] package; while AWT events and event-listener interfaces, such as ActionEvent and
ActionListener are kept in the [Link] 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 11 to 47 define a constructor, which is used to setup the GUI components and event handlers.
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-to-right 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 36-37, 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.
Line 27 (or Line 29-30) is used to setup the callback event-handler, which will be discussed in length later. In brief, whenever the button
is clicked, the actionPerformed() will be called. In the actionPerformed() (Lines 61-66), the counter value increases by 1 and
displayed on the TextField.
In the entry main() method (Lines 52-56), an instance of AWTCounter is constructed. The constructor is executed to initialize the GUI
components and setup the event-handlers. The GUI program then waits for the user action.
[Link](this);
//AWTCounter[frame0,93,0,300x100,invalid,hidden,layout=[Link],title=AWT Counter,resizable,normal]
// name (assigned by compiler) is "frame0"; top-left (x,y) at (93,0); width/height is 300x100 (via setSize());
[Link](lblCount);
//[Link][label0,0,0,0x0,invalid,align=left,text=Counter]
// name is "Label0"; align is "[Link]" (default); text is "Counter" (assigned in constructor)
[Link](tfCount);
//[Link][textfield0,0,0,0x0,invalid,text=0,selection=0-0]
// name is "Textfield0"; text is "0" (assigned in constructor)
[Link](btnCount);
//[Link][button0,0,0,0x0,invalid,label=Count]
// name is "button0"; label text is "Count" (assigned in constructor)
[Link](this);
//AWTCounter[frame0,93,0,300x100,invalid,layout=[Link],title=AWT Counter,resizable,normal]
[Link](lblCount);
//[Link][label0,31,35,57x23,align=left,text=Counter]
[Link](tfCount);
//[Link][textfield0,93,35,124x23,text=0,selection=0-0]
[Link](btnCount);
//[Link][button0,222,35,46x23,label=Count]
1. An AWT GUI program extends from [Link] (Line 6) - the top-level window container.
2. In the constructor (Line 14), we constructs 4 components - 2 anonymous [Link] and 2 [Link]. The Frame
adds the components, in GridLayout.
3. tfInput (TextField) is the source object, which fires an ActionEvent upon hitting the Enter key. tfInput adds an anonymous
instance of TFInputListener as an ActionEvent handler (Line 23). The listener class needs to implement ActionListener
interface and provides implementation to method actionPerformed(). Whenever an user hits Enter on the tfInput (TextField),
the actionPerformed() will be called back.
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 like Visual Basic.
In event-driven programming, a piece of event-handling codes is executed (or called back by the graphics subsystem) when an event was
fired in response to an user input (such as clicking a mouse button or hitting the ENTER key in a text field).
Callback Methods
In the above examples, the method actionPerformed() is known as a callback method. In other words, you never invoke
actionPerformed() in your codes explicitly. The actionPerformed() is called back by the graphics subsystem under certain
circumstances in response to certain user actions.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Counter</title></head>
<body>
<form>
Count: <input id="tfCount" type="text" value="0">
<input id="btnCount" type="button" value="Hello"
>
</form>
<script>
function myBtnOnClick() { [Link]++; }
function myBtnMouseOver() { [Link] = "Count Up"; }
function myBtnMouseOut() { [Link] = "Click Me"; }
[Link] = myBtnMouseOut; // assign a function to a variable
</script>
</body>
</html>
In Java, we CANNOT attach a method to a source object directly, as method is not a first-class object in Java. For example, a Java method
cannot accept methods as its arguments and it cannot return a method; you cannot assign a method to a variable, etc. (JavaScript and C
language CAN!).
Three kinds of 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, the source object creates an event object to
capture the action (e.g., mouse-click x and y, texts entered, etc). 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 event 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.
A source fires an event when triggered. For example, clicking a Button fires an ActionEvent, clicking a mouse button fires
MouseEvent, typing a key fires KeyEvent, and 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., ActionEvent). Firstly, we need to declare an interface called XxxListener (e.g.,
ActionListener) containing the names of the handler methods (recall that an interface contains only abstract methods without
implementation). For example, the ActionListener interface is declared as follows:
Secondly, all XxxEvent listeners 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
listener(s) can response to these events appropriately. For example,
Thirdly, in the source, we need to maintain a list of XxxEvent listener object(s), and define two methods: addXxxListener() and
removeXxxListener() to add and remove a XxxEvent listener from this list. For the ActionEvent, the signature of the methods
are:
Take note that the addXxxListener() takes a XxxListener object as its sole parameter. In other words, it can only add objects of
the type XxxListener, and its sub-type. Since XxxListener is an interface, you cannot create instance of XxxListener, but need
to create instance of a subclass implementing the XxxListener interface.
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(XxxListener lis) method.
2. The source is triggered by a user.
3. The source create a XxxEvent object, which encapsulates the necessary information about the activation. For example, the (x, y)
position of the mouse pointer, the text entered, etc.
4. Finally, for each of the XxxEvent listeners in the listener list, the source invokes the appropriate handler on the listener(s), which
provides the programmed response.
In summary, triggering a source fires an event to all its registered listeners, and invoke an appropriate handler of the listener.
The listener(s) is required to implement ActionListener interface, and override the actionPerformed() method to provide the
response. In Line 56-65, we write an inner class called BtnCountListener, which override the actionPerformed() to increment and
display the count. An inner class is a class defined inside an outer class, and it can access the private entities of the outer class. We will
elaborate on the inner class in the next section.
The source object registers listener via the addActionListener(). In this example, the source btnCount (Button) adds an instance of
BtnCountListener as a listener via:
Note that addActionListener() takes an argument of the type ActionListener. BtnCountListener, 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:
3. In Line 46-59, we define an inner class called TFInputListener as the ActionEvent [Link] ActionEvent listener is required to
implement the ActionListener interface, and override the actionPerformed() method to provide the programmed response
upon activation.
4. The source object tfInput (of TextField) registers an anonymous instance of TFInputListener as its ActionEvent listener via
the [Link](new TFInputListener()) (Line 23).
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.
The following program added support for "close-window button" to "Example 1: AWTCounter".
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 the super Frame as the source object.
2. The Frame fires the WindowEvent to all its registered WindowEvent listener(s).
3. In Line 53-69, we define an inner class called MyWindowListener as the WindowEvent listener. It is required to implement the
WindowListener interface, which declares 7 abstract methods: windowOpened(), windowClosed(), windowClosing(),
windowActivated(), windowDeactivated(), windowIconified() and windowDeiconified().
4. We register an anonymous instance of MyWindowListener as the WindowEvent listener to the source Frame via method
addWindowListener(new MyWindowListener()).
5. We override the windowClosing() handler to terminate the program using [Link](0). We ignore the other 6 handlers, but
required to provide an empty body for compilation.
A MouseEvent listener must implement the MouseListener interface, which declares the following five abstract methods:
1 import [Link].*;
2 import [Link].*;
3
4 public class MouseEventDemo extends Frame {
5 private TextField tfMouseX; // to display mouse-click-x
6 private TextField tfMouseY; // to display mouse-click-y
7
8 // Constructor - Setup the UI components and event handlers
9 public MouseEventDemo() {
10 setLayout(new FlowLayout()); // "super" frame sets its layout to FlowLayout
11
12 // Label (anonymous)
13 add(new Label("X-Click: ")); // "super" frame adds Label component
14
15 // TextField
16 tfMouseX = new TextField(10); // 10 columns
17 [Link](false); // read-only
18 add(tfMouseX); // "super" frame adds TextField component
19
20 // Label (anonymous)
21 add(new Label("Y-Click: ")); // "super" frame adds Label component
22
23 // TextField
24 tfMouseY = new TextField(10);
25 [Link](false); // read-only
26 add(tfMouseY); // "super" frame adds TextField component
27
28 addMouseListener(new MyMouseListener());
29 // "super" frame (source) fires the MouseEvent.
30 // "super" frame adds an anonymous instance of MyMouseListener
31 // as a MouseEvent listener.
32
33 setTitle("MouseEvent Demo"); // "super" Frame sets title
34 setSize(350, 100); // "super" Frame sets initial size
35 setVisible(true); // "super" Frame shows
36 }
37
38 public static void main(String[] args) {
39 new MouseEventDemo(); // Let the constructor do the job
40 }
41
42 // Define an inner class to handle MouseEvent
43 private class MyMouseListener implements MouseListener {
44 // Called back upon mouse clicked
45 @Override
46 public void mouseClicked(MouseEvent evt) {
47 [Link]([Link]() + "");
48 [Link]([Link]() + "");
49 }
50
51 // Not used - need to provide an empty body to compile.
52 @Override public void mousePressed(MouseEvent evt) { }
53 @Override public void mouseReleased(MouseEvent evt) { }
54 @Override public void mouseEntered(MouseEvent evt) { }
55 @Override public void mouseExited(MouseEvent evt) { }
56 }
57 }
In this example, we setup a GUI with 4 components (two anonymous Labels and two non-editable TextFields) inside a top-level
container Frame, arranged in FlowLayout.
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits from the top-level container [Link]
5 public class MouseMotionDemo extends Frame {
6
7 // To display the (x, y) of the mouse-clicked
8 private TextField tfMouseClickX;
9 private TextField tfMouseClickY;
10 // To display the (x, y) of the current mouse-pointer position
11 private TextField tfMousePositionX;
12 private TextField tfMousePositionY;
13
14 // Constructor to setup the GUI components and event handlers
15 public MouseMotionDemo() {
16 setLayout(new FlowLayout()); // "super" frame sets to FlowLayout
17
18 add(new Label("X-Click: "));
19 tfMouseClickX = new TextField(10);
20 [Link](false);
21 add(tfMouseClickX);
22 add(new Label("Y-Click: "));
23 tfMouseClickY = new TextField(10);
24 [Link](false);
25 add(tfMouseClickY);
26
27 add(new Label("X-Position: "));
28 tfMousePositionX = new TextField(10);
29 [Link](false);
30 add(tfMousePositionX);
31 add(new Label("Y-Position: "));
32 tfMousePositionY = new TextField(10);
33 [Link](false);
34 add(tfMousePositionY);
35
36 MyMouseListener listener = new MyMouseListener();
37 addMouseListener(listener);
38 addMouseMotionListener(listener);
39 // "super" frame (source) fires MouseEvent.
40 // "super" frame adds an instance of MyMouseListener
41 // as MouseListener and MouseMotionListener.
42
43 setTitle("MouseMotion Demo"); // "super" Frame sets title
44 setSize(400, 120); // "super" Frame sets initial size
45 setVisible(true); // "super" Frame shows
46 }
47
48 // The entry main() method
49 public static void main(String[] args) {
50 new MouseMotionDemo(); // Let the constructor do the job
51 }
52
53 // Define an inner class as both the MouseListener and MouseMotionListener
54 // A Java class can extend one superclass but implement many interfaces
55 private class MyMouseListener implements MouseListener, MouseMotionListener {
56 /* MouseListener handlers */
57 // Called back when a mouse-button has been clicked
58 @Override
59 public void mouseClicked(MouseEvent evt) {
60 [Link]([Link]() + "");
61 [Link]([Link]() + "");
62 }
63
64 // Not Used, but need to provide an empty body for compilation
65 @Override public void mousePressed(MouseEvent evt) { }
66 @Override public void mouseReleased(MouseEvent evt) { }
67 @Override public void mouseEntered(MouseEvent evt) { }
68 @Override public void mouseExited(MouseEvent evt) { }
69
70 /* MouseMotionEvent handlers */
71 // Called back when the mouse-pointer has been moved
72 @Override
73 public void mouseMoved(MouseEvent evt) {
74 [Link]([Link]() + "");
75 [Link]([Link]() + "");
76 }
77
78 // Not Used, but need to provide an empty body for compilation
79 @Override public void mouseDragged(MouseEvent evt) { }
80 }
81 }
1. We identify the super Frame as the source, which fires the MouseEvent to its registered MouseListener and
MouseMotionListener.
2. In Line 53-80, we define an inner class called MyMouseListener as both the MouseListener and MouseMotionListener.
3. We register an instance of MyMouseListener as the listener to super Frame via method addMouseListener() and
addMouseMotionListener().
4. The MouseMotionListener 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.
In this example:
1. We identify the tfInput (of 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. In Line 35-46, we define an inner class called MyKeyListener as the KeyEvent listener.
4. We register an anonymous instance of MyKeyListener as the KeyEvent listener to the source TextField via method
[Link]().
5. The KeyEvent listener 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().
tfCount = new TextField(count + "", 10); // construct the TextField component with initial text
[Link](false); // set to read-only
add(tfCount); // "super" Frame container adds TextField component
Can you see the problem? This external class cannot access the variables such as count and tfCount in the AWTCounterExternal class.
We can fix this problem, but the solution is messy. An easy solution is to use an inner class instead of an ordinary external class (to be
explained in the following sections).
4.2 What are Inner classes?
A nested class (or commonly called inner class) is a class defined inside another class - introduced in JDK 1.1. As an illustration, two nested
classes MyNestedClass1 and MyNestedClass2 are defined inside the definition of an outer class called MyOuterClass.
4.3 Example 7: A Named Inner Class as Event Listener (Revisit Example 1 AWTCounter)
A nested class is useful if you need a small class which relies on the enclosing outer class for its private variables and methods. It is ideal in
an event-driven environment for implementing event 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 (revisit Example 1 AWTCounter), we define an inner 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. BtnCountListener needs to be defined as an inner class, as it needs to access
private variables (count and tfCount) of the outer class.
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits from the top-level container [Link]
5 public class AWTCounter extends Frame {
6
7 // The event-handler actionPerformed() needs to access these "private" variables
8 private TextField tfCount;
9 private Button btnCount;
10 private int count = 0;
11
12 // Constructor to setup the GUI components and event handlers
13 public AWTCounter() {
14 setLayout(new FlowLayout()); // "super" Frame sets to FlowLayout
15 add(new Label("Counter")); // An anonymous instance of Label
16 tfCount = new TextField("0", 10);
17 [Link](false); // read-only
18 add(tfCount); // "super" Frame adds tfCount
19
20 btnCount = new Button("Count");
21 add(btnCount); // "super" Frame adds btnCount
22
23 // Construct an anonymous instance of BtnCountListener (a named inner class).
24 // btnCount adds this instance as a ActionListener.
25 [Link](new BtnCountListener());
26
27 setTitle("AWT Counter");
28 setSize(250, 100);
29 setVisible(true);
30 }
31
32 // The entry main method
33 public static void main(String[] args) {
34 new AWTCounter(); // Let the constructor do the job
35 }
36
37 /**
38 * BtnCountListener is a "named inner class" used as ActionListener.
39 * This inner class can access private variables of the outer class.
40 */
41 private class BtnCountListener implements ActionListener {
42 @Override
43 public void actionPerformed(ActionEvent evt) {
44 ++count;
45 [Link](count + "");
46 }
47 }
48 }
[Link](new BtnCountListener());
The inner class can access the private variable tfCount and count of the outer class.
The inner class is compiled into AWTCount$[Link], in the format of OuterClassName$[Link].
// The event handler can access the private variables thru "frame"
@Override
public void actionPerformed(ActionEvent evt) {
[Link]++;
[Link]([Link] + "");
}
}
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits from the top-level container [Link]
5 public class AWTCounterAnonymousInnerClass extends Frame {
6
7 // The event-handler actionPerformed() needs to access these private variables
8 private TextField tfCount;
9 private Button btnCount;
10 private int count = 0;
11
12 // Constructor to setup the GUI components and event handlers
13 public AWTCounterAnonymousInnerClass () {
14 setLayout(new FlowLayout()); // "super" Frame sets to FlowLayout
15 add(new Label("Counter")); // An anonymous instance of Label
16 tfCount = new TextField("0", 10);
17 [Link](false); // read-only
18 add(tfCount); // "super" Frame adds tfCount
19
20 btnCount = new Button("Count");
21 add(btnCount); // "super" Frame adds btnCount
22
23 // Construct an anonymous instance of an anonymous class.
24 // btnCount adds this instance as a ActionListener.
25 [Link](new ActionListener() {
26 @Override
27 public void actionPerformed(ActionEvent evt) {
28 ++count;
29 [Link](count + "");
30 }
31 });
32
33 setTitle("AWT Counter");
34 setSize(250, 100);
35 setVisible(true);
36 }
37
38 // The entry main method
39 public static void main(String[] args) {
40 new AWTCounterAnonymousInnerClass(); // Let the constructor do the job
41 }
42 }
[Link](new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
++count;
[Link](count + "");
}
});
private class N implements ActionListener { // N is a running number of the inner classes created
@Override
public void actionPerformed(ActionEvent evt) {
++count;
[Link](count + "");
}
}
[Link](new N());
// Or
N n = new N()
[Link](n);
From JDK 8, you can write the event handler using "Lambda Expression" in a one-liner as follows:
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class AWTCounter3ButtonsAnonymousIC extends Frame {
6 private TextField tfCount;
7 private Button btnCountUp, btnCountDown, btnReset;
8 private int count = 0;
9
10 // Constructor to setup the GUI components and event handlers
11 public AWTCounter3ButtonsAnonymousIC() {
12 setLayout(new FlowLayout());
13 add(new Label("Counter")); // an anonymous instance of Label
14 tfCount = new TextField("0", 10);
15 [Link](false); // read-only
16 add(tfCount); // "super" Frame adds tfCount
17
18 btnCountUp = new Button("Count Up");
19 add(btnCountUp);
20 // Construct an anonymous instance of an anonymous inner class.
21 // The source Button adds the anonymous instance as ActionEvent listener
22 [Link](new ActionListener() {
23 @Override
24 public void actionPerformed(ActionEvent evt) {
25 ++count;
26 [Link](count + "");
27 }
28 });
29
30 btnCountDown = new Button("Count Down");
31 add(btnCountDown);
32 [Link](new ActionListener() {
33 @Override
34 public void actionPerformed(ActionEvent evt) {
35 count--;
36 [Link](count + "");
37 }
38 });
39
40 btnReset = new Button("Reset");
41 add(btnReset);
42 [Link](new ActionListener() {
43 @Override
44 public void actionPerformed(ActionEvent evt) {
45 count = 0;
46 [Link]("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 AWTCounter3ButtonsAnonymousIC(); // Let the constructor do the job
58 }
59 }
4.6 Example 10: Using the Same Listener Instance for All the Buttons
If you use the same instance as the listener for all 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.
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class AWTCounter3Buttons1Listener extends Frame {
6 private TextField tfCount;
7 private Button btnCountUp, btnCountDown, btnReset;
8 private int count = 0;
9
10 // Constructor to setup the GUI components and event handlers
11 public AWTCounter3Buttons1Listener () {
12 setLayout(new FlowLayout());
13 add(new Label("Counter"));
14 tfCount = new TextField("0", 10);
15 [Link](false);
16 add(tfCount);
17
18 // Construct Buttons
19 btnCountUp = new Button("Count Up");
20 add(btnCountUp);
21 btnCountDown = new Button("Count Down");
22 add(btnCountDown);
23 btnReset = new Button("Reset");
24 add(btnReset);
25
26 // Allocate an instance of the "named" inner class BtnListener.
27 AllButtonsListener listener = new AllButtonsListener();
28 // Use the same listener instance for all the 3 Buttons.
29 [Link](listener);
30 [Link](listener);
31 [Link](listener);
32
33 setTitle("AWT Counter");
34 setSize(400, 100);
35 setVisible(true);
36 }
37
38 // The entry main method
39 public static void main(String[] args) {
40 new AWTCounter3Buttons1Listener(); // Let the constructor do the job
41 }
42
43 /**
44 * AllButtonsListener is an named inner class used as ActionEvent listener for all the Buttons.
45 */
46 private class AllButtonsListener implements ActionListener {
47 @Override
48 public void actionPerformed(ActionEvent evt) {
49 // Need to determine which button fired the event.
50 // the getActionCommand() returns the Button's label
51 String btnLabel = [Link]();
52 if ([Link]("Count Up")) {
53 ++count;
54 } else if ([Link]("Count Down")) {
55 --count;
56 } else {
57 count = 0;
58 }
59 [Link](count + "");
60 }
61 }
62 }
1 import [Link].*;
2 import [Link].*;
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 [Link](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 AllButtonsListener listener = new AllButtonsListener();
27 // Use the same listener instance to all the 3 Buttons.
28 [Link](listener);
29 [Link](listener);
30 [Link](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 * AllButtonsListener is a named inner class used as ActionEvent listener for all the Buttons.
44 */
45 private class AllButtonsListener implements ActionListener {
46 @Override
47 public void actionPerformed(ActionEvent evt) {
48 // Need to determine which button has fired the event.
49 Button source = (Button)[Link]();
50 // Get a reference of the source that has fired the event.
51 // getSource() returns a [Link]. Downcast back to Button.
52 if (source == btnCountUp) {
53 ++count;
54 } else if (source == btnCountDown) {
55 --count;
56 } else {
57 count = 0;
58 }
59 [Link](count + "");
60 }
61 }
62 }
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class WindowEventDemoWithInnerClass extends Frame {
6 private TextField tfCount;
7 private Button btnCount;
8 private int count = 0;
9
10 // Constructor to setup the GUI components and event handlers
11 public WindowEventDemoWithInnerClass () {
12 setLayout(new FlowLayout());
13 add(new Label("Counter"));
14 tfCount = new TextField("0", 10);
15 [Link](false);
16 add(tfCount);
17
18 btnCount = new Button("Count");
19 add(btnCount);
20 [Link](new ActionListener() {
21 @Override
22 public void actionPerformed(ActionEvent evt) {
23 ++count;
24 [Link](count + "");
25 }
26 });
27
28 // Allocate an anonymous instance of an anonymous inner class
29 // that implements WindowListener.
30 // "super" Frame adds this instance as WindowEvent listener.
31 addWindowListener(new WindowListener() {
32 @Override
33 public void windowClosing(WindowEvent evt) {
34 [Link](0); // terminate the program
35 }
36 // Need to provide an empty body for compilation
37 @Override public void windowOpened(WindowEvent evt) { }
38 @Override public void windowClosed(WindowEvent evt) { }
39 @Override public void windowIconified(WindowEvent evt) { }
40 @Override public void windowDeiconified(WindowEvent evt) { }
41 @Override public void windowActivated(WindowEvent evt) { }
42 @Override public void windowDeactivated(WindowEvent evt) { }
43 });
44
45 setTitle("WindowEvent Demo");
46 setSize(250, 100);
47 setVisible(true);
48 }
49
50 // The entry main method
51 public static void main(String[] args) {
52 new WindowEventDemoWithInnerClass(); // Let the constructor do the job
53 }
54 }
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class WindowEventDemoAdapter extends Frame {
6 private TextField tfCount;
7 private Button btnCount;
8 private int count = 0;
9
10 // Constructor to setup the GUI components and event handlers
11 public WindowEventDemoAdapter () {
12 setLayout(new FlowLayout());
13 add(new Label("Counter"));
14 tfCount = new TextField("0", 10);
15 [Link](false);
16 add(tfCount);
17
18 btnCount = new Button("Count");
19 add(btnCount);
20 [Link](new ActionListener() {
21 @Override
22 public void actionPerformed(ActionEvent evt) {
23 ++count;
24 [Link](count + "");
25 }
26 });
27
28 // Allocate an anonymous instance of an anonymous inner class
29 // that extends WindowAdapter.
30 // "super" Frame adds the instance as WindowEvent listener.
31 addWindowListener(new WindowAdapter() {
32 @Override
33 public void windowClosing(WindowEvent evt) {
34 [Link](0); // Terminate the program
35 }
36 });
37
38 setTitle("WindowEvent Demo");
39 setSize(250, 100);
40 setVisible(true);
41 }
42
43 /** The entry main method */
44 public static void main(String[] args) {
45 new WindowEventDemoAdapter(); // Let the constructor do the job
46 }
47 }
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.
For example,
There is only ONE class in this code. But this code is much harder to understand and seldom used nowadays. Using inner class is a better
solution.
AWT provides the following layout managers (in package [Link]): FlowLayout, GridLayout, BorderLayout, GridBagLayout,
BoxLayout, CardLayout, and others. Swing added more layout manager in package [Link], to be described later.
// [Link]
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,
7.1 FlowLayout
In the [Link], components are arranged from left-to-right inside the
container in the order that they are added (via method [Link](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 alignment);
public FlowLayout(int alignment, int hgap, int vgap);
// alignment: [Link] (or LEADING), [Link] (or TRAILING), or [Link]
// hgap, vgap: horizontal/vertical gap between the components
// By default: hgap = 5, vgap = 5, alignment = [Link]
Example
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class AWTFlowLayoutDemo extends Frame {
6 private Button btn1, btn2, btn3, btn4, btn5, btn6;
7
8 // Constructor to setup GUI components and event handlers
9 public AWTFlowLayoutDemo () {
10 setLayout(new FlowLayout());
11 // "super" 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"); // "super" Frame sets title
28 setSize(280, 150); // "super" Frame sets initial size
29 setVisible(true); // "super" 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 }
7.2 GridLayout
In [Link], 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 [Link](aComponent)).
Constructors
Example
1 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class AWTGridLayoutDemo extends Frame {
6 private Button btn1, btn2, btn3, btn4, btn5, btn6;
7
8 // Constructor to setup GUI components and event handlers
9 public AWTGridLayoutDemo () {
10 setLayout(new GridLayout(3, 2, 3, 3));
11 // "super" Frame sets layout to 3x2 GridLayout, horizontal and vertical 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"); // "super" Frame sets title
28 setSize(280, 150); // "super" Frame sets initial size
29 setVisible(true); // "super" 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 }
7.3 BorderLayout
In [Link], the container is divided into 5 zones: EAST, WEST, SOUTH, NORTH,
and CENTER. Components are added using method [Link](aComponent, zone),
where zone is either [Link] (or PAGE_START), [Link] (or
PAGE_END), [Link] (or LINE_START), [Link] (or LINE_END), or
[Link].
You need not place 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 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class AWTBorderLayoutDemo extends Frame {
6 private Button btnNorth, btnSouth, btnCenter, btnEast, btnWest;
7
8 // Constructor to setup GUI components and event handlers
9 public AWTBorderLayoutDemo () {
10 setLayout(new BorderLayout(3, 3));
11 // "super" 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, [Link]);
17 btnSouth = new Button("SOUTH");
18 add(btnSouth, [Link]);
19 btnCenter = new Button("CENTER");
20 add(btnCenter, [Link]);
21 btnEast = new Button("EAST");
22 add(btnEast, [Link]);
23 btnWest = new Button("WEST");
24 add(btnWest, [Link]);
25
26 setTitle("BorderLayout Demo"); // "super" Frame sets title
27 setSize(280, 150); // "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 AWTBorderLayoutDemo(); // Let the constructor do the job
34 }
35 }
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 import [Link].*;
2 import [Link].*;
3
4 // An AWT GUI program inherits the top-level container [Link]
5 public class AWTPanelDemo extends Frame {
6 private Button[] btnNumbers; // Array of 10 numeric Buttons
7 private Button btnHash, btnStar;
8 private TextField tfDisplay;
9
10 // Constructor to setup GUI components and event handlers
11 public AWTPanelDemo () {
12 // Set up display panel
13 Panel panelDisplay = new Panel(new FlowLayout());
14 tfDisplay = new TextField("0", 20);
15 [Link](tfDisplay);
16
17 // Set up button panel
18 Panel panelButtons = new Panel(new GridLayout(4, 3));
19 btnNumbers = new Button[10]; // Construct an array of 10 numeric Buttons
20 btnNumbers[1] = new Button("1"); // Construct Button "1"
21 [Link](btnNumbers[1]); // The Panel adds this Button
22 btnNumbers[2] = new Button("2");
23 [Link](btnNumbers[2]);
24 btnNumbers[3] = new Button("3");
25 [Link](btnNumbers[3]);
26 btnNumbers[4] = new Button("4");
27 [Link](btnNumbers[4]);
28 btnNumbers[5] = new Button("5");
29 [Link](btnNumbers[5]);
30 btnNumbers[6] = new Button("6");
31 [Link](btnNumbers[6]);
32 btnNumbers[7] = new Button("7");
33 [Link](btnNumbers[7]);
34 btnNumbers[8] = new Button("8");
35 [Link](btnNumbers[8]);
36 btnNumbers[9] = new Button("9");
37 [Link](btnNumbers[9]);
38 // You should use a loop for the above statements!!!
39 btnStar = new Button("*");
40 [Link](btnStar);
41 btnNumbers[0] = new Button("0");
42 [Link](btnNumbers[0]);
43 btnHash = new Button("#");
44 [Link](btnHash);
45
46 setLayout(new BorderLayout()); // "super" Frame sets to BorderLayout
47 add(panelDisplay, [Link]);
48 add(panelButtons, [Link]);
49
50 setTitle("BorderLayout Demo"); // "super" Frame sets title
51 setSize(200, 200); // "super" Frame sets initial size
52 setVisible(true); // "super" Frame shows
53 }
54
55 // The entry main() method
56 public static void main(String[] args) {
57 new AWTPanelDemo(); // Let the constructor do the job
58 }
59 }
7.5 GridBagLayout
Reference: Read "How to Use GridBagLayout" @ [Link]
7.6 Example 1
1 import [Link].*;
2 import [Link].*;
3
4 @SuppressWarnings("serial")
5 public class GridBagLayoutExample extends JPanel {
6 JButton button1, button2, button3, button4, button5;
7
8 // Constructor
9 public GridBagLayoutExample() {
10 [Link](new GridBagLayout()); // super JPanel
11 GridBagConstraints gbc = new GridBagConstraints();
12
13 // The following constraints are applicable to all components
14 // [Link] = [Link];
15
16 // Define constraints for individual components
17 button1 = new JButton("B1");
18 [Link] = 0; // column 0 (first column)
19 [Link] = 0; // row 0 (first row)
20 // [Link] = 0.5; // weight for distributing extra x-spaces
21 [Link](button1, gbc); // add and apply constraints
22
23 button2 = new JButton("B2");
24 [Link] = 1; // column 1
25 [Link] = 0; // row 0
26 // [Link] = 1.0;
27 [Link](button2, gbc);
28
29 button3 = new JButton("B3");
30 [Link] = 2; // column 2
31 [Link] = 0; // row 0
32 // [Link] = 0.5;
33 [Link](button3, gbc);
34
35 button4 = new JButton("Long-Named Button 4");
36 [Link] = 0; // column 0
37 [Link] = 1; // row 1 (second row)
38 [Link] = 3; // span 3 columns
39 [Link] = 40; // internal padding-y
40 [Link](button4, gbc);
41
42 button5 = new JButton("5");
43 [Link] = 1; // second column
44 [Link] = 2; // third row
45 [Link] = 2; // span 2 columns
46 [Link] = 0; // reset to default
47 [Link] = GridBagConstraints.PAGE_END; // align at bottom of space
48 [Link] = new Insets(10, 0, 0, 0); // top margin
49 // [Link] = 1.0; // request any extra vertical space
50 [Link](button5, gbc);
51 }
52
53 public static void main(String[] args) {
54 [Link](new Runnable() {
55 public void run() {
56 JFrame frame = new JFrame("GridBagLayoutDemo");
57 [Link](new GridBagLayoutExample());
58 [Link](JFrame.EXIT_ON_CLOSE);
59 [Link](300,300);
60 [Link](true);
61 }
62 });
63 }
64 }
Run the program, the output is in (a), where all components have their natural width and height, placed in center (anchor=CENTER) with
extra x and y spaces at the 4 margins. The ipady increases the internal y-padding for button-4 (spans 3 columns). Button 5 (spans second
and third columns) has top margin specified by insets, and anchor at the bottom-right corner (anchor=PAGE_END).
Uncomment the [Link] = [Link] (line 14). The output is in (b), where ALL components fill horizontally
with equal column width.
Uncomment all weightx (lines 20, 26, 32). The output is in (c). The extra x spaces are distributed according to the weightx of (0.5, 1.0, 0.5).
Uncomment the weighty (line 49). The output is in (d). The extra y spaces are given to row 3, as row 1 and 2 have weighty of 0 and non-
participating in the distribution of extra y-spaces.
7.7 BoxLayout
BoxLayout arrange components in a single row or column. It respects components' requests on the minimum sizes.
8. Swing
8.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:
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 [Link]()). 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).
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.
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 macOS). 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 [Link]). Swing added some new classes in package
[Link], but they are not frequently used.
8. Swing application uses AWT's layout manager (such as FlowLayout and BorderLayout in package [Link]). It added new layout
managers, such as Springs, Struts, and BoxLayout (in package [Link]).
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".
Swing's Components
Compared with the AWT component classes (in package [Link]), Swing component classes (in package [Link]) 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.
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.
2. set the content-pane to a JPanel (the main panel created in your application which holds all your GUI components) via JFrame's
setContentPane().
Notes: If a component is added directly into a JFrame, it is added into the content-pane of JFrame instead, i.e.,
Event-Handling in Swing
Swing uses the AWT event-handling classes (in package [Link]). Swing introduces a few new event-handling classes (in package
[Link]) but they are not frequently used.
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.
JFrame's Content-Pane
The JFrame's method getContentPane() returns the content-pane (which is a [Link]) of the JFrame. You can then set its
layout (the default layout is BorderLayout), and add components into it. For example,
You can also use the JFrame's setContentPane() method to directly set the content-pane to a JPanel (or a JComponent). For example,
JFrame's setDefaultCloseOperation()
Instead of writing a WindowEvent listener with a windowClosing() handler to process the "close-window" button, JFrame provides a
method called setDefaultCloseOperation() to sets the default operation when the user initiates a "close" on this frame. Typically, we
choose the option JFrame.EXIT_ON_CLOSE, which terminates the application via a [Link]().
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
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 [Link]() to asynchronously
queue the constructor on the event-dispatching thread. The codes will be run after all pending events have been processed. For example,
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) or createAndShowGUI() (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 [Link] (via its superclass [Link]) implements the
[Link] 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.
3. Suppress this particular warning via annotation @SuppressWarnings (in package [Link]) (JDK 1.5):
@SuppressWarnings("serial")
public class MyFrame extends JFrame { ...... }
9.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".
9.2 Eclipse
For using Eclipse GUI Builder, read "Writing Swing Applications using Eclipse GUI Builder".