Chapter 6

Working with Frames, Dialogs, and Menus


CONTENTS

In the prior chapter you learned all about the individual components that make up a Java applet's user interface. In this chapter you see how to effectively combine these components and place them on panels, frames, and dialogs. Using a panel gives you more control over how a group of components is placed on the screen. Frames and dialogs enable you to display freestanding windows from within an applet. Finally, you learn all about Java menus and how to add a menu to a frame.

Containers

There are many occasions when it would be convenient to treat more than one component (for example, a label and a set of checkboxes) as a group. For example, instead of placing each component onto the user interface one at a time, it would be nice to be able to create a group of components and then place the group on the user interface. Not only would this be convenient, it is necessary due to some of the built-in limitations of some of Java's layout managers.

As an example, think about the BorderLayout layout manager that was introduced in Chapter 5 "Java's User Interface Components." This layout manager enables you to place a component in the North, South, East, West, or Center. If there are only five locations you can put a component, where do you put a sixth?

Fortunately, Java provides a solution in the form of its Container classes. A Container class can be used to hold components. And because Container is a subclass of Component, you can place a container on the user interface just like you'd place a check box, text field, or any other component.

Container serves as a superclass for a number of other classes, one of which is the familiar Applet class. Each of the other subclasses of Container will be described in the following sections.

The Panel Class

The Panel class is the simplest of the Java Container classes. When an applet creates a panel, no new window is created and the panel is unseen by the user. However, by using panels you can exert more explicit control over the placement of components in an applet's user interface. A new panel is created using a simple constructor, as follows:

Panel myPanel = new Panel();

After a panel is created, you can add components to it. You then add the panel to another container. Because Applet is a container, you can add the panel directly to the Applet. This is demonstrated in the following code:

public class PanelSample extends Applet
{
    public void init()
    {
        Panel myPanel = new Panel();
        myPanel.add(new Button());
        add(myPanel);
    }
}

After the new panel is created, a button is placed on the panel. Finally, add is used to place the panel on the applet's user interface.

Each panel uses its own layout manager that is independent of the container on which the panel is placed. By default, Panel uses the FlowLayout manager. Because a panel can have a different layout manager than its parent Container, this enables you to create user interfaces that are as complex and precise as you want.

A Panel Example

As an example of how you can use panels, class EX06A is shown in Listing 6.1. This class illustrates a couple of important concepts related to the use of panels, including the use of different layout managers.


Listing 6.1. EX06A.java.
import java.applet.*;
import java.awt.*;

public class EX06A extends Applet
{
    public void init()
    {
        resize(320, 240);

        setLayout(new GridLayout(2,2));

        Panel p1 = new Panel();
        p1.add(new Button("Panel 1"));
        add(p1);

        Panel p2 = new Panel();
        p2.add(new Button("Panel 2-1"));
        p2.add(new TextField(8));
        add(p2);

        Panel p3 = new Panel();
        p3.setLayout(new GridLayout(2,2));
        p3.add(new Button("Panel 3-1"));
        p3.add(new Button("Panel 3-2"));
        p3.add(new Button("Panel 3-3"));
        p3.add(new Button("Panel 3-4"));
        add(p3);

        add(new Button("No Panel"));
    }
}

When run, example EX06A creates the screen shown in Figure 6.1. To create this interface the applet uses setLayout(new GridLayout(2, 2)) to create a two-row by two-column layout. The top two cells in the grid and the bottom, left cell are each displaying a panel. These panels are displaying the components placed on them.

Figure 6.1 : Running EX06A creates three panels and a panelless area with a large button.

Panel p1 is created, and a new button is placed on it. The statement add(p1) is then used to place the panel onto the applet. Because no explicit layout manager was specified for panel p1, the default FlowLayout will be used. Next, a second panel, p2, is created. After adding a button and a TextField to this panel, it is added to the applet.

The third panel, p3, appears a little more complicated. However, this is only because it does not use the default layout manager. For p3 a new GridLayout object is created and four buttons are added to the panel. Then add(p3) is used to add this panel to the applet.

Finally, another button is created. This last button is added directly to the apple and is not first placed on a panel. Because the applet is using a GridLayout layout manager, the size of the button is increased so that it fills the entire grid cell, as shown in Figure 6.2.

Figure 6.2 : EX06B illustrates the placement of a panel on a panel.

