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, a program can open an independent window by creating an object of type Frame. A Frame has a title, displayed in the title bar at the top of the window. It can have a menu bar containing one or more pull-down menus. A Frame is a Container, which means that it can hold other GUI components. The default layout manager for a Frame is a BorderLayout. A common way to design a frame is to add a single GUI component, such as a Panel or Canvas, in the layout's Center position so that it will fill the entire frame.
It is possible for an applet to create 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 displays just one small button on the page. When you click the "Launch ShapeDraw" button, a window will be opened. This is another version of the ShapeDraw program that you've seen several times already. This version has a pull-down menu named "Add Shape", which contains commands that you can use to add shapes to the window. (Note for Macintosh users: The menus for the frame might just be added to the list of menus at the top of the screen. Look there for the "Add Shape" menu if you don't see it at the top of the window.) Once a shape is in the window, you can drag it around. The color that will be used for newly created shapes is controlled by another pull-down menu named "Color". There is also a pop-up menu that appears when you click on a shape in the appropriate, platform-dependent way.
The window in this example belongs to a class named ShapeDrawFrame, which is defined as a subclass of Frame. The applet creates the window with the statement
shapeDraw = new ShapeDrawFrame();
The constructor sets up the structure of the window and makes the window appear on the screen. Once the window has been created, it runs independently of the applet and of any other windows. Of course, there can be some communication through shared variables, method calls, and events. In the sample applet above, for example, the name of the button changes from "Launch ShapeDraw" to "Close ShapeDraw" while the window is open. If the user clicks on the "Close ShapeDraw" button, the applet responds by closing the window. This is done by calling an appropriate method in the window object. On the other hand, if the user closes the window by clicking on its close box, an event is generated belonging to the class WindowEvent. The applet listens for such events so that it can change the name of the button back to "Launch ShapeDraw" when the window is closed. (You might be interested to see how all this is handled in the source code for the applet. You can find it in the file ShapeDrawLauncher.java.)
Fortunately, you don't have to learn a lot of new material to use frames. There are a few specialized methods in the Frame class that you should know about. You'll want to know how to use pull-down menus and menu bars. And there are a few important events that are generated by windows. Aside from that, programming with frames is the same as programming with applets. Many of the new features show up in the constructor for the ShapeDrawFrame class, so let's take a look at that:
public ShapeDrawFrame() { // Constructor. Create and open the window. The window // has a menu bar containing two menus, "Add Shape" and // "Color". The content of the window is a canvas belonging // to the class ShapeCanvas. setBackground(Color.white); /* Set window title by calling the superclass constructor. */ super("Shape Draw"); /* Create a canvas that fills the frame. */ canvas = new ShapeCanvas(); add(canvas, BorderLayout.CENTER); /* Create pull-down menus and a menu bar. The frame listens for ActionEvents from the menus. These are generated when the user selects commands from the menus. */ Menu colorMenu = new Menu("Color", true); // Color choice menu. colorMenu.add("Red"); colorMenu.add("Green"); colorMenu.add("Blue"); colorMenu.add("Cyan"); colorMenu.add("Magenta"); colorMenu.add("Yellow"); colorMenu.add("Black"); colorMenu.add("White"); colorMenu.addActionListener(this); Menu shapeMenu = new Menu("Add Shape", true); // Shape menu. shapeMenu.add("Rectangle"); shapeMenu.add("Oval"); shapeMenu.add("Round Rect"); shapeMenu.addActionListener(this); MenuBar mbar = new MenuBar(); // Create menu bar and add the menus. mbar.add(shapeMenu); mbar.add(colorMenu); setMenuBar(mbar); // Add the menu bar to the frame. /* Do the remaining setup and show the window. */ setBounds(30,50,380,280); // Set size and position of window. setResizable(false); // Make the window non-resizable. addWindowListener( new WindowAdapter() { // A listener that will close the window // when the user clicks its close box. public void windowClosing(WindowEvent evt) { ShapeDrawFrame.this.dispose(); } } ); // end addWindowListener statement show(); // Make the window visible. } // end constructorThis constructor begins by calling a constructor from the superclass, Frame. The Frame constructor specifies a title for the window, which is displayed in the window's title bar. A Frame object contains an instance method, setTitle(String), that can be used to set the title of the frame at any time. There is also a getTitle() method that returns the current title.
The frame's "Color" menu is created with the command "Menu colorMenu = new Menu("Color", true);". The second parameter of the Menu constructor specifies that this should be a tear-off menu, which can be dragged by the user away from the menu bar, where it becomes a little independent window. (This might not work on all platforms.) This second parameter is optional. The add() method is used to add commands to the menu. The version of the add() command that is used here takes a String, which will appear as one of the commands in the menu. You can be more sophisticated than this if you want. There is another version of the add command that adds one menu as a submenu or hierarchical menu in another menu. You can add a separator line to a menu with the addSeparator() method. You can also create an object belonging to the class MenuItem and add it to a menu. A MenuItem represents a command, and the text of that command appears in the menu. But you can also associate a keyboard shortcut key with the menu item (like Control-V for pasting in Windows or Command-V for pasting on a Macintosh). Furthermore, a MenuItem can be enabled and disabled. And there are even CheckboxMenuItems representing menu items that can be checked and unchecked by the user (generally to represent options that can be turned on and off). You can look up all the details in a Java reference if you are interested.
For a menu to be useful, you must register an ActionListener with it. Whenever the user selects an item from the menu, an ActionEvent is generated and the actionPerformed() method of the action listener is called. The action command that is associated with the ActionEvent is the text of the menu item. (It is also possible to listen for action events from individual MenuItem objects, but it is more common simply to use one listener for the menu as a whole.) In the above constructor, the frame itself is registered as the listener for the menus. There is an actionPerformed() method elsewhere in the ShapeDrawFrame class that handles menu commands.
A pull-down menu can only appear in the menu bar of a frame. A menu bar is an object of type MenuBar. Menus are added to the menu bar using the add() method of the MenuBar object. The menu bar itself must be associated with the frame by calling the frame's setMenuBar() command. Note that a frame can have only one menu bar, but that you can change to a different menu bar at any time by calling setMenuBar().
In this example, I call setBounds(30,50,380,280) to set the position and size of the frame. The upper left corner of the frame will be at the point (30,50) on the computer screen, where (0,0) is the upper left corner of the screen. The width of the frame will be 380 and the height will be 280. By calling setResizable(false), I make it impossible for the user to change the size of the frame. By default, the frame would be resizable. In most cases, it's better to allow the user to change the size of the frame, but that means you have to write a program that can deal with components that change size.
(Using the setBounds() command is also not particularly good style. Better style would be to use the pack() command, which tells the frame to set itself to its so-called preferred size. A frame computes its preferred size by taking into consideration the preferred sizes and the layout of all the components that it contains. However, I failed to define a getPreferredSize() method in the ShapeCanvas class, so the canvas component in the frame doesn't know how big it wants to be. In this case, setBounds() works well enough. But if you ever start writing "serious" GUI components that will see a lot of reuse by yourself or other programmers, you have to start worrying about the fine points of style, like getPreferredSize(). The MessageDialog example that is discussed at the end of this page uses both pack() and getPreferredSize(). Look at the source code if you'd like to see how they work.)
The next statement in the constructor sets up a listener to listen for WindowEvents from the frame:
addWindowListener( new WindowAdapter() { // A listener that will close the window // when the user clicks its close box public void windowClosing(WindowEvent evt) { ShapeDrawFrame.this.dispose(); } } ); // end addWindowListener statementThis requires some explanation. To understand it all, you need to have absorbed the material about anonymous nested classes and adapter classes from the previous section. Frames generate events belonging to the class WindowEvent. There are seven types of WindowEvent, and the associated WindowListener interface declares seven methods. Here, I only want to define a response to one type of window event, so I base my listener object on the WindowAdapter class. The listener object belongs to an anonymous subclass of WindowAdapter. The windowClosing() method is called when the user clicks on the close box of the frame. The system does not automatically close the window when the user does this! It just generates a windowClosing event. The event handler for that event is responsible for closing the window. (It can even decide not to close it. For example, the event handler method might ask the user, "Do you really want to close this window?" and give the user a chance to change his mind.) A frame is closed by calling its dispose() method. The statement ShapeDrawFrame.this.dispose() in the event handler refers to the dispose() method in the ShapeDrawFrame class.
The final line in the constructor for ShapeDrawFrame is "show();". This is extremely important. When a window is first created, it is hidden. The show() command makes the window appear on the screen.
By the way, the ShapeDrawFrame class also includes the following main() routine:
public static void main(String[] args) { new ShapeDrawFrame(); }This means that ShapeDrawFrame can be executed as an independent program. When it is executed, the main() routine simply opens a window of type ShapeDrawFrame. After opening the window, the main() routine ends, but the window stays open until the user closes it. (If the frame is being run as an independent program, it would be a good idea to call System.exit(0) when the user closes the window. This statement will end the program completely, not just dispose of the window. This can be done in the windowClosing method.)
A problem sometimes arises when using Insets with frames. To leave a border between the edges of the frame and the components that it contains, you have to define a getInsets() method in the frame class. However, on some platforms, a frame already uses insets to leave space for the menu bar. Your getInsets() has to allow for this space. The way to do this is to call super.getInsets() to find out how much space the frame is already allowing for insets. Then add on the extra space that you want to allow for the border. The following getInsets() method adds an extra 2 pixels to the insets along each edge of the frame:
public Insets getInsets() { Insets currentInsets = super.getInsets(); Insets myInsets = new Insets(); myInsets.top = currentInsets.top + 2; myInsets.bottom = currentInsets.bottom + 2; myInsets.left = currentInsets.left + 2; myInsets.right = currentInsets.right + 2; return myInsets; }(When you call super.getInsets(), you are not supposed to modify the object that it returns. You should construct a new object and return that one, as is done in this example.)
Dialogs
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, 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.
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 or even bring the parent to the front of the screen 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 Java, a dialog box is an object belonging to the class Dialog or to one of its subclasses. A Dialog cannot have a menu bar, but it can contain other GUI components, and it generates the same WindowEvents as a Frame. Dialog objects can be either modal or modeless. A parameter to the constructor specifies which it should be.
Click on this button to run a little demonstration of modal dialogs:
The dialogs in this example belong to a class named MessageDialog, which is defined in the file MessageDialog.java. You might find this class useful for creating simple dialogs for your own programs. The source code for the applet and frame classes used in this example is in the file DialogDemoLauncher.java
[ Next Section | Previous Section | Chapter Index | Main Index ]