> ### Section 7.2
> More about Layouts and Components

> 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 the class `java.awt.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 standard GUI elements are: `Button`, `Checkbox`, `Choice`, `Label`, `List`, `Scrollbar`, `TextArea`, and `TextField`. The `Canvas` class, which we've used in several examples, is also a subclass of `Component`. 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. I will discuss the standard GUI components in the next section.

> To appear on the screen, a component must be added to a container. A container in this context is a component that can contain other components. Containers are represented by the class `java.awt.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. I will discuss the window classes in Section 7.

> 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 must be displayed on a Web page or in some other window. 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 usually 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`. It is also possible to define new layout managers, if none of these suit your purpose. Every container is assigned a default layout manager when it is first created. 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()` 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. I will discuss this option further in Section 4.

> 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 between components. 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`. This forces the `FlowLayout` to put exactly one component in each row, since there won't be room on a single row for two components and the horizontal gap between them. The appropriate `add()` method for `FlowLayouts` has a single parameter of type `Component`, specifying the component to be added.

> For example, suppose that we want an applet to contain one button, located in the upper right corner of the applet. The default layout manager for an applet is a `FlowLayout` that uses center alignment. It would center the button horizontally. We need to give the applet a new layout manager that uses right alignment, which will shove the button to the right edge of the applet. The following `init()` method will do this:

>                 public void init() {
>                    setLayout( new FlowLayout(FlowLayout.RIGHT, 5, 5) );
>                    add( new Button("Press me!") );
>                 }

> Although `FlowLayouts` are useful in certain circumstances, I almost always set the layout manager of an applet to be a `BorderLayout` or a `GridLayout`. I use the `FlowLayout` mainly when I want to use a panel to hold a strip of controls along the top or bottom of an applet. The examples in Section 6.6 used panels in this way.

> ### 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 a version of the `add()` method that has two parameters. The first parameter is the component that is being added to the container. The second parameter specifies where the component is to be placed. It must be one of the constants `BorderLayout.CENTER`, `BorderLayout.NORTH`, `BorderLayout.SOUTH`, `BorderLayout.EAST`, or `BorderLayout.WEST`. 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(drawArea, BorderLayout.CENTER);
>                    // Assume drawArea already exists.
>            panel.add(hScroll, BorderLayout.SOUTH);
>                    // Assume hScroll is a horizontal scroll bar
>                    //     component that already exists.
>            panel.add(vScroll, BorderLayout.EAST);
>                    // 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 "`new GridLayout(R,C)`". If you want to leave horizontal gaps of `H` pixels between columns and vertical gaps of `V` pixels between rows, use "`new 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. In fact, as long as 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 all the 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 Section 6.6, 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, and I have used it on exactly two occasions in my own Java programming career. 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.

> 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(panel1, "First");
>                   // add panel1 with name "First"
>              cardPanel.add(panel2, "Second");
>                   // add panel2 with name "Second"
>              cardPanel.add(panel3, "Third");
>                   // 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)`, `cards.next(cardPanel)`, and `cards.previous(cardPanel)`. Note that each of these methods takes the container as a parameter. To use a `CardLayout` effectively, you'll need to have instance variables to record both the layout manager (`cards` in the example) and the container (`cardPanel` in the example). You need both of these objects in order to flip from one card to another.

> ### 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` are integers that specify the number of pixels to be inserted as a gap along each edge. However, things get a little more complicated in the case of a container that already has non-zero insets -- most commonly a window that uses insets to leave space for the borders of the window and the menu bar.

> In Section 7, 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:

> Sorry, but your browser
> doesn't support Java.

> 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. This panel is set to use a `CardLayout` as its layout manager. The layout contains six cards. Each card is itself another 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 Section | Chapter Index | Main Index ]