You should take a moment to think about the different layout managers that are in use in EX06A. The applet itself is being laid out under the control of GridLayout. The first two cells in this grid are using FlowLayout, because it is the default for Panel. The third cell is again using GridLayout. This means the third cell is placed on the applet as part of a grid and that it will lay out its components as part of a grid within the grid. Finally, the fourth cell of the applet's GridLayout does not use a panel at all and its button is placed directly on the applet.

Placing a Panel on a Panel

What if instead of the screen shown in Figure 6.1, you wanted to create the screen shown in Figure 6.2? The only change here is in the top right cell. Here, a label is displayed in the top of the panel and a label and TextField are displayed in the bottom of the panel. How can you create this look?

It's actually fairly simple. The answer lies in the capability to put a panel on another panel, as shown in class EX06B in Listing 6.2.


Listing 6.2. EX06B.java.
import java.applet.*;
import java.awt.*;

public class EX06B extends Applet
{
    public void init()
    {
        resize(320, 240);

        setLayout(new GridLayout(2,2));

        Panel p1 = new Panel();
        p1.add(new Button("Panel 1"));
        add(p1);

        Panel p2 = new Panel();
        p2.setLayout(new BorderLayout());
        p2.add("North", new Label("This is a panel on a panel"));

        Panel subPanel = new Panel();
        subPanel.add(new Label("Name:"));
        subPanel.add(new TextField(8));
        p2.add("South", subPanel);
        add(p2);

        Panel p3 = new Panel();
        p3.setLayout(new GridLayout(2,2));
        p3.add(new Button("Panel 3-1"));
        p3.add(new Button("Panel 3-2"));
        p3.add(new Button("Panel 3-3"));
        p3.add(new Button("Panel 3-4"));
        add(p3);

        add(new Button("No Panel"));
    }
}

EX06B creates a new GridLayout instance and creates the first cell in the same way as was done in EX06A. The second panel, p2, is created and assigned a new instance of BorderLayout. A label, "This is a panel on a panel," is created and placed in the North of the panel.

Here is where things get interesting. A new panel, subPanel, is created. No layout manager is specified so subPanel will use the default FlowLayout. The Label and TextField are added to subPanel and then subPanel is added to the South of panel p2, creating the desired look.

Using Panels with CardLayout

In Chapter 5 you learned about the CardLayout layout manager. One shortcoming of this layout manager that was pointed out then is that it only allows you to place a single component on it. This sounds like the type of layout manager the federal government would buy: a layout manager so powerful it can lay out one component. Actually, CardLayout does serve a very useful purpose, but normally only when combined with panels.

The code for class EX06C, shown in Listing 6.3, uses CardLayout to enable the user to page between a sequence of four panels. If you recall example EX05I from Chapter 5, one of the problems with it was that it is difficult for the user to know which page he is currently on. Because CardLayout allows only one component to be added to each card, it was impossible to add both a label saying "You're on page one" and a button saying "Press here to go to page two." This problem is solved using panels in EX06C, as you can see in Figure 6.3.


Listing 6.3. EX06C.java.
import java.applet.*;
import java.awt.*;

public class EX06C extends Applet
{
    CardLayout layout;
    public void init()
    {
        resize(320, 240);
        layout = new CardLayout();
        setLayout(layout);

        Panel panel1 = new Panel();
        panel1.setLayout(new BorderLayout());
        panel1.add("North", new Label("This is page 1"));
        panel1.add("Center", new Button("Go to Page 2"));
        panel1.add("South", new Label("This entire card is a panel."));
        add("Page1", panel1);

        Panel panel2 = new Panel();
        panel2.setLayout(new BorderLayout());
        panel2.add("North", new Label("This is page 2"));
        panel2.add("Center", new Button("Go to Page 3"));
        add("Page2", panel2);

        Panel panel3 = new Panel();
        panel3.setLayout(new BorderLayout());
        panel3.add("North", new Label("This is page 3"));
        panel3.add("Center", new Button("Go to Page 4"));
        add("Page3", panel3);

        Panel panel4 = new Panel();
        panel4.setLayout(new BorderLayout());
        panel4.add("North", new Label("This is page 4"));
        panel4.add("Center", new Button("Go to Page 1"));
        add("Page4", panel4);
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result=false;

        if("Go to Page 1".equals(obj)) {
            layout.show(this, "Page1");
            result = true;
        }
        else if("Go to Page 2".equals(obj)) {
            layout.show(this, "Page2");
            result = true;
        }
        else if("Go to Page 3".equals(obj)) {
            layout.show(this, "Page3");
            result = true;
        }
        else if("Go to Page 4".equals(obj)) {
            layout.show(this, "Page4");
            result = true;
        }
        return result;
    }
}

