James Thornton logo
James Thornton
Google
Web jamesthornton.com
Internet Business Consultant Call Toll Free: 1 (800) 409-2501
About James My MySpace Internet Marketing Enron Loophole Lock Bumping Contact Me
JamesThornton.com -> Bruce Eckel Books -> TIJ-2nd-edition -> One Page

MindView Inc.
[ Viewing Hints ] [ Revision History ] [ Book Home Page ] [ Free Newsletter ]
[ Seminars ] [ Seminars on CD ROM ] [ Consulting ]

Thinking in Java, 2nd edition, Revision 12

©2000 by Bruce Eckel

[ Previous Chapter ] [ Short TOC ] [ Table of Contents ] [ Index ] [ Next Chapter ]

13: Creating Windows
& Applets

A fundamental design guideline is “make simple things easy, and difficult things possible.” [61]

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) produces a GUI that looks equally mediocre on all systems. In addition, it’s restrictive: you can use only four fonts and you cannot 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. [ Add Comment ]

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 finishes 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 isn’t good until revision 3) seems to hold true with programming languages as well. [ Add Comment ]

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. If for some reason you need to use the original “old” AWT (because you’re 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). [ Add Comment ]

Early in this chapter, you’ll see how things are different when you want to create an applet vs. 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 either applets or applications. [ Add Comment ]

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 you’re willing to do the research. [ Add Comment ]

I assume here that you have downloaded and installed the (free) Java 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 you’ll want to go to those if you need more depth, or if you want to modify the default Swing behavior. [ Add Comment ]

As you learn about Swing you’ll discover: Add Comment ]

  1. Swing is a much better programming model than you’ve probably seen in other languages and development environments. JavaBeans (which will be introduced toward the end of this chapter) is the framework for that library. [ Add Comment ]
  2. “GUI builders” (visual programming environments) are a de rigueur aspect of a complete Java development environment. JavaBeans and Swing allow the GUI builder to write code for you as you place components onto forms using graphical tools. This not only rapidly speeds development during GUI building, but it allows for greater experimentation and thus the ability to try out more designs and presumably come up with a better one. [ Add Comment ]
  3. The simplicity and well-designed nature of Swing means that even if you do use a GUI builder rather than coding by hand, the resulting code will still be comprehensible—this solves a big problem with GUI builders from the past, which could easily generate unreadable code. [ Add Comment ]

Swing contains all the components that you expect to see in a modern UI, everything from buttons that contain pictures to trees and tables. It’s a big library, but it’s designed to have appropriate complexity for the task at hand—if something is simple, you don’t 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 you’ve got the power if you need it. [ Add Comment ]

Much of what you’ll 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. [ Add Comment ]

For speed, all the components are “lightweight,” and Swing is written entirely in Java for portability. [ Add Comment ]

Keyboard navigation is automatic—you can run a Swing application without using the mouse, and this doesn’t 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. [ Add Comment ]

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. It’s even possible (albeit difficult) to invent your own look and feel. [ Add Comment ]

The basic applet

One of Java’s design goals is 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. [ Add Comment ]

Applet restrictions

Programming within an applet is so restrictive that it’s often referred to as being “inside the sandbox,” since you always have someone—that is, the Java run-time security system—watching over you. [ Add Comment ]

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. We’ve been writing regular applications all along in this book, but they’ve been console applications without any graphical components. Swing can also be used to build GUI interfaces for regular applications. [ Add Comment ]

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 you’ll notice are probably: Add Comment ]

  1. An applet can’t touch the local disk. This means writing or reading, since you wouldn’t want an applet to read and transmit private information over the Internet without your permission. Writing is prevented, of course, since that would be an open invitation to a virus. Java offers digital signing for applets. Many applet restrictions are relaxed when you choose to allow trusted applets (those signed by a trusted source) to have access to your machine. [ Add Comment ]
  2. Applets can take longer to display, since you must download the whole thing every time, including a separate server hit for each different class. Your browser can cache the applet, but there are no guarantees. Because of this, you should always package your applets in a JAR (Java ARchive) file that combines all the applet components (including other .class files as well as images and sounds) together into a single compressed file that can be downloaded in a single server transaction. “Digital signing” is available for each individual entry in the JAR file. [ Add Comment ]

Applet advantages

