Advanced GUI Programming
Advanced GUI Programming
In graphical programming (I), the basic event handling and Swing components have been discussed. This section
strengthens the understanding of the Java Swing further to some more complex components which are used
intensively in real world Swing applications.
Dialogs
As we have mentioned, there are root containers that host other components. We have seen JFrame a lot in
previous chapter. In this section, we want to discuss another root container: Dialog. The
javax.swing.JDialog is the base class for all kinds of dialogs. Normally, you may not need to implement a
dialog by yourself, Swing provides some standard dialogs that you can use directly. Every dialog is dependent on
a frame, the dialog will be destroyed as soon as the frame is destroyed. A dialog can be modal, which means it
blocks user input to all other windows in the program. Let us take a look at three basic classes that support the
modal dialog.
JOptionPane
JOptionPane is used to create a set of standard modal dialogs. For most simple dialogs, you create and show the
dialog using one of JOptionPanes showXxxDialog methods. The two most important methods are the
showMessageDialog and showOptionDialog.
Recall the example for the user registration panel developed in previous chapter. Once the user enters the
invalid date of birth in the formatted field, an error dialog will come out showing the validation error, as shown
in Figure 1. This is a typical scenario inwhich you could use the JOptionPanes showMessageDialog method.
There are several overloads for this method, the complete one is:
showMessageDialog(component,message,title,messageType,icon)
The component indicates the parent component for the dialog, it can be set to null. The message argument
specifies the message showing on the dialog. The title determines the title of the dialog. The message type is an
integer value such as PLAIN_MESSAGE, WARNING_MESSAGE and ERROR_MESSAGE from the JOptionPane class.
The icon refers to the icon showing before the message. The title, message type and icon optional default values
will be used if not specified. The following code shows how the validation error dialog is created:
JOptionPane.showMessageDialog(null,
"'" + dateOfBirth + "' is not a valid date",
"Validation Error", JOptionPane.ERROR_MESSAGE);
The code uses the showOptionDialog method to show an option dialog with YES or NO choices. The options
array customizes the text on the buttons related to the two choices. The return value of the method is an integer
that indicates the decision made by the user. If the user chooses the YES option, we save the document, otherwise
a new document will be created. You can also use the JOptionPanes showConfirmDialog to solve the same
problem. The difference between the two methods is the showOptionDialog allows you to specify the text
information on the buttons.
However, it is not quite interesting just to show the dialog. Both methods return an integer indicating the users
actions (open, save or cancel). For example, when the user selects a file and clicks the open button, you should
know what the user does and what file the user has opened:
int decision = fileChooser.showOpenDialog();
// check the decision
if (decision==JfileChooser.APPROVE_OPTION){
// get the selected file
File selectedFile = fileChooser.getSelectedFile();
// read data from the file
The showSaveDialog works exactly the same way. By checking the returned value from the dialog, you can
decide what to do.
int decision = fileChooser.showSaveDialog();
// check the decision
if (decision==JfileChooser.APPROVE_OPTION){
// get the selected file
File selectedFile = fileChooser.getSelectedFile();
// write data to the file
Another interesting feature of the file chooser is that you are able to restrict the user to open or save certain types
of files by applying a file filter. For example, if you want to restrict the user only to manipulate .txt files, you
could implement a file filter as follows:
fileChooser.setFileFilter(new FileFilter() {
// The description of the type of file such as .txt
public String getDescription() {
return "*.txt";
}
public boolean accept(File file) {
// accept only directories or file with .txt extension
return file.isDirectory() ||
file.getAbsolutePath().endWith(".txt");
}
});
The getDescription method returns the string of the supported type, *.txt in this case. The description will
be shown at the Files of Type drop down list in the dialog. The accept method is responsible for checking the
file whether it is acceptable or should be filtered. In this case, all the directories and all files with .txt extension
are acceptable.
The first argument is the parent component for this dialog, the second argument is the title of the dialog, and the
third argument is the default value for the color dialog. As Swing has done so much for you to choose a color, you
do not have to implement a complicated dialog for this purpose.
Advanced Containers
The normal containers like JFrame, JPanel and Dialogs contain components without any special effects,
developers have to arrange the layout and put the components on the right place. However, it lacks of support for
some particular layout arrangement such as tabs, split bars between different areas, trees and tables. Java Swing
provides special components including JTabbedPane, JSplitPane, JTree and JTable for those situations.
JTabbedPane
With the JTabbedPane component, several components, such as panels, could share the same space. The user
chooses which component to view by selecting the tab corresponding to the desired component. Figure 6 shows
an example of using the JTabbedPane with three tabs. Each tab contains different components, when user choose
different tab, those components will be presented within the shared area. The complete implementation of this
example is shown in Program 1.
The init method creates the JTabbedPane and three separate JPanel with a sub-component each. These three
JPanels are then added as a tab for each of them using the addTab method from the JTabbedPane class.
When creating a JSplitPane, it is able to control how the two components split each other by giving a int value
(HORIZONTAL_SPLIT or VERTICAL_SPLIT). The first component passing into the constructor of the
JSplitPane will be on the left or top depending on the split policy and the second component will be on the right
or bottom. It is also fairly easy to nest split panes, developers just need to pass the JSplitPane instance to the
constructor arguments, as shown in the program code, the top pane is actually a split pane which nests inside the
main split pane with another JPanel object.
JTree
JTree is very useful when there is hierarchical data that needs to be displayed in the GUI application. A JTree
object does not actually contain the data; it simply provides a view of the data. Like any non-trivial Swing
component, the tree gets data by querying its data model. Every tree has a root node from which all nodes descend.
A node can either have children or not.
As shown in Figure 8, the tree displays the some of the sections of the book. The root of the tree is the title of
the book, and each chapter is a sub node under the root. Especially, the chapter 14 contains the section 13.4.8 and
all the component sections. If the user selects one of the sections, a text showing the path of selection will be
shown in the text area on the right side of the frame. This simple example demonstrates how to create a JTree
instance with various tree nodes, and also how to handle selection event with TreeSelectionListener interface. The
complete implementation of this example is given in Program 3.
javax.swing.JFrame;
javax.swing.JScrollPane;
javax.swing.JSplitPane;
javax.swing.JTextArea;
javax.swing.JTree;
javax.swing.event.TreeSelectionEvent;
javax.swing.event.TreeSelectionListener;
javax.swing.tree.*;
The example creates a tree with its default tree model by passing the root tree node into the constructor. The
creation of the tree nodes are straightforward. The setSelectionMode method is used to determine what selection
mode should be used including SINGLE_TREE_SELECTION, CONTIGUOUS_TREE_SELECTION and
DISCONTIGUOUS_TREE_SELECTION.
If the DefaultTreeModel does not suit the needs of the application, then it is able to create a customized data
model that implements the TreeModel interface. It is also possible to change the look and feel of each tree node
by providing an instance of DefaultTreeCellRenderer to the JTree instance use the setCellRenderer method.
Moreover, the setRootVisible(boolean visiable) method can be used to control the visibility of the root node and
the setShowsRootHandles(boolean handle) method can be used to control whether to show the root handles.
JTable
Unlike the JTree which displays the hierarchical data, JTable class provides a way to display data as a table, JTable
does not contain or cache data; it is simply a view of the data. Every table object uses a table model object to
manage the actual table data. Figure 9 shows a simple JTable example that displays items for a given purchase
order. The example not only demonstrates the ability of the JTable displaying data, it also allows the user to edit
particular columns of data such as the unit price and quantity which will dynamically updates the original data.
The total amount will be recalculated after the price or quantity has been updated and reflected on the table. The
complete implementation of this example is given in Program 4.
import
import
import
import
import
javax.swing.JFrame;
javax.swing.JOptionPane;
javax.swing.JScrollPane;
javax.swing.JTable;
javax.swing.table.AbstractTableModel;
case 0:
value =
break;
case 1:
value =
break;
case 2:
value =
break;
case 3:
value =
break;
case 4:
value =
break;
}
return value;
item.getItemCode();
item.getItemDescription();
item.getSinglePrice();
item.getQuantity();
item.getTotalAmount();
}
public boolean isCellEditable(int row, int col) {
// only allows to change the quantity and price
return col == 3 || col == 2;
}
//set the value of the data at a given row and column
public void setValueAt(Object value, int row, int col) {
try {
Item item = order.getItems().get(row);
switch (col) {
case 2:
item.setSinglePrice(
Double.parseDouble(value.toString()));
break;
case 3:
item.setQuantity(
Integer.parseInt(value.toString()));
break;
}
fireTableCellUpdated(row, col);
} catch (Exception ex) {
JOptionPane.showMessageDialog(null,
"Wrong data type for input");
}
}
}
// The purchase order class
class PurchaseOrder {
// list of items
private List<Item> items = new ArrayList<Item>();
public PurchaseOrder(List<Item> items) {
super();
this.items = items;
}
public List<Item> getItems() {
return items;
}
}
// the item class
class Item {
private String itemCode;
private String itemDescription;
private double singlePrice;
The creation of the user interface is very simple in this example, the init method simply instantiates a JTable
instance by passing a customized TableModel implementation (PurchaseOrderTableModel). Notice that the
PurchaseOrderTableModel does not implement the TableModel directly, as the AbstractTableModel provides a
lot of default implementation for the TableModel, it then gives more flexibility. The getColumnName method is
used to get the name of a given column. For example, column two as defined in the model refers to Unit Price
displayed on the table heading. The getColumnCount simply get the total number of columns that the model has.
getRowCount identifies how many rows the model has. isCellEditable tells the JTable which set of cells are
editable. In this example, the column two and three allows user to edit.The getValueAt method is used by the
JTable to extract data at a given row and column. The setValueAt in contrast is used to update the value for a given
row and column. In general, the implementation of the setValueAt method needs to validate the input value and
notify the table view that the data has been changed by using the fireTableCellUpdated(row, col) method.
Similar to the TreeModel, TableModel(Model) provides a way to control the actual data without affecting the
user interface(View), along with the delegation event model(Controller) which controls what actions need to be
taken when certain event raises. This Model-View-Controller (MVC) philosophy exists in all of the Java GUI
development process, all most every component provided by Java GUI library adopts this philosophy as its
primary design principle. The next section will briefly explain MVC more in details.
Model-View-Controller
Model-View-Controller known as MVC is a design pattern derived from SmallTalk language to build visual
applications. It is also the design principle behind Java Swing library. The basic idea of this pattern is to divide a
visual application into three separate parts: Model, View, and Controller.
Controller is responsible for capturing user inputs on the view and converting to the model
The isolation of these three parts enforces a basic software design principle known as Single Responsibility
Principle (SRP) introduced by Tom DeMarco in his book Structured Analysis and Systems Specification, Yourdon
Press Computing Series, 1979. It then had been re-interpreted by Robert Cecil Martin (Uncle Bob) for ObjectOrientation as part of his Principles of Object-Oriented Design. According to Uncle Bobs interpretation, a
responsibility is a reason to change, and a class or module should have one and only one reason to change.
Applying this principle to the MVC pattern, the model only needs to change when the structure of data is different,
the view only needs to change when a different view is required, or the controller only needs to change how it
handles different models and views. For example, if you want to change the look and feel of your Swing
components from Windows XP to Solaris Motif, you will not expect to change the state or data bound to those
components. It is what MVC helps you, to avoid changing the models when you have to change your view of
those models. The following code simply changes the whole look and feel of your Swing program to Solaris Motif
schema:
UIManager.setLookAndFeel(
"com.sun.java.swing.plaf.motif.MotifLookAndFeel");
The concept of the MVC design pattern is to split the model, the view, and the controller into three different
entities. The Java Swing toolkit, however, has made a slight modification against the original MVC, as shown in
Figure 10. It merges the View and Controller into one entity: a UI Component also known as a UI delegate.
Swing MVC
Generic MVC
the
Re
nde
mo
d
da
ta
ut
np
s
ea
te
ri
se
Cr
el
tu
Ge
od
m
el
r to
ta
da
te
ea
Cr
use
r
C
Get user inputs
Swing UI Component
Java Applet
Swing provides an excellent library to develop standalone GUI applications running on top of the operating
systems over various platforms. However, at the very beginning when the Java first came out, it provided
interactive features to the web applications that can not be done by HTML. These features were brought to the
users by means of Java Applets that run inside a web browser. Java Applet is a very old technology introduced in
the first version of the Java language in 1995. It was also the dominant technology provided by Java at that time.
Java developers at the early years were normally writing Java Applets rather than the standalone applications
using AWT. Although, Applet is used to develop interactive web applications, the actual code is running inside
the clients Java virtual machine (JVM) rather than executing on the server side such as the web servers.
1
APPLET
Development
hello.java
AT
SUN.COM
hello.class
AT SUNS
WEB
SERVER
Create
Applet
tag in
HTML
document
4
Accessing
from
Your Organisation
5
The browser
creates
a new
window and
a new thread
and
then runs the
code
Hello Java
<app=
Hello>
The Internet
Hello
o
Applets are usually loaded automatically via its lifecycle methods rather than executing via the main
method;
Applets are embedded inside the web page and executed in browsers;
The applet tag uses the code attribute to locate the applet class. If the applet is packaged into a jar file, the
codebase attribute can be used to locate the URI of the jar file. The width and height attributes determine the
rectangular area where the applet is executed. If your applet needs to read some parameters, you could provide
param tag specifying a name/value pair nested inside the applet tag. The identical approach of making use of the
new object tag is shown as the following:
<object classid="java:HelloWorldApplet.class" width="200" height="200">
<param name="message" value="Hello World!">
</object>
Which one to use is up to your choice, even though it is recommended to use the object tag over the applet
tag, as the latter has been deprecated and may be removed in the future.
Java also provides a very useful command line tool the Applet viewer that can be used to load the applet during
development. Before we jump into a real applet example, it is worth to mention about the lifecycle of a Java
Applet.
The Lifecycle of Applets
As we have pointed out that the applets will be loaded automatically by the JVM at runtime, the Applets
specification defines a set of standard methods that each applet must follow. These methods including init,
start, paint, stop, and destroy are so called lifecycle callbacks which will be called when the JVM loads
Begin
init()
Born
stop()
start()
Running
paint()
Idle
start()
destroy()
Dead
End
Initialization: the init method will be invoked only once when the applet is first loaded;
Running: the start method will be called automatically by the system after the execution of init
method. It can also be called when applets moves from idle/stop state to running state. For example,
when we return back to the web page after temporarily visiting other pages;
Display: paint method will be called immediately after the applet enters into the running state. It is
responsible for displaying output;
Idle: stop method will be executed when the applet is stopped from running. For example, it occurs
when we leave a web page;
Dead/Destroyed: the destroy method will be invoked automatically when we close the browser
window or the applet viewer.
import
import
import
import
java.lang.*;
java.awt.*;
javax.swing.*;
javax.swing.JApplet;
When you open the HTML page using the Internet Explorer (IE), the result should not be surprising which has
been shown in Figure 13. To develop Java Applet, you do not have to start from scratch and care about all the
lifecycle methods in the specification. Swing provides the JApplet base class which your applet can extend from.
What you really need to worry about is where to put your application logic in. For example, you would probably
put all the initialization code by overriding the init method from the base class. Our example overrides the init
method where it first reads the parameters for the calculation and calculates the result according to those
parameters. The paint method will draw the result onto the screen once the applet is loaded and displayed to the
client. This example shows very simple usage scenario for Java applet. We will discuss a more attractive example
in the next section and you will be able to learn how to write interactive applets.
Eclipse is a well-known and popular open source Java Integrated Development Environment (IDE), you can download the latest Eclipse at
http://eclipse.org.
3
4
5
6
7
8
9
10
11
12
15
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import javax.swing.JApplet;
public class CarApplet extends JApplet{
// car images in different direction
private Image carLeft = null;
private Image carRight = null;
private Image carUp = null;
private Image carDown = null;
// current car
private Image carImage = null;
// geometric information for the car
private int direction = -1;
private Point carLocation = new Point(25,25);
// speed for the car
private int speed = 10;
public void setCar(Image carImage) {
this.carImage = carImage;
}
public Image getCar() {
return carImage;
}
private int getDirection() {
return direction;
}
private void setDirection(int direction) {
this.direction = direction;
}
private Point getCarLocation() {
return carLocation;
}
private void setCarLocation(Point carLocation) {
this.carLocation = carLocation;
}
public void paint(Graphics g) {
Image car = getCar(); // get current car image
if (car == null) // throw exception if null
throw new RuntimeException("Error");
// first clean the whole area
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
// draw the car at the new position
g.drawImage(car, getCarLocation().x, getCarLocation().y, this);
}
public void init() {
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
repaint();
}});
}
}
The above code has 112 lines, which is probably is the longest code list we have ever seen in this book so far.
Do not be scared by the length of the code, because the logic behind the implementation is relatively simple and
straightforward. Let us look at this example to understand how the magic to move a car in the applet happens.
From lines 1 to 3, we import the required Java packages for the applet. As you may notice, the CarApplet
extends the javax.swing.JApplet class which is essential for every applet (you can also extend your applet
directly from java.awt.Applet, however, in this case, you lose the opportunity to utilize Java Swing
components). Lines 7 to 10 define four car images that will be used for different directions including north, south,
west and east. It is also important to know what the current car image is null, the image object at line 12 serves
this purpose. Besides the car images that you need to draw on the screen, the geometric information for your car
is also very important and must be recorded in the program. The properties defined at lines 14 and 15 make these
information available to the entire applet. A car speed property at line 17 is for you to control how far the car will
move. From lines 19 to 42 are some common setter/getter methods.
The paint method from lines 43 to 53 is very important for you to understand how we simulate the movement
on the screen. The movement of the car is actually simulated by repainting the car image according to its current
location and direction as soon as the client types the up, down, left or right key. In the paint method, we first get
the current car image, and then the runtime exception will be thrown if the car image is null to prevent faulty
operations. Before we draw the car image, we have to fill the whole applet area with white color to overwrite the
previous car image on the screen. Then we just invoke the drawImage method to draw the car image at current
car location recorded in the program.
Now, you have seen how we simulate the movement of the car. The only thing left is to determine which
direction the car will move and how far the move will be. Let us take a deep look at the init method, especially
the inner KeyListener class from lines 55 to 120. The first part of the init method concentrates on loading the
car images from the external image files. The getImage method will load the image file to an image object. We
have used the getCodeBase method to find out the codebase for this applet. If you run the applet using the applet
viewer, the codebase will be the directory where you put your applet class in. For example, my applet class is at
file:/D:/java/eclipse/workspace/JavaBook/target/classes/com/javabook/gui/applet/CarApplet.class,the
getCodeBase method returns file:/D:/java/eclipse/ workspace/JavaBook/target/classes, We also put all our .gif
files into the file:/D:/java/eclipse/workspace/JavaBook/target/classes/icon directory. So I could use the
getCodeBase() + "/icon/carLeft.gif" to get the absolute URI of the image file for loading. That is what
exactly lines 57 to 60 do to load the images. At line 62, we set the default car image as the one to the east.
In order to capture the key event, the CarApplet has to register a KeyListener. At line 63, we make use of
the convenient KeyAdapter class to provide our key KeyListener implementation as an anonymous inner class.
The keyPressed method has been overridden to handle the key event. The first step is to find out the cars current
location as shown as lines 66 and 67. Then it is important to determine which key the user has actually pressed.
We gain this information by invoking the KeyEvent.getKeyCode at line 70. The switch-case-default
statement captures cases for up, down, left and right. Other keys pressed by the user will be ignored. Each situation
for the movement looks pretty similar. If up key is pressed, we replace the current car image with carUp if the
current direction is not north, then we decrease the Y location by speed amount simulating moving car to north
by speed amount. If down key is pressed, we replace the current car image to carDown if the current direction is
not south, then we increase the Y location by speed amount simulating moving car to south by speed amount. If
left key is pressed, we replace the current car image to carLeft if the current direction is not west, then we
decrease the X location by speed amount simulating moving car to west by speed amount. If right key is pressed,
we replace the current car image to carRight if the current direction is not east, then we increase the X location
by speed amount simulating moving car to east by speed amount. As soon as we replace the car image with correct
direction and recalculate the new location for the car, we record the direction and the car location by setting the
properties in the program. Finally, the repaint method is called to tell the applet to repaint itself. Consequently,
the applet itself will invoke the paint method to redraw the proper car image at new location.
Until now, you should get a very clear understanding about the magic we did in this example. You can actually
extend this example to be more interesting such as a car racing game. To that extent, you probably need animation
support for the car, as a hint, you could use the MediaTracker class to accomplish this challenging work. The
gif files used in this example can be downloaded from the book website.
}
private void loadAudioClip(String audioClipUrl)
throws MalformedURLException{
//load the audio clip from the given url
AudioClip audioClip = getAudioClip(new URL(audioClipUrl));
//set it as the current playing clip
setPlayingAudioClip(audioClip);
}
private void log(String message){
loggerLabel.setText(message);
}
private void createPlayerGUI(){
//create the information panel at the top of the applet
final JTextField audioUrlField = new JTextField(50);
final JLabel label = new JLabel("Audio clip URL ->");
JPanel infoPanel = new JPanel();
infoPanel.setLayout(new BorderLayout());
infoPanel.add(label,BorderLayout.WEST);
infoPanel.add(audioUrlField,BorderLayout.CENTER);
//create the control panel at the bottom of the applet
final JButton playBtn = new JButton("Play");
final JButton loopBtn = new JButton("Loop");
final JButton stopBtn = new JButton("Stop");
JPanel controlPanel = new JPanel();
controlPanel.add(playBtn);
controlPanel.add(loopBtn);
controlPanel.add(stopBtn);
//hook event handlers for actions
playBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
try{
loadAudioClip(audioUrlField.getText());
getPlayingAudioClip().play();
log("INFO -> Play Audio!");
}catch(Exception ex){
log("ERROR -> "+ex.getMessage());
}
}
});
stopBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
AudioClip audioClip = getPlayingAudioClip();
if(audioClip != null){
audioClip.stop();
log("INFO -> Audio Stopped!");
}else{
log("INFO -> No Audio to Stop!");
}
}
});
loopBtn.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
try{
loadAudioClip(audioUrlField.getText());
getPlayingAudioClip().loop();
log("INFO -> Loop Audio!");
}catch(Exception ex){
log("ERROR -> "+ex.getMessage());
}
}
});
//add these two panel to the main applet
getContentPane().setLayout(new BorderLayout());
getContentPane().add(infoPanel, BorderLayout.NORTH);
getContentPane().add(controlPanel, BorderLayout.CENTER);
//add the error display label to the south
getContentPane().add(loggerLabel, BorderLayout.SOUTH);
}
public AudioClip getPlayingAudioClip() {
return currentAudio;
}
public void setPlayingAudioClip(AudioClip audipClip) {
this.currentAudio = audipClip;
}
}
The loadAudioClip method internally use the getAudioClip(URL url) method of the Applet class to get an
instance of the AudoClip interface. Whenever a specific button is clicked, the action listener implemented will try
to invoke the play, loop and stop method on the given AudioClip. All other codes are related to GUI creation
which will not be explained further. It needs to be aware how the Applet.init method invokes the Swing component
creation method createPlayerGUI. It uses javax.swing.SwingUtilities.invokeAndWait method instead
of directly calling the method. The reason behind this is that Swing components should be created, queried, and
manipulated on the event-dispatching thread, but browsers don't invoke applet lifecycle methods from that thread.
For this reason, the lifecycle methods init, start, stop, and destroy should use the SwingUtilities method
invokeAndWait (or, if appropriate, invokeLater) so that code that refers to the Swing components is executed on
the event-dispatching thread. For more information about threads, please refer to the thread programming chapter.
Instead of using the AudioClip interface, Applet class provides a method play which serves the same purpose of
playing audio clips.
AppletContext
Applets can obtain additional services from an AppletContext object, which returns from the method
getAppletContext () in the java.awt.Applet class. The methods of the AppletContext interface include:
getApplets () - returns an enumeration of references to other applets running on same html page.
getApplet (String name) - returns a reference to the applet called name used in the Name attribute in the
applet tag.
showDocument(URL url) and showDocument (URL url, String target) are used to load the web page
linked to the url. In the first case the applet page will be replaced. In the second case, target can be
"_self"
- show in current frame
"_top"
- show in topmost frame
showStatus (String msg) - show the string msg on the bottom edge status line of the browser frame.
getImage (URL url), getAudioClip (URL url) - return image and audioclip references.
Iterator getStreamKeys()finds all the keys of the streams in this applet context.
void setStream(String key, InputStream stream) associates the specified stream with
the specified key in this applet context.
Figure 16 demonstrates a simple example using the AppletContext object to load web pages. The program
load the URL entered by the user and open the web site. The status bar will be updated according to the action.
For example, the figure depicts one scenario that the user forget to put protocol http before the website
www.gridbus.org. The complete code is shown in Program 8. Note, this example will only work in web browsers.
java.applet.AppletContext;
java.awt.BorderLayout;
java.awt.event.ActionEvent;
java.awt.event.ActionListener;
java.net.URL;
import
import
import
import
import
javax.swing.JApplet;
javax.swing.JButton;
javax.swing.JLabel;
javax.swing.JPanel;
javax.swing.JTextField;
TheAppletContext.showDocument method is used to show the web page, and the AppletContext.showStatus
method is used to update the status information in the web browsers status bar . The program get the
AppletContext object via Applet.getAppletContext method.
AppletStub
AppletStub serves as the interface between the applet and the browser environment or applet viewer environment
in which the application is running. In general, developers does not need to deal with this interface directly as it
will be automatically set by the Applet runtime when the Applet is first created by using the setStub method. The
methods of the AppletStub interface include:
void appletResize(int width, int height) is called when the applet want to resize
Program 9
/* MockAppletStub.java A customized applet stub implementation*/
package com.javabook.gui.applet;
import
import
import
import
import
import
import
java.applet.Applet;
java.applet.AppletContext;
java.applet.AppletStub;
java.io.IOException;
java.net.MalformedURLException;
java.net.URL;
java.util.Properties;
Program 10
/* AppletStubDemo.java An example shows how to use AppletStub interface */
package com.javabook.gui.applet;
import java.awt.Graphics;
import javax.swing.JApplet;
import javax.swing.JFrame;
The MockAppletStub implements the AppletStub interface, it simply returns the code base and document base
as the current directory where the class has been loaded. The getParameter returns the parameter value by looking
up the properties defined in a parameters.properties file located in the current document base.
The implication of this single thread model is that any updates to the GUI have to be done within this thread,
other threads which wish to access the GUI components will cause exceptions.
thread. This method returns immediately, without waiting for the code to execute.
invokeAndWait(Runnable runnable): Acts like invokeLater(), except that this method waits for the
code to execute. As a rule, you should use invokeLater() instead of this method.
The logic inside the Runnable implementation will be gurranted to execute inside the event dispatcher thread
which will not cause any exceptions. For example, the long running operations can be implemented inside a
runnable implementation and updates GUI inside the run methods.
updateClock();
}
});
JPanel panel = new JPanel();
panel.add(clockLabel);
panel.add(button);
getContentPane().add(panel);
pack();
setTitle("Clock Window");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
private void updateClock() {
//start updater thread
new ClockUpdater(clockLabel).start();
}
public static void main(String[] args) {
new NonBlockingClock();
}
}
/* The clock updater thread whick updates the clock every second */
class ClockUpdater extends Thread {
private JLabel clockLabel;
public ClockUpdater(JLabel clockLabel) {
this.clockLabel = clockLabel;
}
public void run() {
while (true) {
//invoke setTime inside the event dispatcher thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
setTime();
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void setTime() {
clockLabel.setText(
new Date(System.currentTimeMillis()).toString());
}
}
By running this program, the window shown as Figure 18. Once a start clock button is clicked, a new
ClockUpdater thread is started and periodically (every second) updates the clock label. The update method has
been invoked inside the SwingUtilities to make sure that the operation is executed inside the event dispatcher
thread.