Figure 6.3 : EX06C shows how to combine panels with CardLayout.

In EX06C, a new instance of CardLayout is created and assigned to the applet using setLayout. Next the first panel, panel1, is created and is assigned to use BorderLayout. Two labels and a button are then added to panel1, and panel1 is added to the applet under the name "Page1" with add("Page1", panel1).

Three other panels are created in a similar manner, and the applet's action method is written to catch the button presses and to display the appropriate card.

The Window Class

Java's Window class is also a subclass of Container; however, you will probably never use the Window class directly. Instead, when you want to add a free-standing window to your applet, use either Frame or Dialog. These classes extend Window, and each adds unique functionality.

Frames

The Frame class extends Window and provides a class that can be used whenever you want to create a free-standing window that is not part of the browser in which the applet is being run. Figure 6.4, for example, illustrates a simple frame that has been moved outside the browser's borders.

Figure 6.4 : A frame exists outside the browser window.

Creating a frame is as simple as using one of the constructors shown in Table 6.1. If you use the Frame() constructor, a default title of "Untitled" will be used. So, unless the frame will be used to display untitled books, you probably want to use the Frame(String) constructor. As examples of constructing new frames, consider the following:

Frame untitledFrame = new Frame();
Frame noTitleFrame = new Frame("");
Frame titledFrame = new Frame("Hi Mom, Send Cash");

Table 6.1. Constructors for the Frame class.

ConstructorPurpose
Frame()Creates a frame with no title.
Frame(String)Creates a frame with the specified title.

Once a frame is constructed you can treat it like any other container. By default, a frame uses the BorderLayout layout manager. You can specify a different layout manager and can add components, including Panels, using add. Of course Frame offers its own features beyond those available to other containers. A frame can have a menu, can use a variety of different cursors, and can have an icon placed on its title bar. In addition to the menu that can be added to the frame, each frame has a control menu located in the top left of the frame, as shown in Figure 6.5.

Figure 6.5 : A frame with its control menu dropped down.

To support these features, the Frame class includes the nonprivate member methods shown in Table 6.2.

Table 6.2. Nonprivate methods of the Frame class.

MethodPurpose
addNotify()Creates a peer for the component.
dispose()Disposes of resources (for example, menu bars) in use by the frame.
getCursorType()Returns the cursor type that is displayed when the mouse pointer is over the frame.
getIconImage()Returns the image used as the frame's icon.
getMenuBar()Gets the frame's menubar.
getTitle()Returns the frame's title.
isResizable()Returns true if the frame is resizable.
paramString()Returns the frame's parameter string.
remove(MenuComponent)Removes the specified item from the frame's menu.
setCursor(int)Sets the cursor that will be displayed when the mouse pointer is over the frame.
setIconImage(Image)Sets the image to use as the frame's icon.
setMenuBar(MenuBar)Sets the frame's menubar.
setResizable(boolean)Makes the frame resizable or not, depending on the specified parameter.
setTitle(String)Sets the dialog's title.

When using a frame, you should take the following steps:

  1. Create the frame using new.
  2. Place components (buttons, text fields, and so on) on the frame.
  3. Use resize to set the frame to the correct dimensions.
  4. When done with the frame, call dispose to release resources.

A Frame Example

Listing 6.4 creates the sample frame that was shown in Figure 6.4. The EX06D class is derived from Applet. In the init method, an instance of a new class called MyFrame is allocated but not displayed. A button labeled Show Frame is then added to the applet, and the action method looks for a push of this button. If the button push is detected, frame.show is used to display the instance.


Listing 6.4. EX06D.java.
import java.applet.*;
import java.awt.*;

public class EX06D extends Applet
{
    MyFrame frame;

    public void init()
    {
        frame = new MyFrame();
        add(new Button("Show Frame"));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result = false;

        if ("Show Frame".equals(obj)) {
            frame.show();
            result = true;
        }
        return result;
    }
}

class MyFrame extends Frame {
    public MyFrame() 
    {
        add("Center", new Label("This is a frame"));

        Panel p = new Panel();
        p.add(new Button("Close"));
        add("South", p);

        resize(250, 250);
    }