If you can live within the restrictions, applets have definite advantages, especially when building client/server or other networked applications: Add Comment ]

  1. There is no installation issue. An applet has true platform independence (including the ability to easily play audio files, etc.) so you don’t need to make any changes in your code for different platforms nor does anyone have to perform any “tweaking” on installation. In fact, installation is automatic every time the user loads the Web page that contains applets, so updates happen silently and automatically. In traditional client/server systems, building and installing a new version of the client software is often a nightmare. [ Add Comment ]
  2. You don’t have to worry about bad code causing damage to someone’s system, because of the security built into the core Java language and applet structure. This, along with the previous point, makes Java popular for so-called intranet client/server applications that live only within a company or restricted arena of operation where the user environment (Web browser and add-ins) can be specified and/or controlled. [ Add Comment ]

Because applets are automatically integrated with HTML, you have a built-in platform-independent documentation system to support the applet. It’s an interesting twist, since we’re used to having the documentation part of the program rather than vice versa. [ Add Comment ]

Application frameworks

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 framework’s 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[62]. [ Add Comment ]

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. You’ll 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:

//: c13: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( ). That’s all wired into the application framework; you put any startup code in init( ). [ Add Comment ]

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 above program this label is placed on the form. [ Add Comment ]

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 that’s the way it used to be in the old AWT. However, Swing requires you to add all components to the “content pane” of a form, and so you must call getContentPane( ) as part of the add( ) process. [ Add Comment ]

Running applets inside a Web browser

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[63] to tell the page how to load and run the applet. [ Add Comment ]

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, JavaSoft 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 browser’s extension mechanism. By using the extension mechanism (which a browser vendor cannot disable—in an attempt to gain competitive advantage—without breaking all the third-party extensions), JavaSoft guarantees that Java cannot be shut out of the Web browser by an antagonistic vendor. [ Add Comment ]

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. Here’s what the simplest resulting HTML page looks like for Applet1:[64]

//:! c13:Applet1.html
<html><head><title>Applet1</title></head><hr>
<OBJECT 
  classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
  width="100" height="50" align="baseline"  codebase="http://java.sun.com/products/plugin/1.2.2/jinstall-1_2_2-win.cab#Version=1,2,2,0">
<PARAM NAME="code" VALUE="Applet1.class">
<PARAM NAME="codebase" VALUE=".">
<PARAM NAME="type" VALUE="application/x-java-applet;version=1.2.2">
<COMMENT>
  <EMBED type=
    "application/x-java-applet;version=1.2.2" 
    width="200" height="200" align="baseline"
    code="Applet1.class" codebase="."
pluginspage="http://java.sun.com/products/plugin/1.2/plugin-install.html">
  <NOEMBED>
</COMMENT>
   No Java 2 support for APPLET!!
  </NOEMBED>
</EMBED>
</OBJECT>
<hr></body></html>
///:~

Some of these lines were too long and had to be wrapped to fit on the page. The code in this book’s source code (on the book’s CD ROM, and downloadable from www.BruceEckel.com) will work without having to worry about correcting line wraps. [ Add Comment ]

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. [ Add Comment ]

The source code package for this book provides an HTML page for each of the applets in this book, and thus many examples of the applet tag. You can find a full and current description of the details of placing applets in Web pages at java.sun.com. [ Add Comment ]

Using Appletviewer

Sun’s JDK (freely downloadable from java.sun.com) 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 don’t need to create tiny HTML files to run tests. For example, you can add the commented HTML tags to Applet1.java:

//: c13: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, you’ll see another coding approach which will allow you to execute applets from the command line without the Appletviewer. [ Add Comment ]

Testing applets

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 isn’t in the CLASSPATH then it will give an error message on the status line of the browser to the effect that it couldn’t find that .class file. [ Add Comment ]

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? [ Add Comment ]

Here’s 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 can’t find it. So when you’re 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. [ Add Comment ]

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. It took some time to discover that the package statement was the culprit. In general, you’ll want to leave the package statement out of an applet. [ Add Comment ]

Running applets from the command line

There are times when you’d like to make a windowed program do something else other than sit on a Web page. Perhaps you’d 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 we’ve made command-line applications, but in some operating environments (the Macintosh, for example) there isn’t a command line. So for any number of reasons you’d like to build a windowed, non-applet program using Java. This is certainly a reasonable desire. [ Add Comment ]

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[65] only if you can use the latest version of Java and associated tools so you can deliver applications that won’t confound your users. If for some reason you’re forced to use an older version of Java, think hard before committing to building a significant windowed application. [ Add Comment ]

