A fundamental design guideline is make simple things easy, and difficult things possible. [74]
The original design goal of the graphical user interface (GUI) library in Java 1.0 was to allow the programmer to build a GUI that looks good on all platforms. That goal was not achieved. Instead, the Java 1.0 Abstract Window Toolkit (AWT) produced a GUI that looked equally mediocre on all systems. In addition, it was restrictive; you could use only four fonts and you couldnt access any of the more sophisticated GUI elements that exist in your operating system. The Java 1.0 AWT programming model is also awkward and non-object-oriented. A student in one of my seminars (who had been at Sun during the creation of Java) explained why: The original AWT had been conceptualized, designed, and implemented in a month. Certainly a marvel of productivity, and also an object lesson in why design is important. Feedback
The situation improved with the Java 1.1 AWT event model, which takes a much clearer, object-oriented approach, along with the addition of JavaBeans, a component programming model that is oriented toward the easy creation of visual programming environments. Java 2 (JDK 1.2) finished the transformation away from the old Java 1.0 AWT by essentially replacing everything with the Java Foundation Classes (JFC), the GUI portion of which is called Swing. These are a rich set of easy-to-use, easy-to-understand JavaBeans that can be dragged and dropped (as well as hand programmed) to create a GUI that you can (finally) be satisfied with. The revision 3 rule of the software industry (a product isnt good until revision 3) seems to hold true with programming languages as well. Feedback
This chapter does not cover anything but the modern Java 2 Swing library and makes the reasonable assumption that Swing is the final destination GUI library for Java.[75] If for some reason you need to use the original old AWT (because youre supporting old code or you have browser limitations), you can find that introduction in the first edition of this book, downloadable at www.BruceEckel.com (also included on the CD ROM bound with this book) Note that some AWT components remain in Java, and in some situations you must use them. Feedback
Early in this chapter, youll see how things are different when you want to create an applet versus a regular application using Swing, and how to create programs that are both applets and applications so they can be run either inside a browser or from the command line. Almost all the GUI examples in this book will be executable as both applets and applications. Feedback
Please be aware that this is not a comprehensive glossary of either all the Swing components or all the methods for the described classes. What you see here is intended to be simple. The Swing library is vast, and the goal of this chapter is only to get you started with the essentials and comfortable with the concepts. If you need to do more, then Swing can probably give you what you want if youre willing to do the research. Feedback
I assume here that you have downloaded and installed the JDK library documents in HTML format from java.sun.com and will browse the javax.swing classes in that documentation to see the full details and methods of the Swing library. Because of the simplicity of the Swing design, this will often be enough information to solve your problem. There are numerous (rather thick) books dedicated solely to Swing, and youll want to go to those if you need more depth, or if you want to modify the default Swing behavior. Feedback
As you learn about Swing, youll discover: Feedback
Swing contains all the components that you expect to see in a modern UI: everything from buttons that contain pictures to trees and tables. Its a big library, but its designed to have appropriate complexity for the task at hand; if something is simple, you dont have to write much code, but as you try to do more complex things, your code becomes proportionally more complex. This means an easy entry point, but youve got the power if you need it. Feedback
Much of what youll like about Swing could be called orthogonality of use. That is, once you pick up the general ideas about the library, you can apply them everywhere. Primarily because of the standard naming conventions, much of the time that I was writing these examples I could guess at the method names and get it right the first time without looking anything up. This is certainly the hallmark of a good library design. In addition, you can generally plug components into other components and things will work correctly. Feedback
For speed, all the components are lightweight, and Swing is written entirely in Java for portability. Feedback
Keyboard navigation is automatic; you can run a Swing application without using the mouse, and this doesnt require any extra programming. Scrolling support is effortless; you simply wrap your component in a JScrollPane as you add it to your form. Features such as tool tips typically require a single line of code to use. Feedback
Swing also supports a rather radical feature called pluggable look and feel, which means that the appearance of the UI can be dynamically changed to suit the expectations of users working under different platforms and operating systems. Its even possible (albeit difficult) to invent your own look and feel. Feedback
Java has the ability to create applets, which are little programs that run inside a Web browser. Because they must be safe, applets are limited in what they can accomplish. However, applets are a powerful tool that support client-side programming, a major issue for the Web. Feedback
Programming within an applet is so restrictive that its often referred to as being inside the sandbox, since you always have someonethat is, the Java run-time security systemwatching over you. Feedback
However, you can also step outside the sandbox and write regular applications rather than applets, in which case you can access the other features of your OS. Weve been writing regular applications all along in this book, but theyve been console applications without any graphical components. Swing can be used to build GUI interfaces for regular applications. Feedback
You can generally answer the question of what an applet is able to do by looking at what it is supposed to do: extend the functionality of a Web page in a browser. Since, as a Net surfer, you never really know if a Web page is from a friendly place or not, you want any code that it runs to be safe. So the biggest restrictions youll notice are probably: Feedback
If you can live within the restrictions, applets have definite advantages, especially when building client/server or other networked applications: Feedback
Because applets are automatically integrated with HTML, you have a built-in platform-independent documentation system to support the applet. Its an interesting twist, since were used to having the documentation part of the program rather than vice versa. Feedback
Libraries are often grouped according to their functionality. Some libraries, for example, are used as is, off the shelf. The standard Java library String and ArrayList classes are examples of these. Other libraries are designed specifically as building blocks to create other classes. A certain category of library is the application framework, whose goal is to help you build applications by providing a class or set of classes that produces the basic behavior that you need in every application of a particular type. Then, to customize the behavior to your own needs, you inherit from the application class and override the methods of interest. The application frameworks default control mechanism will call your overridden methods at the appropriate time. An application framework is a good example of separating the things that change from the things that stay the same, since it attempts to localize all the unique parts of a program in the overridden methods.[76] Feedback
Applets are built using an application framework. You inherit from class JApplet and override the appropriate methods. There are a few methods that control the creation and execution of an applet on a Web page:
|
Method |
Operation |
|---|---|
|
init( ) |
Automatically called to perform first-time initialization of the applet, including component layout. Youll always override this method. |
|
start( ) |
Called every time the applet moves into sight on the Web browser to allow the applet to start up its normal operations (especially those that are shut off by stop( )). Also called after init( ). |
|
stop( ) |
Called every time the applet moves out of sight on the Web browser to allow the applet to shut off expensive operations. Also called right before destroy( ). |
|
destroy( ) |
Called when the applet is being unloaded from the page to perform final release of resources when the applet is no longer used |
With this information you are ready to create a simple applet:
//: c14:Applet1.java
// Very simple applet.
import javax.swing.*;
import java.awt.*;
public class Applet1 extends JApplet {
public void init() {
getContentPane().add(new JLabel("Applet!"));
}
} ///:~Note that applets are not required to have a main( ). Thats all wired into the application framework; you put any startup code in init( ). Feedback
In this program, the only activity is putting a text label on the applet, via the JLabel class (the old AWT appropriated the name Label as well as other names of components, so you will often see a leading J used with Swing components). The constructor for this class takes a String and uses it to create the label. In the preceding program this label is placed on the form. Feedback
The init( ) method is responsible for putting all the components on the form using the add( ) method. You might think that you ought to be able to simply call add( ) by itself, and in fact thats the way it used to be in the old AWT. However, Swing requires that you add all components to the content pane of a form, so you must call getContentPane( ) as part of the add( ) process. Feedback
To run this program you must place it inside a Web page and view that page inside your Java-enabled Web browser. To place an applet inside a Web page, you put a special tag inside the HTML source for that Web page[77] to tell the page how to load and run the applet. Feedback
This process used to be very simple, when Java itself was simple and everyone was on the same bandwagon and incorporated the same Java support inside their Web browsers. Then you might have been able to get away with a very simple bit of HTML inside your Web page, like this:
<applet code=Applet1 width=100 height=50> </applet>
Then along came the browser and language wars, and we (programmers and end users alike) lost. After awhile, Sun realized that we could no longer expect browsers to support the correct flavor of Java, and the only solution was to provide some kind of add-on that would conform to a browsers extension mechanism. By using the extension mechanism (which a browser vendor cannot disablein an attempt to gain competitive advantagewithout breaking all the third-party extensions), Sun guarantees that Java cannot be shut out of the Web browser by an antagonistic vendor. Feedback
With Internet Explorer, the extension mechanism is the ActiveX control, and with Netscape, it is the plug-in. In your HTML code, you must provide tags to support both, but you can automatically generate the necessary tags with the HTMLconverter tool that comes with the JDK download. Heres what the simplest resulting HTML page looks like for Applet1 after running HTMLconverter on the preceding applet tag:
<!--"CONVERTED_APPLET"-->
<!-- HTML CONVERTER -->
<OBJECT
classid = "clsid:CAFEEFAC-0014-0001-0000-ABCDEFFEDCBA"
codebase = "http://java.sun.com/products/plugin/autodl/jinstall-1_4_1-windows-i586.cab#Version=1,4,1,0"
WIDTH = 100 HEIGHT = 50 >
<PARAM NAME = CODE VALUE = Applet1 >
<PARAM NAME = "type" VALUE = "application/x-java-applet;jpi-version=1.4.1">
<PARAM NAME = "scriptable" VALUE = "false">
<COMMENT>
<EMBED
type = "application/x-java-applet;jpi-version=1.4.1"
CODE = Applet1
WIDTH = 100
HEIGHT = 50
scriptable = false
pluginspage = "http://java.sun.com/products/plugin/index.html#download">
<NOEMBED>
</NOEMBED>
</EMBED>
</COMMENT>
</OBJECT>
<!--
<APPLET CODE = Applet1 WIDTH = 100 HEIGHT = 50>
</APPLET>
-->
<!--"END_CONVERTED_APPLET"-->Some of these lines were too long and had to be wrapped to fit on the page. The code in this books source code (downloadable from www.BruceEckel.com) will work without having to worry about correcting line wraps. Feedback
The code value gives the name of the .class file where the applet resides. The width and height specify the initial size of the applet (in pixels, as before). There are other items you can place within the applet tag: a place to find other .class files on the Internet (codebase), alignment information (align), a special identifier that makes it possible for applets to communicate with each other (name), and applet parameters to provide information that the applet can retrieve. Parameters are in the form:
<param name="identifier" value = "information">
and there can be as many as you want. Feedback
The source code package for this book (freely downloadable at www.BruceEckel.com) provides an HTML page for each of the applets in this book, and thus many examples of the applet tag, all driven from the index.html file corresponding to this chapters source code. You can find a full and current description of the details of placing applets in Web pages at java.sun.com. Feedback
Suns JDK contains a tool called the Appletviewer that picks the <applet> tags out of the HTML file and runs the applets without displaying the surrounding HTML text. Because the Appletviewer ignores everything but APPLET tags, you can put those tags in the Java source file as comments:
// <applet code=MyApplet width=200 height=100></applet>
This way, you can run appletviewer MyApplet.java and you dont need to create tiny HTML files to run tests. For example, you can add the commented HTML tags to Applet1.java:
//: c14:Applet1b.java
// Embedding the applet tag for Appletviewer.
// <applet code=Applet1b width=100 height=50></applet>
import javax.swing.*;
import java.awt.*;
public class Applet1b extends JApplet {
public void init() {
getContentPane().add(new JLabel("Applet!"));
}
} ///:~Now you can invoke the applet with the command
appletviewer Applet1b.java
In this book, this form will be used for easy testing of applets. Shortly, youll see another coding approach that will allow you to execute applets from the command line without the Appletviewer. Feedback
You can perform a simple test without any network connection by starting up your Web browser and opening the HTML file containing the applet tag. As the HTML file is loaded, the browser will discover the applet tag and go hunt for the .class file specified by the code value. Of course, it looks at the CLASSPATH to find out where to hunt, and if your .class file isnt in the CLASSPATH, then it will give an error message on the status line of the browser to the effect that it couldnt find that .class file. Feedback
When you want to try this out on your Web site, things are a little more complicated. First of all, you must have a Web site, which for most people means a third-party Internet Service Provider (ISP) at a remote location. Since the applet is just a file or set of files, the ISP does not have to provide any special support for Java. You must also have a way to move the HTML files and the .class files from your site to the correct directory on the ISP machine. This is typically done with a File Transfer Protocol (FTP) program, of which there are many different types available for free or as shareware. So it would seem that all you need to do is move the files to the ISP machine with FTP, then connect to the site and HTML file using your browser; if the applet comes up and works, then everything checks out, right? Feedback
Heres where you can get fooled. If the browser on the client machine cannot locate the .class file on the server, it will hunt through the CLASSPATH on your local machine. Thus, the applet might not be loading properly from the server, but to you it looks fine during your testing process because the browser finds it on your machine. When someone else connects, however, his or her browser cant find it. So when youre testing, make sure you erase the relevant .class files (or .jar file) on your local machine to verify that they exist in the proper location on the server. Feedback
One of the most insidious places where this happened to me is when I innocently placed an applet inside a package. After uploading the HTML file and applet, it turned out that the server path to the applet was confused because of the package name. However, my browser found it in the local CLASSPATH. So I was the only one who could properly load the applet. Its important to specify the full class name including the package in the CODE parameter of your applet tag. In many published applet examples, the applet is not put inside a package, but its generally best to use packages in production code. Feedback
There are times when youd like to make a windowed program do something else other than sit on a Web page. Perhaps youd also like it to do some of the things a regular application can do, but still have the vaunted instant portability provided by Java. In previous chapters in this book weve made command-line applications, but in some operating environments (the Macintosh, for example) there isnt a command line. So for any number of reasons, youd like to build a windowed, non-applet program using Java. This is certainly a reasonable desire. Feedback
The Swing library allows you to make an application that preserves the look and feel of the underlying operating environment. If you want to build windowed applications, it makes sense to do so[78] only if you can use the latest version of Java and associated tools so you can deliver applications that wont confound your users. If for some reason youre forced to use an older version of Java, think hard before committing to building a significant windowed application. Feedback
Often youll want to be able to create a class that can be invoked as either a window or an applet. This is especially convenient when youre testing the applets, since its typically much faster and easier to run the resulting applet-application from the command line than it is to start up a Web browser or the Appletviewer. Feedback
To create an applet that can be run from the console command line, you simply add a main( ) to your applet that builds an instance of the applet inside a Jframe.[79] As a simple example, lets look at Applet1b.java modified to work as both an application and an applet:
//: c14:Applet1c.java
// An application and an applet.
// <applet code=Applet1c width=100 height=50></applet>
import javax.swing.*;
import java.awt.*;
public class Applet1c extends JApplet {
public void init() {
getContentPane().add(new JLabel("Applet!"));
}
// A main() for the application:
public static void main(String[] args) {
JApplet applet = new Applet1c();
JFrame frame = new JFrame("Applet1c");
// To close the application:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(applet);
frame.setSize(100,50);
applet.init();
applet.start();
frame.setVisible(true);
}
} ///:~main( ) is the only element added to the applet, and the rest of the applet is untouched. The applet is created and added to a JFrame so that it can be displayed. Feedback
You can see that in main( ), the applet is explicitly initialized and started because in this case the browser isnt available to do it for you. Of course, this doesnt provide the full behavior of the browser, which also calls stop( ) and destroy( ), but for most situations its acceptable. If its a problem, you can force the calls yourself.[80] Feedback
Notice the last line:
frame.setVisible(true);
Without this, you wont see anything on the screen. Feedback
Although the code that turns programs into both applets and applications produces valuable results, if used everywhere it becomes distracting and wastes paper. Instead, the following display framework will be used for the Swing examples in the rest of this book:
//: com:bruceeckel:swing:Console.java
// Tool for running Swing demos from the
// console, both applets and JFrames.
package com.bruceeckel.swing;
import javax.swing.*;
import java.awt.event.*;
public class Console {
// Create a title string from the class name:
public static String title(Object o) {
String t = o.getClass().toString();
// Remove the word "class":
if(t.indexOf("class") != -1)
t = t.substring(6);
return t;
}
public static void
run(JFrame frame, int width, int height) {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(width, height);
frame.setVisible(true);
}
public static void
run(JApplet applet, int width, int height) {
JFrame frame = new JFrame(title(applet));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(applet);
frame.setSize(width, height);
applet.init();
applet.start();
frame.setVisible(true);
}
public static void
run(JPanel panel, int width, int height) {
JFrame frame = new JFrame(title(panel));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(panel);
frame.setSize(width, height);
frame.setVisible(true);
}
} ///:~This is a tool you may want to use yourself, so its placed in the library com.bruceeckel.swing. The Console class consists entirely of static methods. The first is used to extract the class name (using RTTI) from any object and to remove the word class, which is typically prepended by getClass( ). This uses the String methods indexOf( ) to determine whether the word class is there, and substring( ) to produce the new string without class or the trailing space. This name is used to label the window that is displayed by the run( ) methods. Feedback
setDefaultCloseOperation( ) causes a JFrame to exit a program when that JFrame is closed. The default behavior is to do nothing, so if you dont call setDefaultCloseOperation( ) or write the equivalent code for your JFrame, the application wont close. Feedback
The run( ) method is overloaded to work with JApplets, JPanels, and JFrames. Note that only if its a JApplet are init( ) and start( ) called. Feedback
Now any applet can be run from the console by creating a main( ) containing a line like this:
Console.run(new MyClass(), 500, 300);
in which the last two arguments are the display width and height. Heres Applet1c.java modified to use Console:
//: c14:Applet1d.java
// Console runs applets from the command line.
// <applet code=Applet1d width=100 height=50></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Applet1d extends JApplet {
public void init() {
getContentPane().add(new JLabel("Applet!"));
}
public static void main(String[] args) {
Console.run(new Applet1d(), 100, 50);
}
} ///:~This allows the elimination of repeated code while providing the greatest flexibility in running the examples. Feedback
Making a button is quite simple: you just call the JButton constructor with the label you want on the button. Youll see later that you can do fancier things, like putting graphic images on buttons. Feedback
Usually, youll want to create a field for the button inside your class so that you can refer to it later. Feedback
The JButton is a componentits own little windowthat will automatically get repainted as part of an update. This means that you dont explicitly paint a button or any other kind of control; you simply place them on the form and let them automatically take care of painting themselves. So to place a button on a form, you do it inside init( ):
//: c14:Button1.java
// Putting buttons on an applet.
// <applet code=Button1 width=200 height=50></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Button1 extends JApplet {
private JButton
b1 = new JButton("Button 1"),
b2 = new JButton("Button 2");
public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(b1);
cp.add(b2);
}
public static void main(String[] args) {
Console.run(new Button1(), 200, 50);
}
} ///:~Something new has been added here: Before any elements are placed on the content pane, it is given a new layout manager, of type FlowLayout. The layout manager is the way that the pane implicitly decides where to place the control on the form. The normal behavior of an applet is to use the BorderLayout, but that wont work here because (as you will learn later in this chapter when controlling the layout of a form is examined in more detail) it defaults to covering each control entirely with every new one that is added. However, FlowLayout causes the controls to flow evenly onto the form, left to right and top to bottom. Feedback
Youll notice that if you compile and run the preceding applet, nothing happens when you press the buttons. This is where you must step in and write some code to determine what will happen. The basis of event-driven programming, which comprises a lot of what a GUI is about, is tying events to code that responds to those events. Feedback
The way that this is accomplished in Swing is by cleanly separating the interface (the graphical components) and the implementation (the code that you want to run when an event happens to a component). Each Swing component can report all the events that might happen to it, and it can report each kind of event individually. So if youre not interested in, for example, whether the mouse is being moved over your button, you dont register your interest in that event. Its a very straightforward and elegant way to handle event-driven programming, and once you understand the basic concepts, you can easily use Swing components that you havent seen beforein fact, this model extends to anything that can be classified as a JavaBean (discussed later in the chapter). Feedback
At first, we will just focus on the main event of interest for the components being used. In the case of a JButton, this event of interest is that the button is pressed. To register your interest in when a button is pressed, you call the JButtons addActionListener( ) method. This method expects an argument that is an object that implements the ActionListener interface, which contains a single method called actionPerformed( ). So all you have to do to attach code to a JButton is to implement the ActionListener interface in a class, and register an object of that class with the JButton via addActionListener( ). The method will be called when the button is pressed (this is normally referred to as a callback). Feedback
But what should the result of pressing that button be? Wed like to see something change on the screen, so a new Swing component will be introduced: the JTextField. This is a place where text can be typed, or in this case, inserted by the program. Although there are a number of ways to create a JTextField, the simplest is just to tell the constructor how wide you want that field to be. Once the JTextField is placed on the form, you can modify its contents by using the setText( ) method (there are many other methods in JTextField, but you must look these up in the HTML documentation for the JDK from java.sun.com). Here is what it looks like:
//: c14:Button2.java
// Responding to button presses.
// <applet code=Button2 width=200 height=75></applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Button2 extends JApplet {
private JButton
b1 = new JButton("Button 1"),
b2 = new JButton("Button 2");
private JTextField txt = new JTextField(10);
class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
String name = ((JButton)e.getSource()).getText();
txt.setText(name);
}
}
private ButtonListener bl = new ButtonListener();
public void init() {
b1.addActionListener(bl);
b2.addActionListener(bl);
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(b1);
cp.add(b2);
cp.add(txt);
}
public static void main(String[] args) {
Console.run(new Button2(), 200, 75);
}
} ///:~Creating a JTextField and placing it on the canvas takes the same steps as for Jbuttons or for any Swing component. The difference in the preceding program is in the creation of the aforementioned ActionListener class ButtonListener. The argument to actionPerformed( ) is of type ActionEvent, which contains all the information about the event and where it came from. In this case, I wanted to describe the button that was pressed; getSource( ) produces the object where the event originated, and I assumed (using a cast) that the object is a JButton. getText( ) returns the text thats on the button, and this is placed in the JTextField to prove that the code was actually called when the button was pressed. Feedback
In init( ), addActionListener( ) is used to register the ButtonListener object with both the buttons. Feedback
It is often more convenient to code the ActionListener as an anonymous inner class, especially since you tend to use only a single instance of each listener class. Button2.java can be modified to use an anonymous inner class as follows: Feedback
//: c14:Button2b.java
// Using anonymous inner classes.
// <applet code=Button2b width=200 height=75></applet>
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Button2b extends JApplet {
private JButton
b1 = new JButton("Button 1"),
b2 = new JButton("Button 2");
private JTextField txt = new JTextField(10);
private ActionListener bl = new ActionListener() {
public void actionPerformed(ActionEvent e) {
String name = ((JButton)e.getSource()).getText();
txt.setText(name);
}
};
public void init() {
b1.addActionListener(bl);
b2.addActionListener(bl);
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(b1);
cp.add(b2);
cp.add(txt);
}
public static void main(String[] args) {
Console.run(new Button2b(), 200, 75);
}
} ///:~The approach of using an anonymous inner class will be preferred (when possible) for the examples in this book. Feedback
A JTextArea is like a JTextField except that it can have multiple lines and has more functionality. A particularly useful method is append( ); with this you can easily pour output into the JTextArea, thus making a Swing program an improvement (since you can scroll backward) over what has been accomplished thus far using command-line programs that print to standard output. As an example, the following program fills a JTextArea with the output from the geography generator in Chapter 11:
//: c14:TextArea.java
// Using the JTextArea control.
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;
public class TextArea extends JFrame {
private JButton
b = new JButton("Add Data"),
c = new JButton("Clear Data");
private JTextArea t = new JTextArea(20, 40);
private Map m = new HashMap();
public TextArea() {
// Use up all the data:
Collections2.fill(m, Collections2.geography,
CountryCapitals.pairs.length);
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Iterator it = m.entrySet().iterator();
while(it.hasNext()) {
Map.Entry me = (Map.Entry)(it.next());
t.append(me.getKey() + ": "+ me.getValue()+"\n");
}
}
});
c.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
t.setText("");
}
});
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(new JScrollPane(t));
cp.add(b);
cp.add(c);
}
public static void main(String[] args) {
Console.run(new TextArea(), 475, 425);
}
} ///:~This is a JFrame rather than a JApplet because it reads from the local disk, and therefore cannot be run as an applet in an HTML page. Feedback
In init( ), the Map is filled with all the countries and their capitals. Note that for both buttons, the ActionListener is created and added without defining an intermediate variable, since you never need to refer to that listener again during the program. The Add Data button formats and appends all the data, and the Clear Data button uses setText( ) to remove all the text from the JTextArea. Feedback
As the JTextArea is added to the applet, it is wrapped in a JScrollPane to control scrolling when too much text is placed on the screen. Thats all you must do in order to produce full scrolling capabilities. Having tried to figure out how to do the equivalent in some other GUI programming environments, I am very impressed with the simplicity and good design of components like JScrollPane. Feedback
The way that you place components on a form in Java is probably different from any other GUI system youve used. First, its all code; there are no resources that control placement of components. Second, the way components are placed on a form is controlled not by absolute positioning but by a layout manager that decides how the components lie based on the order that you add( ) them. The size, shape, and placement of components will be remarkably different from one layout manager to another. In addition, the layout managers adapt to the dimensions of your applet or application window, so if the window dimension is changed, the size, shape, and placement of the components can change in response. Feedback
JApplet, JFrame JWindow, and JDialog can all produce a Container with getContentPane( ) that can contain and display Components. In Container, theres a method called setLayout( ) that allows you to choose a different layout manager. Other classes, such as JPanel, contain and display components directly, so you also set the layout manager directly, without using the content pane. Feedback
In this section well explore the various layout managers by placing buttons in them (since thats the simplest thing to do). There wont be any capturing of button events because these examples are just intended to show how the buttons are laid out. Feedback
Applets use a default layout scheme: the BorderLayout (a number of the previous examples have changed the layout manager to FlowLayout). Without any other instruction, this takes whatever you add( ) to it and places it in the center, stretching the object all the way out to the edges. Feedback
However, theres more to the BorderLayout. This layout manager has the concept of four border regions and a center area. When you add something to a panel thats using a BorderLayout, you can use the overloaded add( ) method that takes a constant value as its first argument. This value can be any of the following:
|
Top |
|
|
BorderLayout. SOUTH |
Bottom |
|
BorderLayout. EAST |
Right |
|
BorderLayout. WEST |
Left |
|
BorderLayout.CENTER |
Fill the middle, up to the other components or to the edges |
If you dont specify an area to place the object, it defaults to CENTER. Feedback
Heres a simple example. The default layout is used, since JApplet defaults to BorderLayout:
//: c14:BorderLayout1.java
// Demonstrates BorderLayout.
//<applet code=BorderLayout1 width=300 height=250></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class BorderLayout1 extends JApplet {
public void init() {
Container cp = getContentPane();
cp.add(BorderLayout.NORTH, new JButton("North"));
cp.add(BorderLayout.SOUTH, new JButton("South"));
cp.add(BorderLayout.EAST, new JButton("East"));
cp.add(BorderLayout.WEST, new JButton("West"));
cp.add(BorderLayout.CENTER, new JButton("Center"));
}
public static void main(String[] args) {
Console.run(new BorderLayout1(), 300, 250);
}
} ///:~For every placement but CENTER, the element that you add is compressed to fit in the smallest amount of space along one dimension while it is stretched to the maximum along the other dimension. CENTER, however, spreads out in both dimensions to occupy the middle. Feedback
This simply flows the components onto the form, from left to right until the top space is full, then moves down a row and continues flowing. Feedback
Heres an example that sets the layout manager to FlowLayout and then places buttons on the form. Youll notice that with FlowLayout, the components take on their natural size. A JButton, for example, will be the size of its string. Feedback
//: c14:FlowLayout1.java
// Demonstrates FlowLayout.
// <applet code=FlowLayout1 width=300 height=250></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class FlowLayout1 extends JApplet {
public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
for(int i = 0; i < 20; i++)
cp.add(new JButton("Button " + i));
}
public static void main(String[] args) {
Console.run(new FlowLayout1(), 300, 250);
}
} ///:~All components will be compacted to their smallest size in a FlowLayout, so you might get a little bit of surprising behavior. For example, because a JLabel will be the size of its string, attempting to right-justify its text yields an unchanged display when using FlowLayout. Feedback
A GridLayout allows you to build a table of components, and as you add them, they are placed left-to-right and top-to-bottom in the grid. In the constructor you specify the number of rows and columns that you need, and these are laid out in equal proportions. Feedback
//: c14:GridLayout1.java
// Demonstrates GridLayout.
// <applet code=GridLayout1 width=300 height=250></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class GridLayout1 extends JApplet {
public void init() {
Container cp = getContentPane();
cp.setLayout(new GridLayout(7,3));
for(int i = 0; i < 20; i++)
cp.add(new JButton("Button " + i));
}
public static void main(String[] args) {
Console.run(new GridLayout1(), 300, 250);
}
} ///:~In this case there are 21 slots but only 20 buttons. The last slot is left empty because no balancing goes on with a GridLayout. Feedback
The GridBagLayout provides you with tremendous control in deciding exactly how the regions of your window will lay themselves out and reformat themselves when the window is resized. However, its also the most complicated layout manager, and is quite difficult to understand. It is intended primarily for automatic code generation by a GUI builder (GUI builders might use GridBagLayout instead of absolute placement). If your design is so complicated that you feel you need to use GridBagLayout, then you should be using a GUI builder tool to generate that design. If you feel you must know the intricate details, Ill refer you to Core Java 2, Volume 1, by Horstmann & Cornell (Prentice Hall, 2001), or a dedicated Swing book as a starting point. Feedback
It is also possible to set the absolute position of the graphical components in this way:
Some GUI builders use this approach extensively, but this is usually not the best way to generate code. Feedback
Because people had so much trouble understanding and working with GridBagLayout, Swing also includes BoxLayout, which gives you many of the benefits of GridBagLayout without the complexity, so you can often use it when you need to do hand-coded layouts (again, if your design becomes too complex, use a GUI builder that generates layouts for you). BoxLayout allows you to control the placement of components either vertically or horizontally, and to control the space between the components using something called struts and glue. First, lets see how to use the BoxLayout directly, in the same way that the other layout managers have been demonstrated:
//: c14:BoxLayout1.java
// Vertical and horizontal BoxLayouts.
// <applet code=BoxLayout1 width=450 height=200></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class BoxLayout1 extends JApplet {
public void init() {
JPanel jpv = new JPanel();
jpv.setLayout(new BoxLayout(jpv, BoxLayout.Y_AXIS));
for(int i = 0; i < 5; i++)
jpv.add(new JButton("jpv " + i));
JPanel jph = new JPanel();
jph.setLayout(new BoxLayout(jph, BoxLayout.X_AXIS));
for(int i = 0; i < 5; i++)
jph.add(new JButton("jph " + i));
Container cp = getContentPane();
cp.add(BorderLayout.EAST, jpv);
cp.add(BorderLayout.SOUTH, jph);
}
public static void main(String[] args) {
Console.run(new BoxLayout1(), 450, 200);
}
} ///:~The constructor for BoxLayout is a bit different than the other layout managersyou provide the Container that is to be controlled by the BoxLayout as the first argument, and the direction of the layout as the second argument. Feedback
To simplify matters, theres a special container called Box that uses BoxLayout as its native manager. The following example lays out components horizontally and vertically using Box, which has two static methods to create boxes with vertical and horizontal alignment:
//: c14:Box1.java
// Vertical and horizontal BoxLayouts.
// <applet code=Box1 width=450 height=200></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box1 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
for(int i = 0; i < 5; i++)
bv.add(new JButton("bv " + i));
Box bh = Box.createHorizontalBox();
for(int i = 0; i < 5; i++)
bh.add(new JButton("bh " + i));
Container cp = getContentPane();
cp.add(BorderLayout.EAST, bv);
cp.add(BorderLayout.SOUTH, bh);
}
public static void main(String[] args) {
Console.run(new Box1(), 450, 200);
}
} ///:~Once you have a Box, you pass it as a second argument when adding components to the content pane. Feedback
Struts add space, measured in pixels, between components. To use a strut, you simply add it between the addition of the components that you want spaced apart:
//: c14:Box2.java
// Adding struts.
// <applet code=Box2 width=450 height=300></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box2 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
for(int i = 0; i < 5; i++) {
bv.add(new JButton("bv " + i));
bv.add(Box.createVerticalStrut(i * 10));
}
Box bh = Box.createHorizontalBox();
for(int i = 0; i < 5; i++) {
bh.add(new JButton("bh " + i));
bh.add(Box.createHorizontalStrut(i * 10));
}
Container cp = getContentPane();
cp.add(BorderLayout.EAST, bv);
cp.add(BorderLayout.SOUTH, bh);
}
public static void main(String[] args) {
Console.run(new Box2(), 450, 300);
}
} ///:~Struts separate components by a fixed amount, but glue is the opposite; it separates components by as much as possible. Thus its more of a spring than glue (and the design on which this was based was called springs and struts, so the choice of the term is a bit mysterious). Feedback
//: c14:Box3.java
// Using Glue.
// <applet code=Box3 width=450 height=300></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box3 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
bv.add(new JLabel("Hello"));
bv.add(Box.createVerticalGlue());
bv.add(new JLabel("Applet"));
bv.add(Box.createVerticalGlue());
bv.add(new JLabel("World"));
Box bh = Box.createHorizontalBox();
bh.add(new JLabel("Hello"));
bh.add(Box.createHorizontalGlue());
bh.add(new JLabel("Applet"));
bh.add(Box.createHorizontalGlue());
bh.add(new JLabel("World"));
bv.add(Box.createVerticalGlue());
bv.add(bh);
bv.add(Box.createVerticalGlue());
getContentPane().add(bv);
}
public static void main(String[] args) {
Console.run(new Box3(), 450, 300);
}
} ///:~A strut works in one direction, but a rigid area fixes the spacing between components in both directions:
//: c14:Box4.java
// Rigid areas are like pairs of struts.
// <applet code=Box4 width=450 height=300></applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;
public class Box4 extends JApplet {
public void init() {
Box bv = Box.createVerticalBox();
bv.add(new JButton("Top"));
bv.add(Box.createRigidArea(new Dimension(120, 90)));
bv.add(new JButton("Bottom"));
Box bh = Box.createHorizontalBox();
bh.add(new JButton("Left"));
bh.add(Box.createRigidArea(new Dimension(160, 80)));
bh.add(new JButton("Right"));
bv.add(bh);
getContentPane().add(bv);
}
public static void main(String[] args) {
Console.run(new Box4(), 450, 300);
}
} ///:~You should be aware that rigid areas are a bit controversial. Since they use absolute values, some people feel that they cause more trouble than they are worth. Feedback
Swing is powerful; it can get a lot done with a few lines of code. The examples shown in this book are reasonably simple, and for learning purposes it makes sense to write them by hand. You can actually accomplish quite a bit by combining simple layouts. At some point, however, it stops making sense to hand-code GUI forms; it becomes too complicated and is not a good use of your programming time. The Java and Swing designers oriented the language and libraries to support GUI building tools, which have been created for the express purpose of making your programming experience easier. As long as you understand whats going on with layouts and how to deal with the events (described next), its not particularly important that you actually know the details of how to lay out components by hand; let the appropriate tool do that for you (Java is, after all, designed to increase programmer productivity). Feedback
In the Swing event model, a component can initiate (fire) an event. Each type of event is represented by a distinct class. When an event is fired, it is received by one or more listeners, which act on that event. Thus, the source of an event and the place where the event is handled can be separate. Since you typically use Swing components as they are, but need to write code that is called when the components receive an event, this is an excellent example of the separation of interface and implementation. Feedback
Each event listener is an object of a class that implements a particular type of listener interface. So as a programmer, all you do is create a listener object and register it with the component thats firing the event. This registration is performed by calling an addXXXListener( ) method in the event-firing component, in which XXX represents the type of event listened for. You can easily know what types of events can be handled by noticing the names of the addListener methods, and if you try to listen for the wrong events, youll discover your mistake at compile time. Youll see later in the chapter that JavaBeans also use the names of the addListener methods to determine what events a Bean can handle. Feedback
All of your event logic, then, will go inside a listener class. When you create a listener class, the sole restriction is that it must implement the appropriate interface. You can create a global listener class, but this is a situation in which inner classes tend to be quite useful, not only because they provide a logical grouping of your listener classes inside the UI or business logic classes they are serving, but also because (as you shall see later) an inner-class object keeps a reference to its parent object, which provides a nice way to call across class and subsystem boundaries. Feedback
All the examples so far in this chapter have been using the Swing event model, but the remainder of this section will fill out the details of that model. Feedback
All Swing components include addXXXListener( ) and removeXXXListener( ) methods so that the appropriate types of listeners can be added and removed from each component. Youll notice that the XXX in each case also represents the argument for the method, for example, addMyListener(MyListener m). The following table includes the basic associated events, listeners, and methods, along with the basic components that support those particular events by providing the addXXXListener( ) and removeXXXListener( ) methods. You should keep in mind that the event model is designed to be extensible, so you may encounter other events and listener types that are not covered in this table. Feedback
|
Components supporting this event |
|
|---|---|
|
ActionEvent |
JButton, JList, JTextField, JMenuItem and its derivatives including JCheckBoxMenuItem, JMenu, and JpopupMenu |
|
AdjustmentEvent |
JScrollbar |
|
ComponentEvent |
*Component and its derivatives, including JButton, JCheckBox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, JFrame, JLabel, JList, JScrollbar, JTextArea, and JTextField |
|
ContainerEvent |
Container and its derivatives, including JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, and JFrame |
|
FocusEvent |
Component and derivatives* |
|
KeyEvent |
Component and derivatives* |
|
MouseEvent (for both clicks and motion) |
Component and derivatives* |
|
MouseEvent[81] (for both clicks and motion) |
Component and derivatives* |
|
WindowEvent |
Window and its derivatives, including JDialog, JFileDialog, and JFrame |
|
ItemEvent |
JCheckBox, JCheckBoxMenuItem, JComboBox, JList, and anything that implements the ItemSelectable interface |
|
TextEvent |
Anything derived from JTextComponent, including JTextArea and JTextField |
You can see that each type of component supports only certain types of events. It turns out to be rather difficult to look up all the events supported by each component. A simpler approach is to modify the ShowMethods.java program from Chapter 10 so that it displays all the event listeners supported by any Swing component that you enter. Feedback
Chapter 10 introduced reflection and used that feature to look up methods for a particular classeither the entire list of methods or a subset of those whose names match a keyword that you provide. The magic of reflection is that it can automatically show you all the methods for a class without forcing you to walk up the inheritance hierarchy, examining the base classes at each level. Thus, it provides a valuable timesaving tool for programming; because the names of most Java methods are made nicely verbose and descriptive, you can search for the method names that contain a particular word of interest. When you find what you think youre looking for, check the JDK documentation. Feedback
However, by Chapter 10 you hadnt seen Swing, so the tool in that chapter was developed as a command-line application. Here is the more useful GUI version, specialized to look for the addListener methods in Swing components:
//: c14:ShowAddListeners.java
// Display the "addXXXListener" methods of any Swing class.
// <applet code=ShowAddListeners
// width=500 height=400></applet>
import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.util.regex.*;
import com.bruceeckel.swing.*;
public class ShowAddListeners extends JApplet {
private JTextField name = new JTextField(25);
private JTextArea results = new JTextArea(40, 65);
private static Pattern addListener =
Pattern.compile("(add\\w+?Listener\\(.*?\\))");
private static Pattern qualifier =
Pattern.compile("\\w+\\.");
class NameL implements ActionListener {
public void actionPerformed(ActionEvent e) {
String nm = name.getText().trim();
if(nm.length() == 0) {
results.setText("No match");
return;
}
Class klass;
try {
klass = Class.forName("javax.swing." + nm);
} catch(ClassNotFoundException ex) {
results.setText("No match");
return;
}
Method[] methods = klass.getMethods();
results.setText("");
for(int i = 0; i < methods.length; i++) {
Matcher matcher =
addListener.matcher(methods[i].toString());
if(matcher.find())
results.append(qualifier.matcher(
matcher.group(1)).replaceAll("") + "\n");
}
}
}
public void init() {
NameL nameListener = new NameL();
name.addActionListener(nameListener);
JPanel top = new JPanel();
top.add(new JLabel("Swing class name (press ENTER):"));
top.add(name);
Container cp = getContentPane();
cp.add(BorderLayout.NORTH, top);
cp.add(new JScrollPane(results));
// Initial data and test:
name.setText("JTextArea");
nameListener.actionPerformed(
new ActionEvent("", 0 ,""));
}
public static void main(String[] args) {
Console.run(new ShowAddListeners(), 500,400);
}
} ///:~You enter the Swing class name that you want to look up in the name JTextField. The results are extracted using regular expressions, and displayed in a JTextArea. Feedback
Youll notice that there are no buttons or other components to indicate that you want the search to begin. Thats because the JTextField is monitored by an ActionListener. Whenever you make a change and press ENTER, the list is immediately updated. If the text field isnt empty, it is used inside Class.forName( ) to try to look up the class. If the name is incorrect, Class.forName( ) will fail, which means that it throws an exception. This is trapped, and the JTextArea is set to No match. But if you type in a correct name (capitalization counts), Class.forName( ) is successful, and getMethods( ) will return an array of Method objects. Feedback
Two regular expressions are used here. The first, addListener, looks for add followed by any word characters, followed by Listener and the argument list in parentheses. Notice that this whole regular expression is surrounded by non-escaped parentheses, which means it will be accessible as a regular expression group when it matches. Inside NameL.ActionPerformed( ), a Matcher is created by passing each Method object to the Pattern.matcher( ) method. When find( ) is called for this Matcher object, it returns true only if a match occurs, and in that case you can select the first matching parenthesized group by calling group(1). This string still contains qualifiers, so to strip them off the qualifier Pattern object is used just as it was in c09:ShowMethods.java. Feedback
At the end of init( ), an initial value is placed in name and the action event is run to provide a test with initial data.
This program is a convenient way to investigate the capabilities of a Swing component. Once you know which events a particular component supports, you dont need to look anything up to react to that event. You simply:
Here are some of the listener interfaces:
|
Methods in interface |
|
|---|---|
|
ActionListener |
actionPerformed(ActionEvent) |
|
AdjustmentListener |
adjustmentValueChanged( |
|
ComponentListener |
componentHidden(ComponentEvent) |
|
ContainerListener |
componentAdded(ContainerEvent) |
|
FocusListener |
focusGained(FocusEvent) |
|
KeyListener |
keyPressed(KeyEvent) |
|
MouseListener |
mouseClicked(MouseEvent) |
|
MouseMotionListener |
mouseDragged(MouseEvent) |
|
WindowListener |
windowOpened(WindowEvent) |
|
ItemListener |
itemStateChanged(ItemEvent) |
This is not an exhaustive listing, partly because the event model allows you to create your own event types and associated listeners. Thus, youll regularly come across libraries that have invented their own events, and the knowledge gained in this chapter will allow you to figure out how to use these events. Feedback
In the table above, you can see that some listener interfaces have only one method. These are trivial to implement, because youll implement them only when you want to write that particular method. However, the listener interfaces that have multiple methods can be less pleasant to use. For example, if you want to capture a mouse click (that isnt already captured for you, for example, by a button), then you need to write a method for mouseClicked( ). But since MouseListener is an interface, you must implement all of the other methods even if they dont do anything. This can be annoying. Feedback
To solve the problem, some (but not all) of the listener interfaces that have more than one method are provided with adapters, the names of which you can see in the table above. Each adapter provides default empty methods for each of the interface methods. Then all you need to do is inherit from the adapter and override only the methods you need to change. For example, the typical MouseListener youll use looks like this:
class MyMouseListener extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
// Respond to mouse click...
}
}The whole point of the adapters is to make the creation of listener classes easy. Feedback
There is a downside to adapters, however, in the form of a pitfall. Suppose you write a MouseAdapter like the previous one:
class MyMouseListener extends MouseAdapter {
public void MouseClicked(MouseEvent e) {
// Respond to mouse click...
}
}This doesnt work, but it will drive you crazy trying to figure out why, since everything will compile and run fineexcept that your method wont be called for a mouse click. Can you see the problem? Its in the name of the method: MouseClicked( ) instead of mouseClicked ( ). A simple slip in capitalization results in the addition of a completely new method. However, this is not the method thats called when the window is closing, so you dont get the desired results. Despite the inconvenience, an interface will guarantee that the methods are properly implemented. Feedback
To prove to yourself that these events are in fact being fired, and as an interesting experiment, its worth creating an applet that tracks extra behavior in a JButton (in addition to whether it has been pressed). This example also shows you how to inherit your own button object because thats what is used as the target of all the events of interest. To do so, you can just inherit from Jbutton.[82] Feedback
The MyButton class is an inner class of TrackEvent, so MyButton can reach into the parent window and manipulate its text fields, which is whats necessary to be able to write the status information into the fields of the parent. Of course, this is a limited solution, since MyButton can be used only in conjunction with TrackEvent. This kind of code is sometimes called highly coupled:
//: c14:TrackEvent.java
// Show events as they happen.
// <applet code=TrackEvent width=700 height=500></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import com.bruceeckel.swing.*;
public class TrackEvent extends JApplet {
private HashMap h = new HashMap();
private String[] event = {
"focusGained", "focusLost", "keyPressed",
"keyReleased", "keyTyped", "mouseClicked",
"mouseEntered", "mouseExited", "mousePressed",
"mouseReleased", "mouseDragged", "mouseMoved"
};
private MyButton
b1 = new MyButton(Color.BLUE, "test1"),
b2 = new MyButton(Color.RED, "test2");
class MyButton extends JButton {
void report(String field, String msg) {
((JTextField)h.get(field)).setText(msg);
}
FocusListener fl = new FocusListener() {
public void focusGained(FocusEvent e) {
report("focusGained", e.paramString());
}
public void focusLost(FocusEvent e) {
report("focusLost", e.paramString());
}
};
KeyListener kl = new KeyListener() {
public void keyPressed(KeyEvent e) {
report("keyPressed", e.paramString());
}
public void keyReleased(KeyEvent e) {
report("keyReleased", e.paramString());
}
public void keyTyped(KeyEvent e) {
report("keyTyped", e.paramString());
}
};
MouseListener ml = new MouseListener() {
public void mouseClicked(MouseEvent e) {
report("mouseClicked", e.paramString());
}
public void mouseEntered(MouseEvent e) {
report("mouseEntered", e.paramString());
}
public void mouseExited(MouseEvent e) {
report("mouseExited", e.paramString());
}
public void mousePressed(MouseEvent e) {
report("mousePressed", e.paramString());
}
public void mouseReleased(MouseEvent e) {
report("mouseReleased", e.paramString());
}
};
MouseMotionListener mml = new MouseMotionListener() {
public void mouseDragged(MouseEvent e) {
report("mouseDragged", e.paramString());
}
public void mouseMoved(MouseEvent e) {
report("mouseMoved", e.paramString());
}
};
public MyButton(Color color, String label) {
super(label);
setBackground(color);
addFocusListener(fl);
addKeyListener(kl);
addMouseListener(ml);
addMouseMotionListener(mml);
}
}
public void init() {
Container c = getContentPane();
c.setLayout(new GridLayout(event.length + 1, 2));
for(int i = 0; i < event.length; i++) {
JTextField t = new JTextField();
t.setEditable(false);
c.add(new JLabel(event[i], JLabel.RIGHT));
c.add(t);
h.put(event[i], t);
}
c.add(b1);
c.add(b2);
}
public static void main(String[] args) {
Console.run(new TrackEvent(), 700, 500);
}
} ///:~In the MyButton constructor, the buttons color is set with a call to SetBackground( ). The listeners are all installed with simple method calls. Feedback
The TrackEvent class contains a HashMap to hold the strings representing the type of event and JTextFields where information about that event is held. Of course, these could have been created statically rather than putting them in a HashMap, but I think youll agree that its a lot easier to use and change. In particular, if you need to add or remove a new type of event in TrackEvent, you simply add or remove a string in the event arrayeverything else happens automatically. Feedback
When report( ) is called, it is given the name of the event and the parameter string from the event. It uses the HashMap h in the outer class to look up the actual JTextField associated with that event name and then places the parameter string into that field. Feedback
This example is fun to play with because you can really see whats going on with the events in your program. Feedback
Now that you understand layout managers and the event model, youre ready to see how Swing components can be used. This section is a non-exhaustive tour of the Swing components and features that youll probably use most of the time. Each example is intended to be reasonably small so that you can easily lift the code and use it in your own programs. Feedback
Keep in mind:
Swing includes a number of different types of buttons. All buttons, check boxes, radio buttons, and even menu items are inherited from AbstractButton (which, since menu items are included, would probably have been better named AbstractSelector or something equally general). Youll see the use of menu items shortly, but the following example shows the various types of buttons available: Feedback
//: c14:Buttons.java
// Various Swing buttons.
// <applet code=Buttons width=350 height=100></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.plaf.basic.*;
import javax.swing.border.*;
import com.bruceeckel.swing.*;
public class Buttons extends JApplet {
private JButton jb = new JButton("JButton");
private BasicArrowButton
up = new BasicArrowButton(BasicArrowButton.NORTH),
down = new BasicArrowButton(BasicArrowButton.SOUTH),
right = new BasicArrowButton(BasicArrowButton.EAST),
left = new BasicArrowButton(BasicArrowButton.WEST);
public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(jb);
cp.add(new JToggleButton("JToggleButton"));
cp.add(new JCheckBox("JCheckBox"));
cp.add(new JRadioButton("JRadioButton"));
JPanel jp = new JPanel();
jp.setBorder(new TitledBorder("Directions"));
jp.add(up);
jp.add(down);
jp.add(left);
jp.add(right);
cp.add(jp);
}
public static void main(String[] args) {
Console.run(new Buttons(), 350, 100);
}
} ///:~This begins with the BasicArrowButton from javax.swing.plaf.basic, then continues with the various specific types of buttons. When you run the example, youll see that the toggle button holds its last position, in or out. But the check boxes and radio buttons behave identically to each other, just clicking on or off (they are inherited from JToggleButton). Feedback
If you want radio buttons to behave in an exclusive or fashion, you must add them to a button group. But, as the following example demonstrates, any AbstractButton can be added to a ButtonGroup. Feedback
To avoid repeating a lot of code, this example uses reflection to generate the groups of different types of buttons. This is seen in makeBPanel( ), which creates a button group and a JPanel. The second argument to makeBPanel( ) is an array of String. For each String, a button of the class represented by the first argument is added to the JPanel:
//: c14:ButtonGroups.java
// Uses reflection to create groups
// of different types of AbstractButton.
// <applet code=ButtonGroups width=500 height=300></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;
import java.lang.reflect.*;
import com.bruceeckel.swing.*;
public class ButtonGroups extends JApplet {
private static String[] ids = {
"June", "Ward", "Beaver",
"Wally", "Eddie", "Lumpy",
};
static JPanel makeBPanel(Class klass, String[] ids) {
ButtonGroup bg = new ButtonGroup();
JPanel jp = new JPanel();
String title = klass.getName();
title = title.substring(title.lastIndexOf('.') + 1);
jp.setBorder(new TitledBorder(title));
for(int i = 0; i < ids.length; i++) {
AbstractButton ab = new JButton("failed");
try {
// Get the dynamic constructor method
// that takes a String argument:
Constructor ctor =
klass.getConstructor(new Class[]{String.class});
// Create a new object:
ab = (AbstractButton)
ctor.newInstance(new Object[] { ids[i] });
} catch(Exception ex) {
System.err.println("can't create " + klass);
}
bg.add(ab);
jp.add(ab);
}
return jp;
}
public void init() {
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(makeBPanel(JButton.class, ids));
cp.add(makeBPanel(JToggleButton.class, ids));
cp.add(makeBPanel(JCheckBox.class, ids));
cp.add(makeBPanel(JRadioButton.class, ids));
}
public static void main(String[] args) {
Console.run(new ButtonGroups(), 500, 300);
}
} ///:~The title for the border is taken from the name of the class, stripping off all the path information. The AbstractButton is initialized to a JButton that has the label Failed, so if you ignore the exception message, youll still see the problem on screen. The getConstructor( ) method produces a Constructor object that takes the array of arguments of the types in the Class array passed to getConstructor( ). Then all you do is call newInstance( ), passing it an array of Object containing your actual argumentsin this case, just the String from the ids array. Feedback
This adds a little complexity to what is a simple process. To get exclusive or behavior with buttons, you create a button group and add each button for which you want that behavior to the group. When you run the program, youll see that all the buttons e