    public boolean action(Event evt, Object arg) 
    {
        boolean result = false;

        if("Close".equals(evt.arg)) {
            dispose();
            result = true;
        }
        return result;
    }     
}

The MyFrame class is defined as a subclass of Frame. The constructor for MyFrame places a label on the frame and then constructs a new panel. A Close button is then constructed and placed on the panel, and the panel is added to the frame. The frame is then resized. Don't forget to resize the frame, or it will be made only large enough to display the title bar. The action method of MyFrame watches for the Close button and then calls dispose to tell the Java Virtual Machine that the applet is done with this frame.

Controlling a Frame at Runtime

As Table 6.2 shows, there are a number of ways you can control the appearance of a frame at runtime. EX06E, shown in Listing 6.5, illustrates some of these.


Listing 6.5. EX06E.java.
import java.applet.*;
import java.awt.*;

public class EX06E extends Applet
{
    MyFrame frame;

    public void init()
    {
        frame = new MyFrame();
        add(new Button("Show Frame"));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result = false;

        if ("Show Frame".equals(obj)) {
            frame.show();
            result = true;
        }
        return result;
    }
}

class MyFrame extends Frame {
    TextField title;
    Choice cursor;

    public MyFrame() 
    {
        setLayout(new GridLayout(3,1));

        // create a panel and controls for
        // setting the frame's title
        Panel panel1 = new Panel();
        panel1.add(new Label("Title:"));
        title = new TextField(10);
        panel1.add(title);
        add(panel1);

        // create a panel and controls for
        // setting the frame's cursor
        Panel panel2 = new Panel();
        panel2.add(new Label("Cursor:"));
        cursor = new Choice();
        cursor.addItem("Default");
        cursor.addItem("Crosshair");
        cursor.addItem("Hand");
        cursor.addItem("Move");
        cursor.addItem("Text");
        cursor.addItem("Wait");
        panel2.add(cursor);
        add(panel2);

        // create a panel with two buttons
        Panel panel3 = new Panel();
        panel3.add(new Button("Apply"));
        panel3.add(new Button("Close"));
        add(panel3);

        resize(250, 250);
    }

    public boolean action(Event evt, Object arg) 
    {
        boolean result = false;

        if("Close".equals(evt.arg)) {
            dispose();
            result = true;
        }
        else if("Apply".equals(evt.arg)) {
            setTitle(title.getText());

            int Cursors[]={DEFAULT_CURSOR, CROSSHAIR_CURSOR, 
                HAND_CURSOR, MOVE_CURSOR, TEXT_CURSOR, 
                WAIT_CURSOR };

            setCursor(Cursors[cursor.getSelectedIndex()]);
        }
        return result;
    }     
}

In this example, a frame is created that can be customized by the user. The user can enter a string into a text field, select a cursor type from a choice, and then select the Apply button to see the results of his selections. This can be seen in Figure 6.6.

Figure 6.6 : Controlling the appearance of a frame at runtime.

To achieve this, the MyFrame constructor in EX06E constructs a text field and a choice, and adds them to panels to create a pleasant user interface. The action method of MyFrame then traps for a press on the Apply button. When that is detected, setTitle(title.getText) takes the string entered by the user and makes it the title of the frame. Similarly, a call to setCursor causes the frame to use the cursor selected by the user in the cursor Choice member.

Dialogs

The Java Dialog class is similar to the Frame class. Just like a frame, a dialog can hold components and accept user input. A dialog can be constructed using either of the constructors shown in Table 6.3.

Table 6.3. Constructors for the Dialog class.

ConstructorPurpose
Dialog(Frame, boolean)Creates a dialog with the specified parent frame and modality.
Dialog(Frame, String, boolean) Creates a dialog with the specified parent frame, title String, and modality.

Unfortunately, you cannot just construct a dialog and toss it directly onto the screen as you can with a frame. Every dialog must have a frame as its parent. Because an applet is not a frame, you cannot create a dialog with an applet as its parent. This isn't much of an inconvenience, however, because it is simple to create a frame, and you don't really need to do anything with the frame other than use it as the dialog's parent in the dialog constructor. For example, you can create and use a dummy frame as follows:

Frame dummyFrame = new Frame();
dummyFrame.resize(250, 250);
Dialog d = new Dialog(dummyFrame, false); 

