Section 6.1
Components and Layouts
MANY OF THE CLASSES IN THE AWT represent visual elements of a graphical user interface, such as buttons, text-input boxes, and menus. All these classes, except for a few related to menus, are subclasses of a class named Component.
Component is an abstract class, so that you can only create objects belonging to its subclasses, not to Component itself. The subclasses that represent common GUI elements are: Button, Checkbox, Choice, Label, List, Scrollbar, TextArea, and TextField. Objects of these classes have predefined behaviors. For the most part, all you have to do is add them to your program and they take care of themselves. When the user interacts with one of these components and some action is required, the component generates an event that your program can detect and react to. There is also the Canvas subclass of Component, which represents a rectangular area to be used for displaying graphical images. A Canvas can be programmed to respond to the user's mouse and keyboard actions.
To appear on the screen, a component of any of the above types must be added to a container. A container in this context is a component that can contain other components. Containers are represented by a class named Container, which is a subclass of Component. The Container class, like Component, is an abstract class. Container has two direct subclasses, Window and Panel. A Window represents an independent top-level window that is not contained in any other component. Window is not really meant to be used directly. It has two subclasses: Frame, to represent ordinary windows that can have their own menu bars, and Dialog to represent dialog boxes that are used for limited interactions with the user. The window classes are discussed in Section 5.
A Panel, on the other hand, is a container that does not exist independently. The Applet class is a subclass of Panel, and as you have seen, an applet does not exist on its own; it is meant to be displayed by a Web browser. Any panel must be contained inside something else, either a Window, another Panel, or -- in the case of an Applet -- a page in a Web browser.
The fact that panels can contain other panels means that you can have many levels of components containing other components, as shown in the illustration on the right. This leads to two questions: How are components added to a container? How are their sizes and positions controlled?
The sizes and positions of the components in a container are controlled by a layout manager. Different layout managers implement different ways of arranging components. There are several predefined layout manager classes in the AWT: FlowLayout, GridLayout, BorderLayout, CardLayout and GridBagLayout. You can also define new layout managers, if none of these suit your purpose. Every container, by default, has a layout manager. For Panels, including Applets, the default layout manager belongs to the class FlowLayout. For Windows, the default layout manager is a BorderLayout. You can change the layout manager of a container using its setLayout(LayoutManager) method. (It is possible to set the LayoutManager of a container to be null. This allows you to take complete charge of laying out the components in the container. However, I won't deal with this option here.)
As for adding components to a container, that's easy. You just use one of the container's add() methods. There are several add() methods. Which one you should use depends on what type of LayoutManager is being used by the container, so I will discuss the appropriate add() methods as I go along.
I have often found it to be fairly difficult to get the exact layout that I want in my applets and windows. I will briefly discuss each of the layout manager classes here, but using them well will require practice and experimentation.
FlowLayout
A FlowLayout simply lines up its components without trying to be particularly neat about it. After laying out as many items as will fit in a row across the container, it will move on to the next row. The components in a given row can be either left-aligned, right-aligned, or centered, and there can be horizontal and vertical gaps. If the default constructor, "new FlowLayout()" is used, then the components on each row will be centered and the horizontal and vertical gaps will be five pixels. The constructor
FlowLayout(int align, int hgap, int vgap)
can be used to specify alternative alignment and gaps. The possible values of align are FlowLayout.LEFT, FlowLayout.RIGHT, and FlowLayout.CENTER. A nifty trick is to use a very large value of hgap to force the FlowLayout to put exactly one component in each row. The appropriate add() method for FlowLayouts has a single paramer of type Component, specifying the component to be added.
A FlowLayout is the default layout manager for applets and panels, and you have seen them used in the previous chapter. Although they are useful in certain circumstances, I have found that they often don't provide enough control over what actually appears on the screen.
BorderLayout
A BorderLayout places one component in the center of a container. The central component is surrounded by up to four other components that border it to the "North", "South", "East", and "West", as shown in the diagram at the right. Each of the four bordering components is optional. The layout manager first allocates space to the bordering components. Any space that is left over goes to the center component.
If a container uses a BorderLayout, then components should be added to the container using the method add(String,Component). The first parameter specifies where the component is to be placed. It must be one of the strings "Center", "North", "South", "East", or "West". These names are case sensitive. For example, you can't use "east" in place of "East"; if you do, you'll wind up with mysteriously missing components! (By the way, with BorderLayouts, you can also use a version of the add method with the paramaters reversed: add(Component,String).) For example, the following code creates a panel with drawArea as its center component and with scroll bars to the right and below:
Panel panel = new Panel(); panel.setLayout(new BorderLayout()); // To use BorderLayout with a Panel, you have // to change the panel's layout manager; otherwise, // a FlowLayout is used. panel.add("Center", drawArea); // Assume drawArea already exists. panel.add("South", hScroll); // Assume hScroll is a horizontal scroll bar // component that already exists. panel.add("East", vScroll); // Assume vScroll is a vertical scroll bar // component that already exists.Sometimes, you want to leave space between the components in a container. You can specify horizontal and vertical gaps in the constructor of a BorderLayout object. For example, if you say
panel.setLayout(new BorderLayout(5,7));
then the layout manager will insert horizontal gaps of 5 pixels between components and vertical gaps of 7 pixels between components. (The horizontal gap is inserted between the center and west components and between the center and east components; the vertical gap is inserted between the center and north components and between the center and south components.)
GridLayout
A GridLayout lays out components in a grid of equal sized rectangles. The illustration shows how the components would be arranged in a grid layout with 3 rows and 2 columns. If a container uses a GridLayout, the appropriate add method takes a single parameter of type Component (for example: add(myButton)). Components are added to the grid in the order shown; that is, each row is filled from left to right before going on the next row.
The constructor for a GridLayout with R rows and C columns takes the form GridLayout(R,C). If you want to leave horizontal gaps of H pixels between columns and vertical gaps of V pixels between rows, use GridLayout(R,C,H,V) instead.
When you use a GridLayout, it's probably good form to add just enough components to fill the grid. However, this is not required. If you specify a non-zero value for the number of rows, then the number of columns is essentially ignored. The system will use just as many columns as are necessary to hold the number of components that you add to the container. If you want to depend on this behavior, you should probably specify zero as the number of columns. You can also specify the number of rows as zero. In that case, you must give a non-zero number of columns. The system will use the specified number of columns, with just as many rows as necessary to hold the components that are added to the container.
Horizontal grids, with a single row, and vertical grids, with a single column, are very common. For example, suppose that button1, button2, and button3 are buttons and that you'd like to display them in a horizontal row in a panel. If you use a horizontal grid for the panel, then the buttons will completely fill that panel and will all be the same size. The panel can be created as follows:
Panel buttonBar = new Panel(); buttonBar.setLayout(new GridLayout(1,3)); // (Note: The "3" here is pretty much ignored, and // you could also say "new GridLayout(1,0)". // To leave gaps between the buttons, you could use // "new GridLayout(1,0,5,5)".) buttonBar.add(button1); buttonBar.add(button2); buttonBar.add(button3);You might find this button bar to be more attractive than the ones in the examples in the previous chapter, which used the default FlowLayout layout manager.
GridBagLayout
A GridBagLayout is similar to a GridLayout in that the container is broken down into rows and columns of rectangles. However, a GridBagLayout is much more sophisticated because the rows do not all have to be of the same height, the columns do not all have to be of the same width, and a component in the container can spread over several rows and several columns. There is a separate class, GridBagConstraints, that is used to specify the position of a component, the number of rows and columns that it occupies, and several additional properties of the component.
Using a GridBagLayout is rather complicated. I will not explain it here; if you are interested, you should consult a Java reference.
CardLayout
CardLayouts differ from other layout managers in that in a container that uses a CardLayout, only one of its components is visible at any given time. Think of the components as a set of "cards". Only one card is visible at a time, but you can flip from one card to another. Methods are provided in the CardLayout class for flipping to the first card, to the last card, and to the next card in the deck. A name can be specified for each card as it is added to the container, and there is a method in the CardLayout class for flipping directly to the card with a specified name. (The container object has to be passed as a parameter to each of these methods.)
Suppose, for example, that you want to create a Panel that can show any one of three Panels: panel1, panel2, and panel3. Assume that panel1, panel2, and panel3 have already been created:
cardPanel = new Panel(); // assume cardPanel is declared as an instance variable // so that it can be used in other methods cards = new CardLayout(); // assume cards is declared as an instance variable // so that it can be used in other methods cardPanel.setLayout(cards); cardPanel.add("First", panel1); // add panel1 with name "First" cardPanel.add("Second", panel2); // add panel2 with name "Second" cardPanel.add("Third", panel3); // add panel3 with name "Third"Elsewhere in your program, you could show panel1 by saying
cards.show(cardPanel, "First");or
cards.first(cardPanel);Other methods that are available are cards.last(cardPanel) and cards.next(cardPanel). Note that to use a CardLayout effectively, you need to keep both the layout manager and the container that uses the layout manager in instance variables, so that you can refer to them in more than one of your class's methods.
Components in Applets
When you are writing an Applet , you should remember that applets are themselves containers. This means that they have add() methods that can be used to add components and a setLayout() method that can be used to replace the default layout manager. In general, the entire layout of an applet should be set up in its init() method. This will often involve constructing sub-panels and adding them to the applet.
One final point: With most layout managers, you can specify horizontal and vertical gaps between components. But what if you want gaps between the edges of a container and the components that it contains? For that, you have to override the getInsets() method of the container. For an applet or panel, the definition of this method usually has the form:
public Insets getInsets() { return new Insets(top,left,bottom,right); }where top, left, bottom, and right specify the number of pixels to be inserted as a gap along each edge.
In Section 4, I'll explain how to use components and insets in independent windows that belong to the classes Frame and Dialog.
An Example
To finish this section, here is an applet that demonstrates various layout managers:
The applet itself uses a BorderLayout with vertical gaps of 3 pixels. These gaps show up in blue, which is the background color of the applet as a whole. The blue border around the edges comes from a getInsets() method in the applet. The "Center" component of the applet is a panel that uses a CardLayout that can display any of six cards. Each card is itself a panel that contains several buttons. Each card uses a different type of layout manager (several of which are extremely stupid choices for laying out buttons).
The "North" component of the applet is a Choice menu, which contains the names of the six panels in the card layout. The user can switch among the cards by selecting items from this menu. The "South" component of the applet is a Label that displays an appropriate message whenever the user clicks on a button or choses an item from the Choice menu.
The source code for this applet is in the file LayoutDemo.java. It consists mainly of a long init() method that creates all the buttons, panels, and other components and lays out the applet.
[ Next Section | Previous Chapter | Chapter Index | Main Index ]