Often you’ll want to be able to create a class that can be invoked as either a window or an applet. This is especially convenient when you’re testing the applets, since it’s 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. [ Add Comment ]

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.[66] As a simple example, let’s look at Applet1b.java modified to work as both an application and an applet:

//: c13:Applet1c.java
// An application and an applet.
// <applet code=Applet1c width=100 height=50>
// </applet>
import javax.swing.*;
import java.awt.*;
import com.bruceeckel.swing.*;

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:
    Console.setupClosing(frame);
    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. [ Add Comment ]

The line:

Console.setupClosing(frame);

Causes the window to be properly closed. Console comes from com.bruceeckel.swing and will be explained a little later. [ Add Comment ]

You can see that in main( ), the applet is explicitly initialized and started since in this case the browser isn’t available to do it for you. Of course, this doesn’t provide the full behavior of the browser, which also calls stop( ) and destroy( ), but for most situations it’s acceptable. If it’s a problem, you can force the calls yourself.[67]Add Comment ]

Notice the last line:

frame.setVisible(true);

Without this, you won’t see anything on the screen. [ Add Comment ]

A display framework

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 setupClosing(JFrame frame) {
    // The JDK 1.2 Solution as an 
    // anonymous inner class:
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    // The improved solution in JDK 1.3:
    // frame.setDefaultCloseOperation(
    //     EXIT_ON_CLOSE);
  }
  public static void 
  run(JFrame frame, int width, int height) {
    setupClosing(frame);
    frame.setSize(width, height);
    frame.setVisible(true);
  }
  public static void 
  run(JApplet applet, int width, int height) {
    JFrame frame = new JFrame(title(applet));
    setupClosing(frame);
    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));
    setupClosing(frame);
    frame.getContentPane().add(panel);
    frame.setSize(width, height);
    frame.setVisible(true);
  }
} ///:~

This is a tool you may want to use yourself, so it’s 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. [ Add Comment ]

setupClosing( ) is used to hide the code that causes a JFrame to exit a program when that JFrame is closed. The default behavior is to do nothing, so if you don’t call setupClosing( ) or write the equivalent code for your JFrame, the application won’t close. The reason this code is hidden rather than placing it directly in the subsequent run( ) methods is partly because it allows you to use the method by itself when what you want to do is more complicated than what run( ) provides. However, it also isolates a change factor: Java 2 has two ways of causing certain types of windows to close. In JDK 1.2, the solution is to create a new WindowAdapter class and implement windowClosing( ), as seen above (the meaning of this will be fully explained later in this chapter). However, during the creation of JDK 1.3 the library designers observed that you typically need to close windows whenever you’re creating a non-applet, and so they added the setDefaultCloseOperation( ) to JFrame and JDialog. From the standpoint of writing code, the new method is much nicer to use but this book was written while there was still no JDK 1.3 implemented on Linux and other platforms, so in the interest of cross-version portability the change was isolated inside setupClosing( ). [ Add Comment ]

The run( ) method is overloaded to work with JApplets, JPanels, and JFrames. Note that only if it’s a JApplet are init( ) and start( ) called. [ Add Comment ]

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. Here’s Applet1c.java modified to use Console:

//: c13: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. [ Add Comment ]

Using the Windows Explorer

If you’re using Windows, you can simplify the process of running a command-line Java program by configuring the Windows Explorer—the file browser in Windows, not the Internet Explorer—so that you can simply double-click on a .class file to execute it. There are several steps in this process. [ Add Comment ]

First, download and install the Perl programming language from www.Perl.org. You’ll find the instructions and language documentation on that site. [ Add Comment ]

Next, create the following script without the first and last lines (this script is part of this book’s source-code package):

//:! c13:RunJava.bat
@rem = '--*-Perl-*--
@echo off
perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
goto endofperl
@rem ';
#!perl
$file = $ARGV[0];
$file =~ s/(.*)\..*/\1/;
$file =~ s/(.*\\)*(.*)/$+/;
´java $file´;
__END__
:endofperl
///:~

Now, open the Windows Explorer, select “View,” “Folder Options,” then click on the “File Types” tab. Press the “New Type” button. For “Description of Type” enter “Java class file.” For “Associated Extension,” enter “class.” Under “Actions” press the “New” button. For “Action” enter “Open,” and for “Application used to perform action” enter a line like this:

"c:\aaa\Perl\RunJava.bat" "%L"