A key difference between a frame and a dialog is found in the final parameter to each of the Dialog constructors. This boolean parameter specifies the modality of the dialog. A dialog can be either modal or modeless. A modal dialog forces users to respond to it before they can continue working with other parts of the applet. Because of this, a modal dialog is perfect for displaying error messages or for gathering user input that is necessary before continuing further. A modeless dialog does not require that the user close the dialog before continuing.

Another difference between a frame and a dialog is that a menu can only be attached directly to a frame. Later in this chapter you learn how to attach a menu to a frame.

Because a dialog is closely related to a frame, a dialog's set of nonprivate methods should be familiar. These are shown in Table 6.4.

Table 6.4. Nonprivate methods of the Dialog class.

MethodPurpose
addNotify()Creates a peer for the component.
getTitle()Returns the dialog's title.
isModal()Returns true if the dialog is modal.
isResizable()Returns true if the dialog is resizable.
paramString()Returns the dialog's parameter string.
setResizable(boolean)Makes the dialog resizable or not, depending on the specified parameter.
setTitle(String)Sets the dialog's title.

A Dialog Example

Class EX06F is an example of how to use a dialog in an applet. It is similar to EX06E in that the applet's main screen includes a button that activates the main part of the applet. In the prior example, a frame was displayed. In EX06F, pressing the Show Dialog button on the applet's main screen will create and display the dialog shown in Figure 6.7.

Figure 6.7 : An initially unresizable dialog.

The dialog, as shown in Figure 6.7, includes a label telling us that it cannot be resized, a Toggle button, and a Close button. Selecting the Toggle button enables resizing for the dialog and changes the label at the top of the dialog. Once the dialog is resizable, it can be stretched along any of its sides as shown in Figure 6.8.

Figure 6.8 : The same dialog after the Toggle button was pushed and the dialog was resized.

The code that creates this dialog is shown in Listing 6.6. This class includes two classes: the applet class (EX06F) and MyDialog, which extends Dialog. In the EX06F class member dlg of MyDialog is stored. In the init method for this class, you can see that a frame is constructed, resized, and then passed to the constructor for MyDialog. Notice that although the frame is constructed, it is never displayed. A button labeled Show Dialog is created. The action method looks for a push of this button and then calls dlg.show to display the dialog.


Listing 6.6. EX06F.java.
import java.applet.*;
import java.awt.*;

public class EX06F extends Applet
{
    MyDialog dlg;

    public void init()
    {
        Frame f = new Frame();
        f.resize(250, 250);
        dlg = new MyDialog(f, "Howdy");
        add(new Button("Show Dialog"));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result = false;

        if ("Show Dialog".equals(obj)) {
            dlg.show();
            result = true;
        }
        return result;
    }
}

class MyDialog extends Dialog {
    Label resizeLabel;
    String labelText [] = {
        "Dialog is NOT resizable",
        "Dialog is resizable"
    };

    public MyDialog(Frame parent, String title) 
    {
        super(parent, title, false);
        setResizable(false);

        resizeLabel = new Label(labelText[0]);
        add("North", resizeLabel);

        Panel p = new Panel();
        p.add(new Button("Toggle"));
        p.add(new Button("Close"));
        add("South", p);

        pack();
        resize(250, 250);
    }

    public boolean action(Event evt, Object arg) 
    {
        boolean result = false;

        if("Toggle".equals(evt.arg)) {
            if (isResizable())
                resizeLabel.setText(labelText[0]);
            else
                resizeLabel.setText(labelText[1]);

            setResizable(!isResizable());
            result = true;
        }
        else if("Close".equals(evt.arg)) {
            dispose();
            result = true;
        }
        return result;
    }
}

The MyDialog class declares a label, resizeLabel, and an array of strings that will be displayed in the label so the user knows whether or not the dialog is currently resizable. The constructor for MyDialog uses super to construct the actual dialog. This is passed the undisplayed parent frame, the title of the dialog, and false to indicate that the dialog will be modeless.

Next, setResizable(false) is used to prevent the user from resizing the dialog initially. The label indicating this is constructed and placed on the dialog. The user interface is completed by placing the Toggle and Close buttons on a panel, which is then placed on the dialog.

The action method looks for a push of either the Toggle or the Close button. If Close is pushed, dispose is used to release the dialog. If Toggle is pushed, isResizable determines the current state of the dialog and the text of the label is changed appropriately. The code setResizable(!isResizable()) is used to toggle the resizable state of the dialog.

FileDialog

