Up until the previous chapter, when you've added controls to your applets, you've let Java place those controls wherever it felt like it. The only way you could control positioning was by changing the size of the applet. Obviously, if you're going to produce attractive applets that are organized logically, you need some way to tell Java exactly where you want things placed. Java's layout managers were created for exactly this purpose. Working in conjunction with layout managers are components called panels, which enable you to organize other applet components. In this chapter, you learn about these two important layout components.
A panel is a special type of container object that acts as a parent to other components that you want to organize in your applet. For example, you can add several panels to an applet, each with their own layout. By using panels in this way, you can create many different creative displays. Creating a panel is as easy as calling the Panel class's constructor, like this:
Panel panel = new Panel();
As you can see, the Panel class's constructor requires no arguments.
Once you create a panel, you add it to the applet in the normal way, by calling the add() method:
add(panel);
Using panels can be a little confusing at first, so an example is in order. Suppose you need to create an applet that displays four buttons, but you don't want Java to place the buttons one after the other in the display, which Java will do with its default layout. Instead, you want the buttons displayed in two rows of two. One way to accomplish this feat is to add two panels to the applet and then add two buttons to each panel. Listing 22.1 shows how this is done, whereas Figure 22.1 shows what the display looks like.
Figure 22.1 : Using panels,
Listing 22.1 PanelApplet.java: Using Panels.
import java.awt.*; import java.applet.*; public class PanelApplet extends Applet { Panel panel1, panel2; Button button1, button2, button3, button4; public void init() { panel1 = new Panel(); panel2 = new Panel(); add(panel1); add(panel2); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); button4 = new Button("Button4"); panel1.add(button1); panel1.add(button2); panel2.add(button3); panel2.add(button4); } }
Tell Java that the applet uses the classes in the awt package.
Tell Java that the applet uses the classes in the applet package.
Derive the PanelApplet class from Java's Applet class.
Declare the panel and button objects.
Override the init() method.
Create the panels.
Add the panels to the applet.
Create the four buttons.
Add the buttons to the panels.
Notice how, when adding the panels to the applet, the program calls the PanelApplet class's add() method (which adds the panels to the applet's display). However, when adding the buttons, the program calls the panel objects' add() method (which adds the buttons to the panels). This is how you build a hierarchy of components into your applets. In this case, you've got a stack of components three high, with the applet's display on the bottom, the two panels on top of that, and the four buttons on top of the panels. As you create more sophisticated applets, this type of component stacking will be more common.
Panels are kind of a "plain vanilla" container for organizing components in an applet. As you'll discover in the next section, you can combine panels with layout managers to create truly complex displays.
Layout Managers are special objects that determine how elements of your applet are organized in the applet's display. When you create an applet, Java automatically creates and assigns a default layout manager. In many of the applets you've created so far in this book, it's the default layout manager that's determined where your controls appear. You can, however, create different types of layout managers in order to better control how your applets look. The layout managers you can use are listed below:
Each of these layout managers is represented by a class of the same name. To create a layout manager for your applet, you first create an instance of the appropriate layout class and then call the setLayout() method to tell Java which layout object you want to use. In the following sections, you get a chance to see the various layout managers in action.
In the previous section, I mentioned that, when you create an applet, Java assigns to it a default layout manager. It just so happens that this default manager is an object of the FlowLayout class. The FlowLayout manager places controls, in the order in which they're added, one after the other in horizontal rows. When the layout manager reaches the right border of the applet, it begins placing controls on the next row. In its default state, the FlowLayout manager centers controls on each row. However, you can set the alignment when you create the layout manager for your applet, like this:
FlowLayout layout = new FlowLayout(align, hor, ver); SetLayout(layout);
The FlowLayout constructor takes three arguments, which are the alignment (FlowLayout.LEFT, FlowLayout.CENTER, or FlowLayout.RIGHT), the horizontal spacing between components, and the vertical spacing.
Suppose that you want to arrange three buttons in an applet using a FlowLayout manager set to left alignment. Listing 22.2 shows how you'd create the manager and the buttons for the applet. Figure 22.2 shows the resultant control layout. Figures 22.3, and 22.4 show the center and right alignments for the same controls.
Figure 22.2 : These buttons are left aligned by the FlowLayout manager.
Figure 22.3 : These buttons are center aligned by the FlowLayout manager.
Figure 22.4 : These buttons are right aligned by the FlowLayout manager.
Listing 22.2 LST22_2.TXT: Creating a FlowLayout Manager.
FlowLayout layout = new FlowLayout(FlowLayout.LEFT, 10, 10); setLayout(layout); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); add(button1); add(button2); add(button3);
NOTE |
The FlowLayout() constructor shown in this chapter takes four arguments. However, you can actually construct a FlowLayout object with no arguments, FlowLayout(), or with a single argument for the alignment, FlowLayout(FlowLayout.LEFT). Many of Java's classes have multiple constructors. |
Once you start creating more sophisticated applets, you'll quickly discover that the FlowLayout manager may not give you the control you need to create the kind of display you want for your applet. When you need more control over the placement of components, you can try out the GridLayout manager.
Java's GridLayout manager organizes your applet's display into a rectangular grid, similar to the grid used in a spreadsheet. Java then places the components you create for the applet into each cell of the grid, working from left to right and top to bottom. You create a GridLayout manager like this:
GridLayout layout = new GridLayout(rows, cols, hor, ver); SetLayout(layout);
The constructor's four arguments are the number of rows in the grid, the number of columns, and the horizontal and vertical space between the grid cells.
To test the GridLayout manager, suppose you want to place four buttons into a 2x2 grid, with no space between the buttons. Listing 22.3 shows how you'd create the manager and the buttons for the applet. Figure 22.5 shows the resultant control layout. Figure 22.6 shows the same layout manager, except created with horizontal and vertical spacing of 10, and Figure 22.7 shows the layout with a single row of four cells.
Figure 22.5 : This GridLayout manager is set to two rows and two columns.
Figure 22.6 : This is the same GridLayout manager with horizontal and vertical spacing.
Figure 22.7 : This GridLayout manager has one row and four columns.
Listing 22.3 LST22_3.TXT: Creating a GridLayout Manager.
GridLayout layout = new GridLayout(2, 2, 0, 0); setLayout(layout); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); button4 = new Button("Button4"); add(button1); add(button2); add(button3); add(button4);
You'll probably use the GridLayout manager most of the time, but there may be cases where you need to put together something a little more unusual. One layout you can try is provided by the BorderLayout manager, which enables you to position components using the directions north, south, east, west, and center. You create a BorderLayout manager object like this:
BorderLayout layout = new BorderLayout(hor, ver); setLayout(layout);
This constructor's two arguments are the horizontal and vertical spacing between the cells in the layout.
After you create the BorderLayout object, you must add the components using a different version of the add() method:
add(position, object);
Here, position is where to place the component and must be the string North, South, East, West, or Center. The second argument, object, is the component you want to add to the applet.
Suppose you have five buttons that you want to place in the five areas supported by a BorderLayout manager. First, you create and set the manager. Then, you create the five buttons and add them to the applet, using the special version of add() that includes the object's position as the first argument. Listing 22.4 shows how this is done. Figure 22.8 shows the resultant display, whereas Figure 22.9 shows the same applet with the BorderLayout manager with horizontal and vertical spacing.
Figure 22.9 : This is the same applet with horizontal and vertical spacing.
Figure 22.8 : This applet displays five buttons using a BorderLayout manager.
Listing 22.4 LST22_4.TXT: Creating a BorderLayout Manager.
BorderLayout layout = new BorderLayout(0, 0); setLayout(layout); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); button4 = new Button("Button4"); button5 = new Button("Button5"); add("North", button1); add("South", button2); add("East", button3); add("West", button4); add("Center", button5);
One of the most complex layout managers is CardLayout. Using this manager, you can create a stack of layouts not unlike a stack of cards and then flip from one layout to another. This type of display organization is not unlike Windows 95's tabbed dialogs, usually called property sheets. To create a layout with the CardLayout manager, you first create a parent panel to hold the "cards." Then, you create the CardLayout object and set it as the panel's layout manager. Finally, you add each "card" to the layout by creating the components and adding them to the panel.
To create a CardLayout manager, call its constructor and then add it to the applet, like this:
CardLayout cardLayout = new CardLayout(hor, ver); panel.setLayout(cardLayout);
The constructor's two arguments are the horizontal and vertical spacing.
Because the CardLayout manager enables you to switch
between a stack of layouts, you need some way to tell the manager
what to do. For this reason, the CardLayout manager has
a number of public methods that you can call to specify which
card is visible on the screen. Table 22.1 lists the most useful
of these methods along with their descriptions.
Method | Description |
first(Container parent) | Displays the first card. |
last(Container parent) | Displays the last card. |
next(Container parent) | Displays the next card. |
previous(Container parent) | Displays the previous card. |
show(Container parent, String name) | Displays the specified card. |
Putting the CardLayout manager to work is a lot easier if you always keep in mind the hierarchy of components. At the bottom of the stack is the applet's display area. On top of this stack is the component (usually a panel) that will hold the "cards." On top of the parent component is the CardLayout manager, which you can think of as a deck of cards. The cards in this deck are the components that you add to the panel.
Listing 22.5 is an applet that demonstrates how all this works. The cards in this applet are the three buttons. When you run the applet, you see a single button in the display (Figure 22.10). Click the button to switch to the next button in the stack. When you get to button three and click it, you end up back at button one. You can cycle through the buttons as often as you like.
Figure 22.10 : Clicking the button switches the manager to a new card.
Listing 22.5 CardApplet.java: Using a CardLayout Manager.
import java.awt.*; import java.applet.*; public class CardApplet extends Applet { CardLayout cardLayout; Panel panel; Button button1, button2, button3; public void init() { panel = new Panel(); add(panel); cardLayout = new CardLayout(0, 0); panel.setLayout(cardLayout); button1 = new Button("Button1"); button2 = new Button("Button2"); button3 = new Button("Button3"); panel.add("Button1", button1); panel.add("Button2", button2); panel.add("Button3", button3); } public boolean action(Event evt, Object arg) { cardLayout.next(panel); return true; } }
Tell Java that the applet uses the classes in the awt package.
Tell Java that the applet uses the classes in the applet package.
Derive the CardApplet class from Java's Applet class.
Declare the layout, panel, and button objects.
Override the init() method.
Create and add the parent panel.
Create and set the layout.
Create the buttons (which act as the cards).
Add the buttons to the panel.
Override the action() method.
Switch to the next card (button).
Tell Java that the event was handled okay.
NOTE |
The stack of cards that are arranged by a CardLayout manager can be any type of component. For example, you can create several different panels, each with their own controls, and switch between the panels. This enables you to switch between whole sets of controls, just like Windows 95's property sheets. |
The most complex of the layout managers is GridBagLayout, which pretty much lets you organize objects any way you like. However, the price for this power is meticulous planning and a lot of experimentation. At the time of this writing, the documentation for the GridBagLayout manager was sketchy and incomplete. I did the best I could to figure out exactly how this layout manager worked, but there's no question that to get the best out of GridBagLayout, you're going to have to spend some time experimenting with different layouts.
To create a layout using GridBagLayout, you must follow these steps:
Yep, there's much to be done to use a GridBagLayout manager. In the sections that follow, you'll learn how to perform each of the required steps.
To create a GridBagLayout manager, call the class's constructor, like this:
GridBagLayout layout = new GridBagLayout();
The constructor requires no arguments. When you've created the GridBagLayout() object, set the manager by calling setLayout():
setLayout(layout);
This method's single argument is a reference to the layout object.
Because the position of each component in a layout controlled by a GridBagLayout object is determined by the currently set GridBagConstraints object, you must create the GridBagConstraints object before you can start building your layout. To do this, call the class's constructor:
GridBagConstraints constraints = new GridBagConstraints();
Like the GridBagLayout class, the GridBagConstraints constructor requires no arguments. However, although the class's fields start off initialized to default values, you'll almost always change some of those values before adding components to the layout. You perform this task with lines something like this:
constraints.fill = GridBagConstraints.BOTH;
This line sets the GridBagConstraints object's fill
field to a constant defined in the class. Table 22.2 shows the
fields of the GridBagConstraints class and what
they mean.
Description | |
Where within a component's area the component should be placed. Predefined values are GridBagConstraints.NORTH,
GridBagConstraints.NORTHEAST, GridBagConstraints.EAST, GridBagConstraints.SOUTHEAST, GridBagConstraints.SOUTH, GridBagConstraints.SOUTHWEST, GridBagConstraints.WEST, GridBagConstraints.NORTHWEST, and GridBagConstraints.CENTER. | |
Determines how to size a component when the display area is larger than the component. Predefined values you can use are GridBagConstraint.NONE, GridBagConstraint.HORIZONTAL, GridBagConstraint.VERTICAL, and GridBagConstraint.BOTH. | |
The number of cells in each column of a component's display area. | |
The number of cells in each row of a component's display area. | |
The X coordinate of the cell at the upper left of a component's display area. | |
The Y coordinate of the cell at the upper left of the component's display area. | |
The minimum amount of space between a component and the edges of its display area. | |
The amount of horizontal space around a component. | |
The amount of vertical space around a component. | |
Determines whether components stretch horizontally to fill the applet's display area. | |
Determines whether components stretch vertically to fill the applet's display area. |
Once you have the GridBagConstraints object created and initialized, you must set the constraints by calling the layout object's setConstraints() method:
layout.setConstraints(component, constraints);
This method's two arguments are a reference to the component whose constraints you're setting and a reference to the constraints object. You need to call setConstraints() for each component you add to the layout. After setting the constraints for the component, you add the component to the layout in the normal way, by calling the add() method.
As I said before, the only way to really understand how the GridBagLayout manager works is to experiment with it on your own. This book just doesn't have the room to cover every detail of using this complex manager. Still, I won't send you off without at least the basics. So, Listing 22.6 is an applet, called GridBagApplet, that demonstrates how to create and use a GridBagLayout manager. Figure 22.11 shows what the applet looks like when it's run under Appletviewer.
Figure 22.11 : GridBagLayout manager enables you to create unusual layouts.
Listing 22.6 GridBagApplet.java: A GridBagLayout Applet.
import java.awt.*; import java.applet.*; public class GridBagApplet extends Applet { public void init() { GridBagLayout layout = new GridBagLayout(); setLayout(layout); GridBagConstraints constraints = new GridBagConstraints(); Button button1 = new Button("Button1"); Button button2 = new Button("Button2"); Button button3 = new Button("Button3"); Button button4 = new Button("Button4"); Button button5 = new Button("Button5"); Button button6 = new Button("Button6"); Button button7 = new Button("Button7"); Button button8 = new Button("Button8"); Button button9 = new Button("Button9"); constraints.fill = GridBagConstraints.BOTH; layout.setConstraints(button1, constraints); add(button1); constraints.gridwidth = GridBagConstraints.RELATIVE; layout.setConstraints(button2, constraints); add(button2); constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(button3, constraints); add(button3); constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(button4, constraints); add(button4); constraints.gridwidth = GridBagConstraints.RELATIVE; layout.setConstraints(button5, constraints); add(button5); constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(button6, constraints); add(button6); constraints.gridwidth = 1; constraints.gridheight = 2; layout.setConstraints(button7, constraints); add(button7); constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.gridheight = 1; layout.setConstraints(button8, constraints); add(button8); layout.setConstraints(button9, constraints); add(button9); resize(300, 200); } }
Tell Java that the applet uses the classes in the awt package.
Tell Java that the applet uses the classes in the applet package.
Derive the GridBagApplet class from Java's Applet class.
Override the init() method.
Create and set the layout.
Create the constraints object.
Create nine buttons.
Initialize the fill for both vertical and horizontal.
Set the constraints for the buttons and add the buttons.
Set the applet's size.
Although GridBagApplet contains only the init() method, there's a lot going on in the program. In this section, you'll see, line by line, exactly how the applet works. The first two lines in the init() method look like this:
GridBagLayout layout = new GridBagLayout(); setLayout(layout);
This is where the applet creates its GridBagLayout object and sets it as the applet's layout. In the next line, the applet creates its GridBagConstraints object, like this:
GridBagConstraints constraints = new GridBagConstraints();
The applet will use this single GridBagConstraints object in order to set the constraints for each component added to the layout. Before components can be added, however, they must be created, which the applet does as shown in Listing 22.7.
Listing 22.7 LST22_7.TXT: Creating the Applet's Buttons.
Button button1 = new Button("Button1"); Button button2 = new Button("Button2"); Button button3 = new Button("Button3"); Button button4 = new Button("Button4"); Button button5 = new Button("Button5"); Button button6 = new Button("Button6"); Button button7 = new Button("Button7"); Button button8 = new Button("Button8"); Button button9 = new Button("Button9");
After creating the buttons, the program can start adding them to the layout. But before the first button gets added, the constraints object must contain the appropriate values. In this case, only the fill field must be initialized, since the first button component will use all the other default values:
constraints.fill = GridBagConstraints.BOTH;
Setting the fill field to both ensures that the components (in this case, buttons) will expand both vertically and horizontally to completely fill their display areas. After initializing the constraints for the first button, the applet sets the constraints and adds the button:
layout.setConstraints(button1, constraints); add(button1);
Now that you have the first button added, it's time to consider how the second button will fit in the layout. The value the applet initialized the fill field to will remain in effect for all buttons, so the applet doesn't need to change it again. However, the layout manager is going to want to know how button2 should be placed. The following lines set the constraints and add the button:
constraints.gridwidth = GridBagConstraints.RELATIVE; layout.setConstraints(button2, constraints); add(button2);
By setting gridwidth to GridBagConstraints.RELATIVE, the applet tells the layout manager that this button is the next to the last component in this row, which will determine its width. The button3 object is the last component for the first row, so it sets gridwidth to GridBagConstraints.REMAINDER:
constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(button3, constraints); add(button3);
The REMAINDER constant tells the layout manager that this control should fill the first row all the way to the end.
The button4 component is the only object on its row, so it too uses a gridwidth of REMAINDER:
constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(button4, constraints); add(button4);
Yes, it's true that the first line above really isn't necessary, since gridwidth was already set to REMAINDER. However, I like leaving this kind of line around because it tells me that I haven't forgotten something here, that I do indeed want a width of REMAINDER for this button too.
Because button5 is both the first and next-to-last button in its row, it uses a width of RELATIVE:
constraints.gridwidth = GridBagConstraints.RELATIVE; layout.setConstraints(button5, constraints); add(button5);
Because there's only two buttons in this row, the button6 component gets a width of REMAINDER:
constraints.gridwidth = GridBagConstraints.REMAINDER; layout.setConstraints(button6, constraints); add(button6);
Now, things get a little tricky (like they weren't tricky enough, right?). If you look at Figure 22.11, you'll see that button7 is one cell wide but two cells high. Buttons seven and eight, on the other hand are two cells wide but only one cell high. Even though button7 is technically the next-to-last button in its row, you don't want to give it the RELATIVE width because then Java will make the button twice as wide. So, the applet sets the width of button7 to 1 and the height to 2, as shown in Listing 22.8.
Listing 22.8 LST22_8.TXT: Setting button7's size.
constraints.gridwidth = 1; constraints.gridheight = 2; layout.setConstraints(button7, constraints); add(button7);
Now, because button8 is the last button in its row, it gets the REMAINDER width. However, the button also must be set back to a normal one-cell height, as shown in Listing 22.9.
Listing 22.9 LST22_9.LST: Creating button8.
constraints.gridwidth = GridBagConstraints.REMAINDER; constraints.gridheight = 1; layout.setConstraints(button8, constraints); add(button8);
Finally, button9 can use exactly the same restraints, which means simply setting the constraints and adding the button, like this:
layout.setConstraints(button9, constraints); add(button9);
All of the lines described in this section work together to create the applet's layout. Every layout will work differently, requiring that you carefully plan ahead how you want the applet's components laid out. There's almost an infinite number of ways to use the constraints along with the GridBagLayout manager.
You may wonder how changes to this example layout will affect the appearance of the applet. Suppose, for example, you left the fill field set to its default value of GridBagConstraints.NONE. You would then end up with a layout like that shown in Figure 22.12. Figure 22.13 shows the applet with a fill setting of GridBagConstraints.VERTICAL.
Figure 22.13 : The vertical fill stretches sone controls vertically.
Figure 22.12 : The fill setting can make a huge difference in how a layout looks.
Another change you might make is to set weightx and weighty, which tells Java how to use the extra space that usually surrounds the controls in the layout. For example, in GridBagApplet, if you set weightx to 1, you get a display like Figure 22.14, because you've told Java that you want the layout to fill the entire horizontal space in the applet. Setting weighty stretches the layout in the vertical direction, as shown in Figure 22.15, which has both weightx and weighty set.
Figure 22.14 : Setting the weightx field stretches the layout horizontally.
Figure 22.15 : Setting both weightx and weighty stretches the layout in both directions.
Java gives you many options when it comes to creating a layout for the components that make up your applet. However, having so many possibilities at your fingertips can be daunting at first, because there may be several ways to get the effect that you want. The better you learn to use Java's layout managers, the more easily you'll know which is the appropriate manager for a specific situation. Mastering the GridBagLayout manager especially requires time and patience.