You must customize the path before “RunJava.bat” to conform to the location where you placed the batch file. [ Add Comment ]

Once you perform this installation, you may run any Java program by simply double-clicking on the .class file containing a main( ). [ Add Comment ]

Making a button

Making a button is quite simple: you just call the JButton constructor with the label you want on the button. You’ll see later that you can do fancier things, like putting graphic images on buttons. [ Add Comment ]

Usually you’ll want to create a field for the button inside your class so that you can refer to it later. [ Add Comment ]

The JButton is a component—its own little window—that will automatically get repainted as part of an update. This means that you don’t 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( ):

//: c13: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 {
  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 won’t 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. [ Add Comment ]

Capturing an event

You’ll notice that if you compile and run the applet above, 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. [ Add Comment ]

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 you’re not interested in, for example, whether the mouse is being moved over your button, you don’t register your interest in that event. It’s 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 haven’t seen before—in fact, this model extends to anything that can be classified as a JavaBean (which you’ll learn about later in the chapter). [ Add Comment ]

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 JButton’s 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). [ Add Comment ]

But what should the result of pressing that button be? We’d 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 modified 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:

//: c13: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 {
  JButton 
    b1 = new JButton("Button 1"), 
    b2 = new JButton("Button 2");
  JTextField txt = new JTextField(10);
  class BL implements ActionListener {
    public void actionPerformed(ActionEvent e){
      String name = 
        ((JButton)e.getSource()).getText();
      txt.setText(name);
    }
  }
  BL al = new BL();
  public void init() {
    b1.addActionListener(al);
    b2.addActionListener(al);
    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 above program is in the creation of the aforementioned ActionListener class BL. 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 that is a JButton. getText( ) returns the text that’s on the button, and this is placed in the JTextField to prove that the code was actually called when the button was pressed. [ Add Comment ]

In init( ), addActionListener( ) is used to register the BL object with both the buttons. [ Add Comment ]

It is often more convenient to code the ActionListener as an anonymous inner class, especially since you tend to only use a single instance of each listener class. Button2.java can be modified to use an anonymous inner class as follows: [ Add Comment ]

//: c13: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 {
  JButton 
    b1 = new JButton("Button 1"), 
    b2 = new JButton("Button 2");
  JTextField txt = new JTextField(10);
  ActionListener al = new ActionListener() {
    public void actionPerformed(ActionEvent e){
      String name = 
        ((JButton)e.getSource()).getText();
      txt.setText(name);
    }
  };
  public void init() {
    b1.addActionListener(al);
    b2.addActionListener(al);
    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. [ Add Comment ]

Text areas

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 9:

//: c13:TextArea.java
// Using the JTextArea control.
// <applet code=TextArea width=475 height=425>
// </applet>
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 JApplet {
  JButton 
    b = new JButton("Add Data"),
    c = new JButton("Clear Data");
  JTextArea t = new JTextArea(20, 40);
  Map m = new HashMap();
  public void init() {
    // Use up all the data:
    Collections2.fill(m, 
      Collections2.geography, 
      CountryCapitals.pairs.length);
    b.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e){
        for(Iterator it= m.entrySet().iterator();
            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);
  }
} ///:~

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, while the “Clear Data” button uses setText( ) to remove all the text from the JTextArea. [ Add Comment ]

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. That’s 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. [ Add Comment ]

Controlling layout

The way that you place components on a form in Java is probably different from any other GUI system you’ve used. First, it’s 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. [ Add Comment ]

JApplet, JFrame JWindow, and JDialog can all produce a Container with getContentPane( ) that can contain and display Components. In Container, there’s a method called setLayout( ) that allows you to choose a different layout manager. Other classes, such as JPanel, contain and display components directly and so you also set the layout manager directly, without using the content pane. [ Add Comment ]

In this section we’ll explore the various layout managers by placing buttons in them (since that’s the simplest thing to do). There won’t be any capturing of button events since these examples are just intended to show how the buttons are laid out. [ Add Comment ]

BorderLayout

The applet uses a default layout scheme: the BorderLayout (a number of the previous example 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. [ Add Comment ]

However, there’s 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 that’s 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:

BorderLayout. NORTH (top)
BorderLayout. SOUTH (bottom)
BorderLayout. EAST (right)
BorderLayout. WEST (left)
BorderLayout.CENTER (fill the middle, up to the other components or to the edges) [ Add Comment ]

If you don’t specify an area to place the object, it defaults to CENTER. [ Add Comment ]

Here’s a simple example. The default layout is used, since JApplet defaults to BorderLayout:

//: c13: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. [ Add Comment ]

FlowLayout

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. [ Add Comment ]

Here’s an example that sets the layout manager to FlowLayout and then places buttons on the form. You’ll notice that with FlowLayout the components take on their “natural” size. A JButton, for example, will be the size of its string. [ Add Comment ]

//: c13: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. [ Add Comment ]

GridLayout

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. [ Add Comment ]

//: c13: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. [ Add Comment ]

GridBagLayout

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, it’s also the most complicated layout manager, and quite difficult to understand. It is intended primarily for automatic code generation by a GUI builder (good GUI builders will 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, I’ll refer you to Core Java 2 by Horstmann & Cornell (Prentice-Hall, 1999), or a dedicated Swing book, as a starting point. [ Add Comment ]

Absolute positioning

It is also possible to set the absolute position of the graphical components in this way:

  1. Set a null layout manager for your Container: setLayout(null). [ Add Comment ]
  2. Call setBounds( ) or reshape( ) (depending on the language version) for each component, passing a bounding rectangle in pixel coordinates. You can do this in the constructor, or in paint( ), depending on what you want to achieve. [ Add Comment ]

Some GUI builders use this approach extensively, but this is usually not the best way to generate code. More useful GUI builders will use GridBagLayout instead. [ Add Comment ]

BoxLayout

Because people had so much trouble understanding and working with GridBagLayout, Swing also includes the 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 GridBagLayouts 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, let’s see how to use the BoxLayout directly, in the same way that the other layout managers have been demonstrated:

//: c13: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("" + i));
    JPanel jph = new JPanel();
    jph.setLayout(
      new BoxLayout(jph, BoxLayout.X_AXIS));
    for(int i = 0; i < 5; i++)
      jph.add(new JButton("" + 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 managers—you 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. [ Add Comment ]

To simplify matters, there’s 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:

//: c13: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("" + i));
    Box bh = Box.createHorizontalBox();
    for(int i = 0; i < 5; i++)
      bh.add(new JButton("" + 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. [ Add Comment ]

Struts add space between components, measured in pixels. To use a strut, you simply add it in between the addition of the components that you want spaced apart:

//: c13: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("" + i));
      bv.add(Box.createVerticalStrut(i*10));
    }
    Box bh = Box.createHorizontalBox();
    for(int i = 0; i < 5; i++) {
      bh.add(new JButton("" + 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 it’s 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). [ Add Comment ]

//: c13: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:

//: c13: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. [ Add Comment ]

The best approach?

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 what’s going on with layouts and how to deal with the events (described next), it’s 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). [ Add Comment ]

The Swing event model

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. [ Add Comment ]

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 that’s 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 you’ll discover your mistake at compile-time. You’ll see later in the chapter that JavaBeans also use the names of the “addListener” methods to determine what events a Bean can handle. [ Add Comment ]

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 because (as you shall see later) the fact that an inner class object keeps a reference to its parent object provides a nice way to call across class and subsystem boundaries. [ Add Comment ]

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. [ Add Comment ]

Event and listener types

All Swing components include addXXXListener( ) and removeXXXListener( ) methods so that the appropriate types of listeners can be added and removed from each component. You’ll 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.

Event, listener interface and add- and remove-methods

Components supporting this event

ActionEvent
ActionListener
addActionListener( )
removeActionListener( )

JButton, JList, JTextField, JMenuItem and its derivatives including JCheckBoxMenuItem, JMenu, and JpopupMenu.

AdjustmentEvent
AdjustmentListener
addAdjustmentListener( )
removeAdjustmentListener( )

JScrollbar
and anything you create that implements the Adjustable interface.

ComponentEvent
ComponentListener
addComponentListener( )
removeComponentListener( )

*Component and its derivatives, including JButton, JCanvas, JCheckBox, JComboBox, Container, JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, JFrame, JLabel, JList, JScrollbar, JTextArea, and JTextField.

ContainerEvent
ContainerListener
addContainerListener( )
removeContainerListener( )

Container and its derivatives, including JPanel, JApplet, JScrollPane, Window, JDialog, JFileDialog, and JFrame.

FocusEvent
FocusListener
addFocusListener( )
removeFocusListener( )

Component and derivatives*.

KeyEvent
KeyListener
addKeyListener( )
removeKeyListener( )

Component and derivatives*.

MouseEvent (for both clicks and motion)
MouseListener
addMouseListener( )
removeMouseListener( )

Component and derivatives*.

MouseEvent[68] (for both clicks and motion)
MouseMotionListener
addMouseMotionListener( )
removeMouseMotionListener( )

Component and derivatives*.

WindowEvent
WindowListener
addWindowListener( )
removeWindowListener( )

Window and its derivatives, including JDialog, JFileDialog, and JFrame.

ItemEvent
ItemListener
addItemListener( )
removeItemListener( )

JCheckBox, JCheckBoxMenuItem, JComboBox, JList, and anything that implements the ItemSelectable interface.

TextEvent
TextListener
addTextListener( )
removeTextListener( )

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 ShowMethodsClean.java program from Chapter 12 so that it displays all the event listeners supported by any Swing component that you enter.

Chapter 12 introduced reflection and used that feature to look up methods for a particular class—either the entire list of methods or a subset of those whose names match a keyword that you provide. The magic of this 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 you’re looking for, check the online documentation. [ Add Comment ]

However, by Chapter 12 you hadn’t 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:

//: c13: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.io.*;
import com.bruceeckel.swing.*;
import com.bruceeckel.util.*;

public class ShowAddListeners extends JApplet {
  Class cl;
  Method[] m;
  Constructor[] ctor;
  String[] n = new String[0];
  JTextField name = new JTextField(25);
  JTextArea results = new JTextArea(40, 65);
  class NameL implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String nm = name.getText().trim();
      if(nm.length() == 0) {
        results.setText("No match");
        n = new String[0];
        return;
      }
      try {
        cl = Class.forName("javax.swing." + nm);
      } catch(ClassNotFoundException ex) {
        results.setText("No match");
        return;
      }
      m = cl.getMethods();
      // Convert to an array of Strings:
      n = new String[m.length];
      for(int i = 0; i < m.length; i++)
        n[i] = m[i].toString();
      reDisplay();
    }
  } 
  void reDisplay() {
    // Create the result set:
    String[] rs = new String[n.length];
    int j = 0;
    for (int i = 0; i < n.length; i++)
      if(n[i].indexOf("add") != -1 &&
        n[i].indexOf("Listener") != -1)
          rs[j++] = 
            n[i].substring(n[i].indexOf("add"));
    results.setText("");
    for (int i = 0; i < j; i++)
      results.append(
        StripQualifiers.strip(rs[i]) + "\n");
  }
  public void init() {
    name.addActionListener(new NameL());
    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));
  }
  public static void main(String[] args) {
    Console.run(new ShowAddListeners(), 500,400);
  }
} ///:~

The StripQualifiers class defined in Chapter 12 is reused here by importing the com.bruceeckel.util library. [ Add Comment ]

The GUI contains a JTextField name in which you can enter the Swing class name you want to look up. The results are displayed in a JTextArea. [ Add Comment ]

You’ll notice that there are no buttons or other components by which to indicate that you want the search to begin. That’s because the JTextField is monitored by an ActionListener. Whenever you make a change and press ENTER, the list is immediately updated. If the text isn’t 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. Each of the objects in the array is turned into a String via toString( ) (this produces the complete method signature) and added to n, a String array. The array n is a member of class ShowAddListeners and is used in updating the display whenever reDisplay( ) is called. [ Add Comment ]

reDisplay( ) creates an array of String called rs (for “result set”). The result set is conditionally copied from the Strings in n that contain “add” and “Listener.” indexOf( ) and substring( ) are then used to remove the qualifiers like public, static, etc. Finally, StripQualifiers.strip( ) removes the extra name qualifiers. [ Add Comment ]

This program is a convenient way to investigate the capabilities of a Swing component. Once you know which events a particular component supports, you don’t need to look anything up to react to that event. You simply:

  1. Take the name of the event class and remove the word “Event.” Add the word “Listener” to what remains. This is the listener interface you must implement in your inner class. [ Add Comment ]
  2. Implement the interface above and write out the methods for the events you want to capture. For example, you might be looking for mouse movements, so you write code for the mouseMoved( ) method of the MouseMotionListener interface. (You must implement the other methods, of course, but there’s often a shortcut for that which you’ll see soon.) [ Add Comment ]
  3. Create an object of the listener class in Step 2. Register it with your component with the method produced by prefixing “add” to your listener name. For example, addMouseMotionListener( ). [ Add Comment ]
<