The final subclass of Container is the FileDialog class. This class can be used for creating File Save or File Open dialogs, as shown in Figure 6.9. This class is not available when programming applets. Because applets cannot access files, there is no need to open or close them. However, to complete this chapter's coverage of the Java Container classes, a brief overview of FileDialog is presented.

Figure 6.9 : The FileDialog in save mode under Windows 95.

An instance of FileDialog can be constructed using either of two constructors, as shown in the following three examples:

FileDialog dlg1 = new FileDialog(frame, "Save");
FileDialog dlg2 = new FileDialog(frame, "Save", FileDialog.SAVE);
FileDialog dlg3 = new FileDialog(frame, "Open", FileDialog.OPEN);

Each FileDialog is created in either open or save mode, depending on whether it will be used to open an existing file or write to a file. These modes are identified by FileDialog.SAVE and FileDialog.OPEN. By default, a new instance of FileDialog is created in save mode. Therefore, dlg1 and dlg2 will be in save mode but dlg3 will be in open mode.

Because FileDialog is a subclass of Dialog, it is necessary for each FileDialog to have a parent frame. The parent frame is passed as the first parameter to either FileDialog constructor. As with other dialogs, it is not necessary for the frame to be displayed.

Once constructed, a FileDialog is displayed with the show method. Usually, the entire purpose of displaying a FileDialog is to get a filename from the user. Naturally, there are member methods in FileDialog that enable you to retrieve this information after the user has entered it. The getDirectory method can be used to retrieve the directory name selected by the user. Similarly, getFile will return the filename. Combined, they give you the fully qualified filename.

As an example, the following code creates a FileDialog in save mode, displays it, and then prints the full path and filename entered by the user:

Frame f = new Frame();
f.resize(250, 250);

FileDialog d = new FileDialog(f, "Save a File", FileDialog.SAVE);
d.show();

System.out.println("Picked: " + d.getDirectory() + d.getFile());

Menus

The final subject in this chapter's discussion on user interface programming is menus. The hierarchy of Java classes involved in creating menus is shown in Figure 6.10. The MenuComponent class serves as an abstract base class for MenuBar and MenuItem; as such, you will never construct an actual instance of MenuComponent.

Figure 6.10 : The Java Menu classes.

Menus in Java are created by combining three items: a menubar, one or more menus, and one or more menu items on each of the menus. Each of these items is labeled in Figure 6.11, which is a sample menu that will be built in the next section.

Figure 6.11 : The items which comprise a Java menu.

The MenuBar Class

The MenuBar class represents the top-most level of a menu. After creating a menubar, you can create Menu objects and assign the Menu objects to the menubar. A menubar is created as follows:

MenuBar bar = new MenuBar();

In addition to its constructor, the MenuBar class includes the methods listed in Table 6.5.

Table 6.5. Nonprivate methods of the MenuBar class.

MethodPurpose
add(Menu)Adds a menu to this menubar.
addNotify()Creates a peer for the object.
countMenus()Returns the number of menus on this menubar.
getHelpMenu()Returns the menu that has been identified as the Help menu for this menubar.
getMenu(int)Returns the menu at the specified index.
remove(int)Removes the menu at the specified index.
remove(MenuComponent)Removes the specified menu component from this menubar.
removeNotify()Removes the object's peer.
setHelpMenu(Menu)Indicates the Help menu for this menubar.

In Java, menus are added to a class that implements the MenuContainer interface. In most cases this will be a class you define and base on Frame because Frame is the only Java container to implement MenuContainer. The following code adds a menubar to a frame:

MenuBar bar = new MenuBar();
// add menus to the MenuBar
myFrame.setMenuBar(bar);

The Menu Class

A menubar without any menus is as worthless as a pizza without any pepperoni. This can be easily rectified by creating Menu objects. To create a new menu, simply provide the name of the menu to the constructor, as follows:

Menu catMenu = new Menu("Cats");
Menu dogMenu = new Menu("Dogs");

After creating a menu, you must add it to the menubar. Menus will be displayed across the menubar in the order they are added to it. For example, the following code will create a Cats menu followed by a Dogs menu:

Menu catMenu = new Menu("Cats");
menuBar.add(catMenu);
Menu dogMenu = new Menu("Dogs");
menuBar.add(dogMenu);

A second Menu constructor is provided that is passed a string and a Boolean value indicating whether the menu is a tear-off menu. In addition to these constructors, the Menu class includes the methods listed in Table 6.6.

