Section 7.7
Frames and Dialogs
APPLETS ARE A FINE IDEA. It's nice to be able to put a complete program in a rectangle on a Web page. But more serious, large-scale programs have to run in their own windows, independently of a Web browser. In Java's Swing GUI library, an independent window is represented by an object of type JFrame. A stand-alone GUI application can open one or more JFrames to provide the user interface. It is even possible for an applet to open a frame. The frame will be a separate window from the Web browser window in which the applet is running. Any frame created by an applet includes a warning message such as "Warning: Insecure Applet Window." The warning is there so that you can always recognize windows created by applets. This is just one of the security restrictions on applets, which, after all, are programs that can be downloaded automatically from Web sites that you happen to stumble across without knowing anything about them.
Here is an applet that contains just one small button. When you click this "Launch ShapeDraw" button, a JFrame will be opened:
The frame that is created when you click the button is almost identical to the ShapeDrawWithMenus applet from Section 5, and it is used in the same way. However, you can change the size of the window, and you can make the window go away by clicking its close box.
The window in this example belongs to a class named ShapeDrawFrame, which is defined as a subclass of JFrame. The structure of a JFrame is almost identical to a JApplet, and the programming is almost the same. In fact, only a few changes had to be made to the applet class, ShapeDrawWithMenus, to convert it from a JApplet to a JFrame. First, a frame does not have an init() method, so the initialization for ShapeDrawFrame is done in a constructor instead of in init(). In fact, the only real change necessary to convert a typical applet class into a frame class is to convert the applet's init() method into a constructor, and to add a bit of frame-specific initialization to the constructor. Everything that you learned about creating and programming GUI components and adding them to a content pane applies to frames as well. Menus are also handled in exactly the same way. So, we really only need to look at the additional programming that is necessary for frames.
One significant difference is that the size and location of an applet are determined externally to the applet, by the HTML code for a Web page and by the browser that displays the page. The size of a frame, on the other hand, has to be set by the frame itself or by the program that creates the frame. Often, the size is set in the frame's constructor. (If you forget to do this, the frame will have size zero and all you will see on the screen is a tiny border.) There are two methods in the JFrame class that can be used to set the size of a frame:
void setSize(int width, int height); and void pack();Use setSize() if you know exactly what size the frame should be. The pack() method is more interesting. It should only be called after all GUI components have been added to the frame. Calling pack() will make the frame just big enough to hold all the components. It will determine the size of the frame by checking the preferred size of each of the components that it contains. (As mentioned in Section 3, standard GUI components come with a preferred size. When you create your own drawing surface or custom component, you can set its preferred size by calling its setPreferredSize() method or by definining a getPreferredSize() method to compute the size.)
You can also set the location of the frame. This can be done with by calling the JFrame method:
void setLocation(int x, int y);The parameters, x and y give the position of the upper left corner of the frame on the screen. They are given in terms of pixel coordinates on the screen as a whole, where the upper left corner of the screen is (0,0).
Creating a frame object does not automatically make a window appear on the screen. Initially, the window is invisible, You must make it visible by calling its method
void show();This can be called at the end of the frame's constructor, but it can also be reasonable to leave it out. In that case, the constructor will produce an invisible window, and the program that creates the frame is responsible for calling its show() method.
A frame has a title, a string that appears in the title bar at the top of the window. This title can be provided as an argument to the constructor or it can be set by calling the method:
void setTitle(String title);(In the ShapeDrawFrame class, I set the title of the frame to be "Shape Draw". I do this by calling a constructor in the superclass with the command:
super("Shape Draw");at the beginning of the ShapeDrawFrame constructor.)
Now you know how to get a frame onto the screen and how to give it a title. There is still the matter of getting rid of the frame. You can hide a frame by calling its hide() method. If you do this, you can make it visible again by calling show(). If you are completely finished with the window, you can call its dispose() method to close the window and free the system resources that it uses. After calling dispose(), you should not use the frame object again. You also have to be prepared for the fact that the user can click on the window's close box to indicate that it should be closed. By default, the system will hide the window when the user clicks its close box. However, you can tell the system to handle this event differently. Sometimes, it makes more sense to dispose of the window or even to call System.exit() and end the program entirely. It is even possible, as we will see below, to set up a listener to listen for the event, and to program any response you want. You can set the default response to a click in a frame's close box by calling:
void setDefaultCloseOperation(int operation);where the parameter, operation is one of the constants
- JFrame.HIDE_ON_CLOSE -- just hide the window, so it can be opened again
- JFrame.DISPOSE_ON_CLOSE -- destroy the window, but don't end the program
- JFrame.EXIT_ON_CLOSE -- terminate the Java interpretor by calling System.exit()
- JFrame.DO_NOTHING_ON_CLOSE -- no automatic response; your program is responsible for closing the window.
If a frame is opened by a main program, and if the program has only one window, it might make sense to use EXIT_ON_CLOSE. However, note that this option is illegal for a frame that is created by an applet, since an applet is not allowed to shut down the Java interpreter.
In case you are curious, here are the lines that I added to the end of the ShapeDrawFrame constructor:
setDefaultCloseOperation(EXIT_ON_CLOSE); setLocation(20,50); setSize(550,420); show();I also added a main() routine to the class. This means that it is possible to run ShapeDrawFrame as a stand-alone application. In a typical command-line environment, you would do this with the command:
java ShapeDrawFrameIt has been a while since we looked at a stand-alone program with a main() routine, and we have never seen a stand-alone GUI program. It's easy for a stand-alone program to use a graphical user interface -- all it has to do is open a frame. Since a ShapeDrawFrame makes itself visible when it is created, it is only necessary to create the frame object with the command "new ShapeDrawFrame();". The complete main routine in this case looks like:
public static void main(String[] args) { new ShapeDrawFrame(); }It might look a bit unusual to call a constructor without assigning the result to a variable, but it is perfectly legal and in this case we have no need to store the value. The main routine ends as soon as the frame is created, but the frame continues to exist and the program will continue running. Since the default close operation for the frame has been set to EXIT_ON_CLOSE, the frame will close and the program will be terminated when the user clicks the close box of the window. It might seem a bit odd to have this main() routine in the same class that defines ShapeDrawFrame, and in fact it could just as easily be in a separate class. But there is no real need to create an extra class, as long as you understand what is going on. When you type "java ShapeDrawFrame" on the command line, the system looks for a main routine in the ShapeDrawFrame class and executes it. If the main routine happens to create an object belonging to the same class, it's not a problem. It's just a command to be executed.
The source code for the frame class is in the file ShapeDrawFrame.java. The applet, shown above, which opens a frame of this type is in ShapeDrawLauncher.java.
We'll look at a few more fine points of programming with frames by looking at another example. In this case, it's a frame version of the HighLowGUI2 applet from Section 1. Click on this button to open the frame:
The frame in this example is defined in the file HighLowFrame.java. In many ways, this example is similar to the previous one, but there are several differences. You can resize the frame in the first example by dragging its border or corner, but if you try to resize the "High Low" frame, you will find that it is impossible. A frame is resizable by default. You can make it non-resizable by calling setResizable(false). I do this in the constructor of the HighLowFrame class. Another difference shows up if you click the frame's close box. This will not simply close the window. Instead a new window will open to ask you whether you really want to close the HighLow frame. The new window is an example of a "dialog box". You will learn about dialog boxes later in this section. To proceed, you have to click a button in the dialog box. If you click on "Yes", the HighLow frame will be closed; if not, the frame will remain open. (This would be more useful if, for example, you wanted to give the user a chance to save some unsaved work before closing the window.) To get this behavior, I had to turn off the system's default handling of the close box with the command:
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);and I had to program my own response instead. I did this by registering a window listener for the frame. When certain operations are performed on a window, the window generates an event of type WindowEvent. You can program a response to such events in the usual way: by registering a listening object of type WindowListener with the frame. The JFrame class has an addWindowListener()method for this purpose. The WindowListener must define seven event-handling methods, including the poorly named
public void windowClosing(WindowEvent evt) and public void windowClosed(WindowEvent evt)The windowClosing event is generated when the user clicks the close box of the window. The windowClosed event is generated when the window is actually being disposed. The other five methods in the WindowListener interface are more rarely used. Fortunately, you don't have to worry about them if you use the WindowAdapter class. The WindowAdapter class implements the WindowListener interface, but defines all the WindowListener methods to be empty. You can define a WindowListener by creating a subclass of WindowAdapter and providing definitions for just those methods that you actually need. In the HighLowFrame class, I need a listener to respond to the windowClosing event that is generated when the user clicks the close box. The listener is created in the constructor using an anonymous subclass of WindowAdapter with a command of the form:
addWindowListener( new WindowAdapter() { // This window listener responds when the user // clicks the window's close box by giving the // user a chance to change his mind. public void windowClosing(WindowEvent evt) { . . // Show the dialog box, and respond to it. . } });Another window listener is used in the little applet that appears on this page as the "Launch HighLow" button, above. This applet is defined in the file HighLowLauncher.java. Note that when you click on the button to open the frame, the name of the button changes to "Close HighLow". You can close the frame by clicking the button again, as well as by clicking the frame's close box. When the frame is closed, for either reason, the name of the button changes back to "Launch HighLow". The question is, how does the applet know to change the button's name, when the user closes the frame by clicking its close box? That doesn't seem to have anything to do with the applet. The trick is to use an event listener. When the applet creates the frame, it also creates a WindowListener and registers it with the frame. This WindowListener is programmed to respond to the windowClosed event by changing the name of the button. This is a nice example of the sort of communication between objects that can be done with events. You can check the source code to see exactly how it's done.
Images in Applications
In Section 1, we saw how to load an image file into an applet. The JApplet class has a method, getImage(), that can be used for this purpose. The JFrame class does not provide this method, so some other technique is needed for using images in frames.
The standard class java.awt.Toolkit makes it possible to load an image into a stand-alone application. A Toolkit object has a getImage() method for reading an Image from an image file. To use this method, you must first obtain a Toolkit, and you have to do this by calling the static method Toolkit.getDefaultToolkit(). (Any running GUI program already has a toolkit, which is used to perform various platform-dependent functions. You don't need to construct a new toolkit. Toolkit.getDefaultToolkit() returns a reference to the toolkit that already exists.) Once you have a toolkit, you can use its getImage() method to create an Image object from a file. This getImage method takes one parameter, which specifies the name of the file. For example:
Toolkit toolkit = getDefaultToolkit(); Image cards = toolkit.getImage( "smallcards.gif" );or, in one line,
Image cards = Toolkit.getDefaultToolkit().getImage( "smallcards.gif" );Once the Image has been created, it can be used in the same way, whether it has been created by an applet or a standalone application.
Note that an applet's getImage() method is used to load an image from a Web server, while a Toolkit's getImage() loads an image from the same computer on which the program is running. An applet cannot, in general, use a Toolkit to load an image, since, for security reasons, an applet is not usually allowed to read files from the computer on which it is running.
The HighLowFrame example uses an image file named "smallcards.gif" for the cards that it displays. I designed HighLowFrame with a main() routine so that it can be run as a stand-alone application. (When it is run as an application, the "smallcards.gif" file must be in the same directory with the class files.) But a HighLowFrame can also be opened by an applet, as is done in the example on this page. When it is run as an application, the image file must be loaded by a Toolkit. When it is opened by an applet, the image file must be loaded by the getImage() method of the applet. How can this be handled? I decided to do it by making the Image object a parameter to the HighLowFrame constructor. The Image must be loaded before the frame is constructed, so that it can be passed as a parameter to the constructor. The main() routine in HighLowFrame does this using a Toolkit:
Image cards = Toolkit.getDefaultToolkit().getImage("smallcards.gif"); HighLowFrame game = new HighLowFrame(cards);The HighLowLauncher applet, on the other hand, loads the image with its own getImage() method:
Image cards = getImage(getCodeBase(),"smallcards.gif"); highLow = new HighLowFrame(cards);
Dialogs
In addition to JFrame, there is another type of window in Swing. A dialog box is a type of window that is generally used for short, single purpose interactions with the user. For example, a dialog box can be used to display a message to the user, to ask the user a question, or to let the user select a color. In Swing, a dialog box is represented by an object belonging to the class JDialog.
Like a frame, a dialog box is a separate window. Unlike a frame, however, a dialog box is not completely independent. Every dialog box is associated with a frame (or another dialog box), which is called its parent. The dialog box is dependent on its parent. For example, if the parent is closed, the dialog box will also be closed. It is possible to create a dialog box without specifying a parent, but in that case a default frame is used or an invisible frame is created to serve as the parent.
Dialog boxes can be either modal or modeless. When a modal dialog is created, its parent frame is blocked. That is, the user will not be able to interact with the parent until the dialog box is closed. Modeless dialog boxes do not block their parents in the same way, so they seem a lot more like independent windows. In practice, modal dialog boxes are easier to use and are much more common than modeless dialogs. All the examples we will look at are modal.
Aside from having a parent, a JDialog can be created and used in the same way as a JFrame. However, we will not be using JDialog directly. Swing has many convenient methods for creating many common types of dialog boxes. For example, the JColorChooser class has the static method:
Color JColorChooser.showDialog(JFrame parent, Color initialColor)When you call this method, a dialog box appears that allows the user to select a color. The first parameter specifies the parent of the dialog, or it can be null. When the dialog first appears, initialColor is selected. The dialog has a sophisticated interface that allows the user to change the selection. When the user presses an "OK" button, the dialog box closes and the selected color is returned as the value of the method. The user can also click a "Cancel" button or close the dialog box in some other way; in that case, null is returned as the value of the method. By using this predefined color chooser dialog, you can write one line of code that will let the user select an arbitrary color.
The following applet demonstrates a JColorChooser dialog and three other, simpler standard dialog boxes. When you click one of the buttons, a dialog box appears. The label at the top of the applet gives you some feedback about what is happening:
The three simple dialogs in this applet are created by static methods in the class JOptionPane. This class includes many methods for making dialog boxes, but they are all variations on the three basic types shown here: a "message" dialog, a "confirm" dialog, and an "input" dialog. (The variations allow you to provide a title for the dialog box, to specify the icon that appears in the dialog, and to add other components to the dialog box. I will only cover the most basic forms here.)
A message dialog simply displays a message string to the user. The user (hopefully) reads the message and dismisses the dialog by clicking the "OK" button. A message dialog can be shown by calling the method:
void JOptionPane.showMessageDialog(JFrame parent, String message)The parent, as usual, can be null. The message can be more than one line long. Lines in the message should be separated by newline characters, \n. New lines will not be inserted automatically, even if the message is very long.
An input dialog displays a question or request and lets the user type in a string as a response. You can show an input dialog by calling:
String JOptionPane.showInputDialog(JFrame parent, String question)Again, the parent can be null, and the question can include newline characters. The dialog box will contain an input box and an "OK" button and a "Cancel" button. If the user clicks "Cancel", or closes the dialog box in some other way, then the return value of the method is null. If the user clicks "OK", then the return value is the string that was entered by the user. Note that the return value can be an empty string (which is not the same as a null value), if the user clicks "OK" without typing anything in the input box. If you want to use an input dialog to get a numerical value from the user, you will have to convert the return value into a number. A technique for doing this can be found in the first example in Section 4.
Finally, a confirm dialog presents a question and three response buttons: "Yes", "No", and "Cancel". A confirm dialog can be shown by calling:
int JOptionPane.showConfirmDialog(JFrame parent, String question)The return value tells you the user's response. It is one of the following constants:
- JOptionPane.YES_OPTION -- the user clicked the "Yes" button
- JOptionPane.NO_OPTION -- the user clicked the "No" button
- JOptionPane.CANCEL_OPTION -- the user clicked the "Cancel" button
- JOptionPane.CLOSE_OPTION -- the user closed the dialog in some other way.
I use a confirm dialog in the HighLowFrame example, earlier on this page. When the user clicks the close box of a HighLowFrame, I display a confirm dialog to ask whether the user really wants to quit. The frame will only be closed if the return value is JOptionPane.YES_OPTION.
By the way, it is possible to omit the Cancel button from a confirm dialog by calling one of the other methods in the JOptionPane class. Just call:
JOptionPane.showConfirmDialog( parent, question, title, JOptionPane.YES_NO_OPTION )The final parameter is a constant which specifies that only a "Yes" button and a "No" button should be used. The third parameter is a string that will be displayed as the title of the dialog box window.
If you would like to see how dialogs are created and used in the sample applet, you can find the source code in the file SimpleDialogDemo.java.
End of Chapter 7
[ Next Chapter | Previous Section | Chapter Index | Main Index ]