So far, we have covered the basic programming constructs (such as variables, data types, decision, loop, array and method) and introduced the important concept of Object-Oriented Programming (OOP). As discussed, OOP permits higher level of abstraction than traditional Procedural-Oriented Languages (such as C). You can create high-level abstract data types called classes to mimic real-life things. These classes are self-contained and are reusable. Show In this article, I shall show you how you can reuse the graphics classes provided in JDK for constructing your own Graphical User Interface (GUI) applications. Writing your own graphics classes (and re-inventing the wheels) is mission impossible! These graphics classes, developed by expert programmers, are highly complex and involve many advanced design patterns. However, re-using them are not so difficult, if you follow the API documentation, samples and templates provided. I shall assume that you have a good grasp of OOP, including composition, inheritance, polymorphism, abstract class and interface; otherwise, read the earlier articles. I will describe another important OO concept called nested class (or inner class) in this article. There are current three sets of Java APIs for graphics programming: AWT (Abstract Windowing Toolkit), Swing and JavaFX.
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), Java3D, and etc. Furthermore, developers have moved to use technologies such as HTML5 as the basis of webapps. You need to refer to the "JDK API documentation" for the AWT/Swing APIs (under module java.desktop) 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. For 3D graphics, read my 3D articles. Programming GUI with AWTI shall start with the AWT before moving into Swing to give you a complete picture of Java Graphics. AWT PackagesAWT is huge! It consists of 12 packages of 370 classes (Swing is even bigger, with 18 packages of 737 classes as of JDK 8). Fortunately, only 2 packages - java.awt and java.awt.event - are commonly-used.
AWT provides a platform-independent and device-independent interface to develop graphic programs that runs on all platforms, including Windows, macOS, and Unixes. AWT Containers and ComponentsThere are two groups of GUI elements:
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 aContainer.add(aComponent) to add aComponent into itself. For example, Panel pnl = new Panel(); Button btn = new Button("Press"); pnl.add(btn);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. AWT Container ClassesTop-Level Containers: Frame, Dialog and AppletEach GUI program has a top-level container. The commonly-used top-level containers in AWT are Frame, Dialog and Applet:
Secondary Containers: Panel and ScrollPaneSecondary containers are placed inside a top-level container or another secondary container. AWT provides these secondary containers:
Hierarchy of the AWT Container ClassesThe hierarchy of the AWT Container classes is as follows: As illustrated, a Container has a LayoutManager to layout the components in a certain pattern, e.g., flow, grid. AWT Component ClassesAWT provides many ready-made and reusable GUI components in package java.awt. The frequently-used are: Button, TextField, Label, Checkbox, CheckboxGroup (radio buttons), List, and Choice, as illustrated below. AWT GUI Component: java.awt.LabelA java.awt.Label provides a descriptive text string. 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) to provide a text description. Check the JDK API specification for java.awt.Label. Constructors public Label(String strLabel, int alignment); public Label(String strLabel); public Label();The Label class has three constructors:
Constants (final static fields) public static final LEFT; public static final RIGHT; public static final CENTER;These three constants are defined for specifying the alignment of the Label's text, as used in the above constructor. Public Methods 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 ContainerThree steps are necessary to create and place a GUI component:
Example Label lblInput; lblInput = new Label("Enter ID"); add(lblInput); lblInput.setText("Enter password"); lblInput.getText();An Anonymous Label InstanceYou can create a Label without specifying an identifier, called anonymous instance. In the case, the Java compiler will assign an anonymous identifier for the allocated object. You will not be able to reference an anonymous instance in your program after it is created. This is usually alright for a Label instance as there is often no need to reference a Label after it is constructed. Example add(new Label("Enter Name: ", Label.RIGHT)); Label xxx = new Label("Enter Name: ", Label.RIGHT)); add(xxx);AWT GUI Component: java.awt.ButtonA java.awt.Button is a GUI component that triggers a certain programmed action upon clicking. Constructors public Button(String btnLabel); public Button();The Button class has two constructors. The first constructor creates a Button object with the given label painted over the button. The second constructor creates a Button object with no label. Public Methods public String getLabel(); public void setLabel(String btnLabel); public void setEnable(boolean enable);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"); add(btnColor); ... btnColor.setLabel("Green"); btnColor.getLabel();AWT GUI Component: java.awt.TextFieldA 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 fires an ActionEvent. Constructors public TextField(String initialText, int columns); public TextField(String initialText); public TextField(int columns);Public Methods public String getText(); public void setText(String strText); public void setEditable(boolean editable);Event Hitting the "ENTER" key on a TextField fires a ActionEvent, and triggers a certain programmed action. Example TextField tfInput = new TextField(30); add(tfInput); TextField tfResult = new TextField(); tfResult.setEditable(false) ; add(tfResult); ...... int number = Integer.parseInt(tfInput.getText()); number *= number; 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. Example 1: AWTCounterLet's assemble a few 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 shall display count of 0 initially. Each time you click the button, the counter's value increases by 1.
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. Dissecting the AWTCounter.java
Inspecting Container/Components via toString()It is interesting to inspect the GUI objects via the toString(), to gain an insight to these classes. (Alternatively, use a graphic debugger in Eclipse/NetBeans or study the JDK source code.) For example, if we insert the following code before and after the setvisible(): System.out.println(this); System.out.println(lblCount); System.out.println(tfCount); System.out.println(btnCount); setVisible(true); System.out.println(this); System.out.println(lblCount); System.out.println(tfCount); System.out.println(btnCount);Example 2: AWTAccumulatorIn this example, the top-level container is again the typical java.awt.Frame. It contains 4 components: a Label "Enter an Integer", a TextField for accepting user input, another Label "The Accumulated Sum is", and another non-editable TextField for displaying the sum. The components are arranged in GridLayout of 2 rows 2 columns. The program shall accumulate the number entered into the input TextField and display the sum in the output TextField.
Dissecting the AWTAccumulator.java
AWT Event-HandlingJava 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 MethodsIn 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. JavaScript can attach a Callback method to an Event DirectlyIn some languages, you can directly attach a method (or function) to an event (such as mouse-click). For example, the following JavaScript code (called JSCounter.html) implement a counter similar to the AWTCounter, with a text label, text field and button: <!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" onclick=myBtnOnClick() onmouseover=myBtnMouseOver() > </form> <script> function myBtnOnClick() { tfCount.value++; } function myBtnMouseOver() { btnCount.value = "Count Up"; } function myBtnMouseOut() { btnCount.value = "Click Me"; } btnCount.onmouseout = myBtnMouseOut; </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!). Source, Event and Listener ObjectsThe AWT's event-handling classes are kept in package java.awt.event. 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:
In summary, triggering a source fires an event to all its registered listeners, and invoke an appropriate handler of the listener. Revisit Example 1 AWTCounter: ActionEvent and ActionListener InterfaceClicking a Button (or hitting the "Enter" key on a TextField) fires an ActionEvent to all its ActionEvent listener(s). An ActionEvent listener must implement the ActionListener interface, which declares one abstract method called actionPerformed() as follow: public interface ActionListener { public void actionPerformed(ActionEvent evt); }Here are the event-handling steps:
The sequence diagram is as follows: Revisit Example 2 AWTAccumulator: ActionEvent and ActionListener InterfaceIn this example,
Example 3: WindowEvent and WindowListener InterfaceA WindowEvent is fired (to all its WindowEvent listeners) when a window (e.g., Frame) has been opened/closed, activated/deactivated, iconified/deiconified via the 3 buttons at the top-right corner or other means. The source of WindowEvent shall be a top-level window-container such as Frame. A WindowEvent listener must implement WindowListener interface, which declares 7 abstract event-handling methods, as follows. Among them, the windowClosing(), which is called back upon clicking the window-close button, is the most commonly-used. public void windowClosing(WindowEvent evt) public void windowOpened(WindowEvent evt) public void windowClosed(WindowEvent evt) public void windowActivated(WindowEvent evt) public void windowDeactivated(WindowEvent evt) public void windowIconified(WindowEvent evt) public void windowDeiconified(WindowEvent evt)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.
The sequence diagram is as follow: Example 4: MouseEvent and MouseListener InterfaceA MouseEvent is fired 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 evt) public void mousePressed(MouseEvent evt) public void mouseReleased(MouseEvent evt) public void mouseEntered(MouseEvent evt) public void mouseExited(MouseEvent evt)
In this example, we setup a GUI with 4 components (two anonynous Labels and two non-editable TextFields) inside a top-level container Frame, arranged in FlowLayout. To demonstrate the MouseEvent:
Try: Include a WindowListener to handle the close-window button. Example 5: MouseEvent and MouseMotionListener InterfaceA MouseEvent is also fired when you move and drag 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) public void mouseMoved(MouseEvent e)
In this example, we shall illustrate both the MouseListener and MouseMotionListener.
Try: Include a WindowListener to handle the close-window button. Example 6: KeyEvent and KeyListener InterfaceA KeyEvent is fired 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) public void keyPressed(KeyEvent e) public void keyReleased(KeyEvent e)
In this example:
Nested (Inner) ClassesWithout Inner classesIn our AWTCounter example, suppose we would like to write an external ordinary class (say MyExternalBtnListener) as our ActionEvent listener. This class shall implement ActionListener interface and override the actionPerformed() method. An example is as follows: import java.awt.*; import java.awt.event.*; public class AWTCounterExternal extends Frame { private Label lblCount; private TextField tfCount; private Button btnCount; private int count = 0; public AWTCounterExternal () { setLayout(new FlowLayout()); lblCount = new Label("Counter"); add(lblCount); tfCount = new TextField(count + "", 10); tfCount.setEditable(false); add(tfCount); btnCount = new Button("Count"); add(btnCount); MyExternalBtnListener listener = new MyExternalBtnListener(); btnCount.addActionListener(listener); setTitle("AWT Counter"); setSize(250, 100); setVisible(true); } public static void main(String[] args) { AWTCounterExternal app = new AWTCounterExternal(); } } class MyExternalBtnListener implements ActionListener { @Override public void actionPerformed(ActionEvent evt) { System.out.println("You clicked the button!"); ++count; tfCount.setText(count + ""); } }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). 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. public class MyOuterClass { ...... private class MyNestedClass1 { ...... } public static class MyNestedClass2 { ...... } ...... }A nested class has these properties:
The usages of nested class are:
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.
Dissecting the Program
(Advanced) Using an Ordinary (Outer) Class as ListenerTry moving the BtnCountListener class outside, and define it as an ordinary class. You would need to pass a reference of the AWTConnter into the constructor of BtnCountListener, and use this reference to access variables tfCount and count, through public getters or granting them to public access. public class BtnCountListener implements ActionListener { AWTCounter frame; public BtnCountListener(AWTCounter frame) { this.frame = frame; } @Override public void actionPerformed(ActionEvent evt) { frame.count++; frame.tfCount.setText(frame.count + ""); } }This code is messy! Inner class provides a much cleaner solution! Example 8: An Anonymous Inner Class as Event ListenerInstead of using a named inner class (called BtnCountListner in the previous example), we shall use an inner class without a name, known as anonymous inner class as the ActionListener in this example.
Dissecting the Program
Properties of Anonymous Inner Class
Example 9: An Anonymous Inner Class for Each SourceLet'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.
Dissecting the Program
Example 10: Using the Same Listener Instance for All the ButtonsIf 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. Using ActionEvent's getActionCommand()In the following example, we use the same instance of a "named" inner class as the listener for all the 3 buttons. The listener needs to determine which button has fired the event. This can be accomplished via the ActionEvent's getActionCommonad() method, which returns the button's label.
Using getSource() of EventObjectBesides 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,
Event Listener's Adapter ClassesExample 11: WindowAdapter for WindowListenerUsing WindowListener InterfaceRefer 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 abstract methods in order to compile the program. This is tedious, e.g., we can rewrite the WindowEventDemo using an inner class implementing ActionListener as follows:
Using WindowAdapter SuperclassAn 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,
Clearly, the adapter greatly simplifies the codes. Other Event-Listener Adapter ClassesSimilarly, 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. [SKIP] The Legacy "this" ListenerIf you read some old books, you may find many examples that use "this" object as the event listener. 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. Layout Managers and PanelA 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, CardLayout, and others. Swing added more layout manager in package javax.swing, to be described later. Container's setLayout() methodA container has a setLayout() method to set its layout manager: public void setLayout(LayoutManager mgr)To set up the layout of a Container (such as Frame, JFrame, Panel, or JPanel), you have to:
For example, Panel pnl = new Panel(); pnl.setLayout(new FlowLayout()); pnl.add(new JLabel("One")); pnl.add(new JLabel("Two")); pnl.add(new JLabel("Three")); ......Container's getLayout() methodYou can get the current layout via Container's getLayout() method. Panel pnl = new Panel(); System.out.println(pnl.getLayout());Panel's Initial LayoutPanel (and Swing's JPanel) provides a constructor to set its initial layout manager. It is because a primary function of Panel is to layout a group of component in a particular layout. public void Panel(LayoutManager layout) Panel pnl = new Panel(new BorderLayout());FlowLayoutIn the java.awt.FlowLayout, components are arranged from left-to-right inside the container in the order that they are added (via method aContainer.add(aComponent)). When one row is filled, a new row will be started. The actual appearance depends on the width of the display window. Constructors public FlowLayout(); public FlowLayout(int alignment); public FlowLayout(int alignment, int hgap, int vgap);Example
GridLayoutIn 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 public GridLayout(int rows, int columns); public GridLayout(int rows, int columns, int hgap, int vgap);Example
BorderLayoutIn java.awt.BorderLayout, the container is divided into 5 zones: EAST, WEST, SOUTH, NORTH, and CENTER. Components are added using method aContainer.add(aComponent, zone), where zone is either BorderLayout.NORTH (or PAGE_START), BorderLayout.SOUTH (or PAGE_END), BorderLayout.WEST (or LINE_START), BorderLayout.EAST (or LINE_END), or BorderLayout.CENTER. 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);Example
Using Panels as Sub-Container to Organize ComponentsAn AWT Panel is a rectangular pane, which can be used as sub-container to organized a group of related components in a specific layout (e.g., FlowLayout, BorderLayout). Panels are secondary containers, which shall be added into a top-level container (such as Frame), or another Panel. For example, the following figure shows a Frame in BorderLayout containing two Panels - panelResult in FlowLayout and panelButtons in GridLayout. panelResult is added to the NORTH, and panelButtons is added to the CENTER.
GridBagLayoutReference: Read "How to Use GridBagLayout" @ https://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html. Example 1
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 gbc.fill = GridBagConstraints.HORIZONTAL (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. BoxLayoutBoxLayout arrange components in a single row or column. It respects components' requests on the minimum sizes. [TODO] Example and diagram SwingIntroductionSwing is part of the so-called "Java Foundation Classes (JFC)" (have you heard of MFC?), which was introduced in 1997 after the release of JDK 1.1. JFC was subsequently included as an integral part of JDK since JDK 1.2. JFC consists of:
The goal of Java GUI programming is to allow the programmer to build GUI that looks good on ALL platforms. JDK 1.0's AWT was awkward and non-object-oriented (using many event.getSource()). JDK 1.1's AWT introduced event-delegation (event-driven) model, much clearer and object-oriented. JDK 1.1 also introduced inner class and JavaBeans – a component programming model for visual programming environment (similar to Visual Basic). 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. Swing's FeaturesSwing 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):
Using Swing APIIf you understood the AWT programming (in particular, container/component and event-handling), switching over to Swing (or any other Graphics packages) is straight-forward. Swing's ComponentsCompared with the AWT component classes (in package java.awt), Swing component classes (in package javax.swing) begin with a prefix "J", e.g., JButton, JTextField, JLabel, JPanel, JFrame, or JApplet. The above figure shows the class hierarchy of the swing GUI classes. Similar to AWT, there are two groups of classes: containers and components. A container is used to hold components. A container can also hold containers because it is a (subclass of) component. As a rule, do not mix heavyweight AWT components and lightweight Swing components in the same program, as the heavyweight components will always be painted on top of the lightweight components. Swing's Top-Level and Secondary ContainersJust like AWT application, a Swing application requires a top-level container. There are three top-level containers in Swing:
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 ContainerHowever, unlike AWT, the JComponents shall not be added onto the top-level container (e.g., JFrame, JApplet) directly because they are lightweight components. The JComponents must be added onto the so-called content-pane of the top-level container. Content-pane is in fact a java.awt.Container that can be used to group and layout components. You could:
Notes: If a component is added directly into a JFrame, it is added into the content-pane of JFrame instead, i.e., add(new JLabel("add to JFrame directly")); getContentPane().add(new JLabel("add to JFrame directly"));Event-Handling in SwingSwing 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 ApplicationsIn summary, to write a Swing application, you have:
Swing Program Template
I will explain this template in the following Swing example. Swing Example 1: SwingCounterLet'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.
JFrame's Content-PaneThe 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(); cp.setLayout(new FlowLayout()); cp.add(new JLabel("Counter")); ...... cp.add(tfCount); ...... cp.add(btnCount);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(); setContentPane(displayPanel); ..... getContentPane().add(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);Running the GUI Construction Codes on the Event-Dispatching ThreadIn the previous examples, we invoke the constructor directly in the entry main() method to setup the GUI components. For example, public static void main(String[] args) { new SwingCounter(); }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) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new SwingCounter(); } }); }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) 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:
Swing Example 2: SwingAccumulator
Using Visual GUI Builder - NetBeans/EclipseIf you have a complicated layout for your GUI application, you should use a GUI Builder, such as NetBeans or Eclipse to layout your GUI components in a drag-and-drop manner, similar to the popular visual languages such as Visual Basic. NetBeansFor using NetBeans GUI Builder, read my "Writing Java GUI (AWT/Swing) Application in NetBeans"; or Swing Tutorial's "Learning Swing with the NetBeans IDE". EclipseFor using Eclipse GUI Builder, read "Writing Swing Applications using Eclipse GUI Builder". LINK TO JAVA REFERENCES & RESOURCESMORE REFERENCES & RESOURCES |