MindView Inc.

Thinking in Java, 3rd ed. Revision 4.0


[ Viewing Hints ] [ Book Home Page ] [ Free Newsletter ]
[ Seminars ] [ Seminars on CD ROM ] [ Consulting ]

Previous Next Title Page Index Contents

14: Creating Windows
& Applets

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 couldn’t 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 isn’t 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 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) Note that some AWT components remain in Java, and in some situations you must use them. Feedback

Early in this chapter, you’ll 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 you’re 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 you’ll 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, you’ll discover: Feedback

  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. Feedback
  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. Feedback
  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. 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. 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. Feedback

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

The basic applet

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

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. 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. We’ve been writing regular applications all along in this book, but they’ve 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 you’ll notice are probably: Feedback

  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 signed applets (those signed by a trusted source) to have access to your machine. You’ll see an example later in this chapter, as well as an example of Java Web Start, a way to safely send applications to a client over the Internet. Feedback
  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. Feedback

Applet advantages

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

  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 installation “tweaking.” In fact, installation is automatic every time the user loads a 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. Feedback
  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 useful 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. Feedback

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. Feedback

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.[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. 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:

//: 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( ). That’s 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 that’s 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

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[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 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), 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. Here’s 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 book’s 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 chapter’s source code. You can find a full and current description of the details of placing applets in Web pages at java.sun.com. Feedback

Using Appletviewer

Sun’s 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 don’t 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, you’ll see another coding approach that will allow you to execute applets from the command line without the Appletviewer. Feedback

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. 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

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. 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. It’s 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 it’s generally best to use packages in production code. Feedback

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. 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 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. Feedback

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. 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, let’s 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 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.[80] Feedback

Notice the last line:

frame.setVisible(true);


Without this, you won’t see anything on the screen. Feedback

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
  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 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. Feedback

setDefaultCloseOperation( ) 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 setDefaultCloseOperation( ) or write the equivalent code for your JFrame, the application won’t close. Feedback

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. 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. Here’s 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

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. Feedback

Usually, you’ll want to create a field for the button inside your class so that you can refer to it later. Feedback

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( ):

//: 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 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. Feedback

Capturing an event

You’ll 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 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 (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 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). Feedback

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, 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 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. 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

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 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. 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. Feedback

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. Feedback

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, so you also set the layout manager directly, without using the content pane. Feedback

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 because these examples are just intended to show how the buttons are laid out. Feedback

BorderLayout

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, 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

If you don’t specify an area to place the object, it defaults to CENTER. Feedback

Here’s 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

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. Feedback

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. 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

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. 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

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 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, I’ll refer you to Core Java 2, Volume 1, by Horstmann & Cornell (Prentice Hall, 2001), or a dedicated Swing book as a starting point. Feedback

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). Feedback
  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. Feedback

Some GUI builders use this approach extensively, but this is usually not the best way to generate code. Feedback

BoxLayout

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, let’s 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 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. Feedback

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:

//: 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 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). 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

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). Feedback

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. 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 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. 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

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. Feedback

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, 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[81] (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 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 class—either 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 you’re looking for, check the JDK documentation. Feedback

However, by Chapter 10 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:

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

You’ll notice that there are no buttons or other components 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 field 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. 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 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. Feedback
  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 this, which you’ll see soon.) Feedback
  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( ). Feedback

Here are some of the listener interfaces:

Listener interface
w/ adapter

Methods in interface

ActionListener

actionPerformed(ActionEvent)

AdjustmentListener

adjustmentValueChanged(
AdjustmentEvent)

ComponentListener
ComponentAdapter

componentHidden(ComponentEvent)
componentShown(ComponentEvent)
componentMoved(ComponentEvent)
componentResized(ComponentEvent)

ContainerListener
ContainerAdapter

componentAdded(ContainerEvent)
componentRemoved(ContainerEvent)

FocusListener
FocusAdapter

focusGained(FocusEvent)
focusLost(FocusEvent)

KeyListener
KeyAdapter

keyPressed(KeyEvent)
keyReleased(KeyEvent)
keyTyped(KeyEvent)

MouseListener
MouseAdapter

mouseClicked(MouseEvent)
mouseEntered(MouseEvent)
mouseExited(MouseEvent)
mousePressed(MouseEvent)
mouseReleased(MouseEvent)

MouseMotionListener
MouseMotionAdapter

mouseDragged(MouseEvent)
mouseMoved(MouseEvent)

WindowListener
WindowAdapter

windowOpened(WindowEvent)
windowClosing(WindowEvent)
windowClosed(WindowEvent)
windowActivated(WindowEvent)
windowDeactivated(WindowEvent)
windowIconified(WindowEvent)
windowDeiconified(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, you’ll 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

Using listener adapters for simplicity

In the table above, you can see that some listener interfaces have only one method. These are trivial to implement, because you’ll 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 isn’t 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 don’t 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 you’ll 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 doesn’t work, but it will drive you crazy trying to figure out why, since everything will compile and run fine—except that your method won’t be called for a mouse click. Can you see the problem? It’s 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 that’s called when the window is closing, so you don’t get the desired results. Despite the inconvenience, an interface will guarantee that the methods are properly implemented. Feedback

Tracking multiple events

To prove to yourself that these events are in fact being fired, and as an interesting experiment, it’s 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 that’s 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 what’s 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 button’s 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 you’ll agree that it’s 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 array—everything 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 what’s going on with the events in your program. Feedback

A catalog of Swing components

Now that you understand layout managers and the event model, you’re ready to see how Swing components can be used. This section is a non-exhaustive tour of the Swing components and features that you’ll 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:

  1. You can easily see what each of these examples looks like while running by viewing the HTML pages in the downloadable source code for this chapter (www.BruceEckel.com). Feedback
  2. The JDK documentation from java.sun.com contains all of the Swing classes and methods (only a few are shown here). Feedback
  3. Because of the naming convention used for Swing events, it’s fairly easy to guess how to write and install a handler for a particular type of event. Use the lookup program ShowAddListeners.java from earlier in this chapter to aid in your investigation of a particular component. Feedback
  4. When things start to get complicated you should graduate to a GUI builder. Feedback

Buttons

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). You’ll 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, you’ll 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

Button groups

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