Table 6.6. Nonprivate methods of the Menu class.

MethodPurpose
add(MenuItem)Adds the specified menu item to the menu.
add(String)Adds an item with the specified label to the menu.
addNotify()Creates a peer for the object.
addSeparator()Adds a separator line to the menu.
countItems()Returns the number of items in the menu.
getItem(int)Returns the menu item at the specified index.
isTearOff()Returns true if the menu can be torn off.
remove(int)Removes the menu item at the specified index.
remove(MenuComponent)Removes the specified menu component from the menu .
removeNotify()Removes the object's peer.

The MenuItem Class

The menu still isn't useful, however, because no menu items have been added. A new menu item can be created by passing a string to the menu item constructor. For example, the following code illustrates all that is needed to create a menubar, a Dogs menu on the menubar, and three items on the Dogs menu:

MenuBar menuBar = new MenuBar();
Menu dogMenu = new Menu("Dogs");
dogMenu.add(new MenuItem("Labrador"));
dogMenu.add(new MenuItem("Poodle"));
dogMenu.add(new MenuItem("Spaniel"));
menuBar.add(dogMenu);
myFrame.setMenuBar(menuBar);

By default, MenuItems are enabled. This means that the user can select them from the menu on which they appear. It is possible to disable a MenuItem. This can be done using either the disable method or by passing false to the enable method. A MenuItem can be enabled by passing true to enable or by using enable without any parameters. These methods are illustrated in the following:

MenuItem item = new MenuItem("Big Dog");
item.disable();        // disable the MenuItem
item.enable();         // enable the MenuItem
item.enable(false);    // disable the MenuItem
item.enable(true);     // enable the MenuItem

The enable and disable methods are not the only ones available for a MenuItem. Table 6.7 describes each of the nonprivate members of MenuItem.

Table 6.7. Nonprivate methods of the MenuItem class.

MethodPurpose
addNotify()Creates a peer for the object.
disable()Disables selection of this menu item.
enable()Enables selection of this menu item.
enable(boolean)Enables or disables selection of the menu item based on the specified Boolean.
getLabel()Returns the label for this menu item.
IsEnabled()Returns true if the menu item is selectable.
paramString()Returns a parameter string for this menu item.
setLabel(String)Sets the label of the menu item to the specified string.

The CheckboxMenuItem Class

The CheckboxMenuItem class is a subclass of MenuItem that can be used to display a check mark next to the item when desired. A CheckboxMenuItem is used almost identically to a regular MenuItem. It provides the additional getState and setState(boolean) methods, but is constructed and added to a menu as though it were a MenuItem. For example, the following code will create a new CheckboxMenuItem with the menu text "Checkbox", set it to its checked state, and then add it to the menu:

CheckboxMenuItem checkbox = new CheckboxMenuItem("Checkbox");
checkbox.setState(true);
menu.add(checkbox);

In addition to its constructor, the CheckboxMenuItem class includes the methods listed in Table 6.8.

Table 6.8. Nonprivate methods of the CheckboxMenuItem class.

MethodPurpose
addNotify()Creates a peer for the object.
getState()Returns true if the CheckboxMenuItem is checked or false otherwise.
paramString()Returns a parameter String for this menu item.
SetState(boolean)Checks or unchecks the CheckboxMenuItem depending on the specified Boolean.

An Example of Adding a Menu to a Frame

In this example you will create a frame and attach a menubar to it. The menubar includes menus for Dogs, Cats, and Food. These menus are created by EX06G, which is shown in Listing 6.7. The Applet class in this example simply displays a button and waits for the user to push the button before displaying the frame with the menu on it.


Listing 6.7 EX06G.java.
import java.applet.*;
import java.awt.*;

public class EX06G extends Applet
{
    MyFrame frame;

    public void init()
    {
        frame = new MyFrame();
        add(new Button("Show Frame"));
    }

    public boolean action(Event evt, Object obj)
    {
        boolean result = false;

        if ("Show Frame".equals(obj)) {
            frame.show();
            result = true;
        }
        return result;
    }
}

class MyFrame extends Frame {
    TextArea info;

