Lab 09
Lab 09
There are current three sets of Java APIs for graphics programming:
1. AWT (Abstract Windowing Toolkit) API was introduced in JDK 1.0. Most of the AWT components have
become obsolete and should be replaced by newer Swing components.
2. Swing API, a much more comprehensive set of graphics libraries that enhances the AWT, was
introduced as part of Java Foundation Classes (JFC) after the release of JDK 1.1. JFC consists of Swing,
Java2D, Accessibility, Internationalization, and Pluggable Look-and-Feel Support APIs. JFC has been
integrated into core Java since JDK 1.2.
3. The latest JavaFX, which was integrated into JDK 8, is meant to replace Swing. However, Oracle
removed JavaFX from JDK 11 onwards but continued commercial support for JavaFX in the Oracle
JDK 8 through at least 2022.
Other than AWT/Swing/JavaFX graphics APIs provided in JDK, other organizations/vendors have also provided
graphics APIs that work with Java, such as Eclipse's Standard Widget Toolkit (SWT) (used in Eclipse), Google
Web Toolkit (GWT) (used in Android), 3D Graphics API such as Java bindings for OpenGL (JOGL) and Java3D.
You need to check the JDK API the AWT/Swing APIs (under module java.desktop) and JavaFX (under
modules javafx.*) while reading this chapter. The best online reference for Graphics programming is the
"Swing Tutorial" @ http://docs.oracle.com/javase/tutorial/uiswing/. For advanced 2D graphics programming,
read "Java 2D Tutorial" @ http://docs.oracle.com/javase/tutorial/2d/index.html.
The below guideline helps you to practice with all three Java APIs for comparison. You may copy and
paste the suggestion source code but you should analyse to understand and explain the source code.
0. Create GUIProject
- Create a Java Project named GUIProject
- Create the following packages in the GUIProject for this lab:
For Global ICT:
+ hust.soict.globalict.gui.awt
+ hust.soict.globalict.gui.swing
+ hust.soict.globalict.gui.javafx
For HEDSPI:
+ hust.soict.hedspi.gui.awt
+ hust.soict.hedspi.gui.swing
+ hust.soict.hedspi.gui.javafx
Page 1 of 17
Note: All codes in this section should be put into the AWT package.
1.1. AWT Packages
AWT consists of 12 packages of 370 classes (Swing is even bigger, with 18 packages of 737 classes as of JDK 8,
and then we have JavaFX with more than 30 packages). Fortunately, only 2 packages
- java.awt and java.awt.event - are commonly-used.
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.
Page 2 of 17
- Create a class AWTCounter in soict.hust.globalict.gui.awt package or
soict.hust.hedspi.gui.awt. 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 shall display count of 0 initially. Each time you click the button, the
counter's value increases by 1.
- The sample code is presented below:
1import java.awt.*; // Using AWT container and component classes
2import java.awt.event.*; // Using AWT event classes and listener interfaces
3
4// An AWT program inherits from the top-level container java.awt.Frame
5public class AWTCounter extends Frame implements ActionListener {
6 private Label lblCount; // Declare a Label component
7 private TextField tfCount; // Declare a TextField component
8 private Button btnCount; // Declare a Button component
9 private int count = 0; // Counter's value
10
11 // Constructor to setup GUI components and event handlers
12 public AWTCounter () {
13 setLayout(new FlowLayout());
14 // "super" Frame, which is a Container, sets its layout to FlowLayout to arrange
15 // the components from left-to-right, and flow to next row from top-to-bottom.
16
17 lblCount = new Label("Counter"); // construct the Label component
18 add(lblCount); // "super" Frame container adds Label component
19
20 tfCount = new TextField(count + "", 10); // construct the TextField component with initial text
21 tfCount.setEditable(false); // set to read-only
22 add(tfCount); // "super" Frame container adds TextField component
23
24 btnCount = new Button("Count"); // construct the Button component
25 add(btnCount); // "super" Frame container adds Button component
26
27 btnCount.addActionListener(this);
28 // "btnCount" is the source object that fires an ActionEvent when clicked.
29 // The source add "this" instance as an ActionEvent listener, which provides
30 // an ActionEvent handler called actionPerformed().
31 // Clicking "btnCount" invokes actionPerformed().
32
33 setTitle("AWT Counter"); // "super" Frame sets its title
34 setSize(250, 100); // "super" Frame sets its initial window size
35
36 // For inspecting the Container/Components objects
37 // System.out.println(this);
38 // System.out.println(lblCount);
39 // System.out.println(tfCount);
40 // System.out.println(btnCount);
41
42 setVisible(true); // "super" Frame shows
43
Page 3 of 17
44 // System.out.println(this);
45 // System.out.println(lblCount);
46 // System.out.println(tfCount);
47 // System.out.println(btnCount);
48 }
49
50 // The entry main() method
51 public static void main(String[] args) {
52 // Invoke the constructor to setup the GUI, by allocating an instance
53 AWTCounter app = new AWTCounter();
54 // or simply "new AWTCounter();" for an anonymous instance
55 }
56
57 // ActionEvent handler - Called back upon button-click.
58 @Override
59 public void actionPerformed(ActionEvent evt) {
60 ++count; // Increase the counter value
61 // Display the counter value on the TextField tfCount
62 tfCount.setText(count + ""); // Convert int to String
63 }
64}
Swing is part of the so-called "Java Foundation Classes (JFC)", 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
Page 4 of 17
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.
2.1. Swing's Features
Swing is huge (consists of 18 packages of 737 classes as in JDK 1.8) 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.
Page 5 of 17
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.
If you understood the AWT programming (in particular, container/component and event-handling), switching
over to Swing (or any other Graphics packages) is straight-forward.
Page 6 of 17
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.
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.
You could:
get the content-pane via getContentPane() from a top-level container, and add components onto it.
For example,
1. public class SwingDemo extends JFrame {
2. // Constructor
3. public SwingDemo() {
4. // Get the content-pane of this JFrame, which is a java.awt.Container
5. // All operations, such as setLayout() and add() operate on the content-pane
6. Container cp = getContentPane();
7. cp.setLayout(new FlowLayout());
8. cp.add(new JLabel("Hello, world!"));
9. cp.add(new JButton("Button"));
10. ......
11. }
12. .......
13. }
set the content-pane to a JPanel (the main panel created in your application which holds all your GUI
components) via JFrame's setContentPane().
14. public class SwingDemo extends JFrame {
Page 7 of 17
15. // Constructor
16. public SwingDemo() {
17. // The "main" JPanel holds all the GUI components
18. JPanel mainPanel = new JPanel(new FlowLayout());
19. mainPanel.add(new JLabel("Hello, world!"));
20. mainPanel.add(new JButton("Button"));
21.
22. // Set the content-pane of this JFrame to the main JPanel
23. setContentPane(mainPanel);
24. ......
25. }
26. .......
27. }
Notes: If a component is added directly into a JFrame, it is added into the content-pane of JFrame instead.
// Suppose that "this" is a JFrame
add(new JLabel("add to JFrame directly"));
// is executed as
getContentPane().add(new JLabel("add to JFrame directly"));
Page 8 of 17
13 // Retrieve the top-level content-pane from JFrame
14 Container cp = getContentPane();
15
16 // Content-pane sets layout
17 cp.setLayout(new ....Layout());
18
19 // Allocate the GUI components
20 // .....
21
22 // Content-pane adds components
23 cp.add(....);
24
25 // Source object adds listener
26 // .....
27
28 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
29 // Exit the program when the close-window button clicked
30 setTitle("......"); // "super" JFrame sets title
31 setSize(300, 150); // "super" JFrame sets initial size
32 setVisible(true); // "super" JFrame shows
33 }
34
35 // The entry main() method
36 public static void main(String[] args) {
37 // Run GUI codes in Event-Dispatching thread for thread-safety
38 SwingUtilities.invokeLater(new Runnable() {
39 @Override
40 public void run() {
41 new ......(); // Let the constructor do the job
42 }
43 });
44 }
45}
Page 9 of 17
8 private JButton btnCount; // Using Swing's JButton instead of AWT's Button
9 private int count = 0;
10
11 // Constructor to setup the GUI components and event handlers
12 public SwingCounter() {
13 // Retrieve the content-pane of the top-level container JFrame
14 // All operations done on the content-pane
15 Container cp = getContentPane();
16 cp.setLayout(new FlowLayout()); // The content-pane sets its layout
17
18 cp.add(new JLabel("Counter"));
19 tfCount = new JTextField("0");
20 tfCount.setEditable(false);
21 cp.add(tfCount);
22
23 btnCount = new JButton("Count");
24 cp.add(btnCount);
25
26 // Allocate an anonymous instance of an anonymous inner class that
27 // implements ActionListener as ActionEvent listener
28 btnCount.addActionListener(new ActionListener() {
29 @Override
30 public void actionPerformed(ActionEvent evt) {
31 ++count;
32 tfCount.setText(count + "");
33 }
34 });
35
36 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Exit program if close-window button clicked
37 setTitle("Swing Counter"); // "super" JFrame sets title
38 setSize(300, 100); // "super" JFrame sets initial size
39 setVisible(true); // "super" JFrame shows
40 }
41
42 // The entry main() method
43 public static void main(String[] args) {
44 // Run the GUI construction in the Event-Dispatching thread for thread-safety
45 SwingUtilities.invokeLater(new Runnable() {
46 @Override
47 public void run() {
48 new SwingCounter(); // Let the constructor do the job
49 }
50 });
51 }
52}
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,
Page 10 of 17
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,
setContentPane(displayPanel);
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 System.exit().
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 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,
Page 11 of 17
});
}
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() 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 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):
@SuppressWarnings("serial")
public class MyFrame extends JFrame { ...... }
Page 12 of 17
Please go to this tutorial https://o7planning.org/en/11009/javafx, read carefully and do the
following task in order:
- Install e(fx)clipse into Eclipse (JavaFX Tooling)
- Install JavaFX Scene Builder into Eclipse
3.2.2. First JavaFX Application
Please go to this tutorial JavaFX Tutorial for Beginners - Hello JavaFX, read carefully and do the task
in the tutorial.
1 import javafx.application.Application;
2 import javafx.event.ActionEvent;
3 import javafx.event.EventHandler;
4 import javafx.scene.Scene;
5 import javafx.scene.control.Button;
6 import javafx.scene.layout.StackPane;
7 import javafx.stage.Stage;
8
9 public class JavaFXHello extends Application {
10 private Button btnHello; // Declare a "Button" control
11
12 @Override
13 public void start(Stage primaryStage) {
14 // Construct the "Button" and attach an "EventHandler"
15 btnHello = new Button();
16 btnHello.setText("Say Hello");
17 // Using JDK 8 Lambda Expression to construct an EventHandler<ActionEve
18 btnHello.setOnAction(evt -> System.out.println("Hello World!"));
19
20 // Construct a scene graph of nodes
21 StackPane root = new StackPane(); // The root of scene graph is a layo
22 root.getChildren().add(btnHello); // The root node adds Button as a ch
23
24 Scene scene = new Scene(root, 300, 100); // Construct a scene given th
25 primaryStage.setScene(scene); // The stage sets scene
26 primaryStage.setTitle("Hello"); // Set window's title
27 primaryStage.show(); // Set visible (show it)
28 }
29
30 public static void main(String[] args) {
31 launch(args);
32 }
33 }
How It Works
1. A JavaFX GUI Program extends from javafx.application.Application (just like a Java Swing GUI
program extends from javax.swing.JFrame).
Page 13 of 17
2. JavaFX provides a huge set of controls (or components) in package javafx.scene.control,
including Label, Button and TextField.
3. We declare and construct a Button control, and attach
a javafx.event.EventHandler<ActionEvent> to the Button, via
method setOnAction() (of ButtonBase superclass), which takes an EventHandler<ActionEvent>,
as follows:
public final void setOnAction(EventHandler<ActionEvent> value)
The EventHandler is a Functional Interface with an abstract method handle(), defined as follows:
package javafx.event;
@FunctionalInterface
public interface EventHandler<T extends Event> extends EventListener {
void handle(T event); // public abstract
}
We can trigger the handle() by firing the button, via clicking the button with the mouse or touch, key
press, or invoke the fire() method programmatically.
In this example, we use a one-liner Lambda Expression (JDK 8) to construct an instance of Functional
Interface EventHandler. You can also use an anonymous inner class (Pre JDK 8), as follows:
btnHello.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent evt) {
System.out.println("Hello World!");
}
});
4. JavaFX uses the metaphor of a theater to model the graphics application. A stage (defined by
the javafx.stage.Stage class) represents the top-level container (window). The individual controls (or
components) are contained in a scene (defined by the javafx.scene.Scene class). An application can
have more than one scenes, but only one of the scenes can be displayed on the stage at any given time.
The contents of a scene is represented in a hierarchical scene graph of nodes (defined
by javafx.scene.Node).
Page 14 of 17
6. In this example, the root node is a "layout" node (container)
named javafx.scene.layout.StackPane, which layouts its children in a back-to-front stack. This
layout node has one child node, which is the Button. To add child node(s) under a layout, use:
aLayout.getChildren().add(Node node) // Add one node
aLayout.getChildren().addAll(Node... nodes) // Add all nodes
Notes: A JavaFX's Pane is similar to Swing's JPanel. However, JavaFX has layout-specific Pane, such
as FlowPane, GridPane and BorderPane, which is similar to a Swing’s JPanel with FlowLayout,
GridLayout and BorderLayout.
7. We allocate a javafx.scene.Scene by specifying the root of the scene graph, via constructor:
1 import javafx.application.Application;
2 import javafx.scene.Scene;
3 import javafx.scene.control.Label;
4 import javafx.scene.control.Button;
5 import javafx.scene.control.TextField;
6 import javafx.scene.layout.FlowPane;
7 import javafx.stage.Stage;
8 import javafx.geometry.Insets;
9 import javafx.geometry.Pos;
10
11 public class JavafxCounter extends Application {
12 private TextField tfCount;
13 private Button btnCount;
14 private int count = 0;
15
16 @Override
17 public void start(Stage primaryStage) {
18 // Allocate controls
19 tfCount = new TextField("0");
20 tfCount.setEditable(false);
21 btnCount = new Button("Count");
22 // Register event handler using Lambda Expression (JDK 8)
Page 15 of 17
23 btnCount.setOnAction(evt -> tfCount.setText(++count + ""));
24
25 // Create a scene graph of node rooted at a FlowPane
26 FlowPane flow = new FlowPane();
27 flow.setPadding(new Insets(15, 12, 15, 12)); // top, right, bottom, left
28 flow.setVgap(10); // Vertical gap between nodes in pixels
29 flow.setHgap(10); // Horizontal gap between nodes in pixels
30 flow.setAlignment(Pos.CENTER); // Alignment
31 flow.getChildren().addAll(new Label("Count: "), tfCount, btnCount);
32
33 // Setup scene and stage
34 primaryStage.setScene(new Scene(flow, 400, 80));
35 primaryStage.setTitle("JavaFX Counter");
36 primaryStage.show();
37 }
38
39 public static void main(String[] args) {
40 launch(args);
41 }
42 }
How It Works
1. We use 3 controls: Label, TextField and Button (in package javafx.scene.control).
2. We use a layout called FlowPane, which lays out its children in a flow that wraps at the flowpane's
boundary (like a Swing's JPanel in FlowLayout). This layout node is the root of the scene graph, which
has the 3 controls as its children.
See more at https://www3.ntu.edu.sg/home/ehchua/programming/java/Javafx1_intro.html.
Page 16 of 17
Please convert all features that you develop for the AimsProject from the CLI application
to the GUI one:
- You should use Java Swing or JavaFX to do this exercise. AWT is strongly
discouraged.
- Some suggestions for your GUI application:
o Overuse of JOptionPane is unacceptable. Your evaluation result of this part
will be 0 if you only use or abuse JOptionPane.
o You should use Menu of Java Swing or JavaFX for the CLI menu
o You should provide forms with GUI controls for listing medias/orders or
entering a new item or any other features
- Please read the following links for some tips for UI/UX design:
o https://www.cs.umd.edu/~ben/goldenrules.html
o http://athena.ecs.csus.edu/~buckley/CSc238/Psychology%20of%20UX.pdf
Page 17 of 17