    public MyFrame() 
    {
        MenuBar menuBar = new MenuBar();

        // create a menu of dog breeds
        Menu dogMenu = new Menu("Dogs");
        dogMenu.add(new MenuItem("Labrador"));
        dogMenu.add(new MenuItem("Poodle"));
        dogMenu.add(new MenuItem("Spaniel"));
        // add the menu to the menu bar
        menuBar.add(dogMenu);

        // create a menu of cat breeds
        Menu catMenu = new Menu("Cats");
        catMenu.add(new MenuItem("Persian"));
        catMenu.add(new MenuItem("Maine Coon"));
        // separate house cats from wild cats
        catMenu.addSeparator();
        catMenu.add(new MenuItem("Cougar"));
        catMenu.add(new MenuItem("Leopard"));
        // add the menu to the menu bar
        menuBar.add(catMenu);

        // create a menu of pet foods
        Menu foodMenu = new Menu("Food");
        // create a CheckboxMenuItem for dog food
        CheckboxMenuItem dogFood = new CheckboxMenuItem("Dog Chow");
        dogFood.setState(true);
        foodMenu.add(dogFood);        
        // there are two types of cat food so 
        // create a sub menu
        Menu catFoodMenu = new Menu("Cat Food");
        catFoodMenu.add(new MenuItem("Tuna"));
        catFoodMenu.add(new MenuItem("Cat Chow"));
        // add the sub menu to the menu
        foodMenu.add(catFoodMenu);
        // add the menu to the menu bar
        menuBar.add(foodMenu);

        // add a Close button on a panel
        Panel p = new Panel();
        p.add(new Button("Close"));
        add("South", p);

        Panel p2 = new Panel();
        info = new TextArea(3, 20);
        p2.add(info);
        add("Center", p2);

        // set the frame's menu bar to the new menu
        setMenuBar(menuBar);

        resize(250, 250);
    }

    public boolean action(Event evt, Object arg) 
    {
        boolean result = false;

        if("Close".equals(evt.arg)) {
            dispose();
            result = true;
        }
        else if ("Persian".equals(evt.arg)) {
            info.setText("A big furry cat\r\n");
            info.appendText("that I'm allergic to.");            
            result = true;
        }
        else if ("Maine Coon".equals(evt.arg)) {
            info.setText("A really ferocious cat that\r\n");
            info.appendText("my Grandmother thinks is\r\n");
            info.appendText("a house cat.");            
            result = true;
        }
        return result;
    }     
}

The class MyFrame is where all the action is. In the constructor for this class, you can see that first a menubar, menuBar, is constructed. Next, dogMenu is constructed and three MenuItems are added to dogMenu before dogMenu is added to the menubar.

The second menu, catMenu, is created in a similar manner except that the addSeparator method is used to create a separating line between the first two domestic cats and the later two wild cats. Figure 6.11 shows the effect of adding the separator.

A third menu, foodMenu, is constructed. This menu is more involved than the first two menus because it includes both a CheckboxMenuItem and a submenu. This can be seen in Figure 6.12. After the CheckboxMenuItem is constructed and added to foodMenu, a new menu, catFoodMenu, is constructed. The Tuna and Cat Chow items are added to this menu. Then foodMenu.add(catFoodMenu) is used to add the catFoodMenu submenu to the Food menu. This is possible because Menu is a subclass of MenuItem.

Figure 6.12 : The Food menu has a CheckboxMenuItem and a submenu.

After the menus are created, a Close button and a text area are placed on panels on the frame. The text area will be used to display information about items from the Cat menu if they are selected by the user. Finally, the menubar is attached to the frame with setMenuBar(menuBar).

Of course, just putting a menu on a frame doesn't do anything unless you also trap the events generated by a user selecting the menu items. In this example, this is done in the action method. The only events handled are when the user selects Persian or Maine Coon from the Cats menu. Whenever one of these items is selected, an appropriate informational message about the selected breed of cat is written to the text area on the frame. This can be seen in Figure 6.13.

Figure 6.13 : Selecting Maine Coon from the Cats menu displays information about this breed.

Summary

This chapter presented you with a lot of information about Java's Container classes Panel, Frame, Dialog, and FileDialog. You learned how to create and place panels on other containers in order to group components. You saw how panels can be combined with the CardLayout class to create a dynamic user interface. You learned how to use the Frame and Dialog classes to create windows that are not part of the browser's window. Additionally, you saw how to control a frame's cursor. Finally, you learned about Java's menu classes: MenuBar, Menu, MenuItem, and CheckboxMenuItem. You learned how to combine these to create menus and submenus that can be attached to a frame.

In the next chapter you will see how to simplify some of work involved in these tasks by using the Resource Wizard of Visual J++.