A layout manager arranges the child components of a container, as shown in Figure 19-1. It positions and sets the size of components within the container's display area according to a particular layout scheme. The layout manager's job is to fit the components into the available area while maintaining some spatial relationships among them. AWT and Swing come with several standard layout managers that will collectively handle most situations; you can make your own layout managers if you have special requirements.
Figure 19-1. A layout manager at work
Every container has a default layout manager. When you make a new container, it comes with a LayoutManager object of an appropriate type. You can install a new layout manager at any time with the setLayout( ) method. For example, we can set the layout manager of a Swing container's content pane to a type called BorderLayout like so:
mycontainer.getContentPane( ).setLayout( new BorderLayout( ) );
Notice that although we have created a BorderLayout, we haven't bothered to save a reference to it. This is typical; after installing a layout manager it usually does its work behind the scenes, interacting with the container. You rarely call the layout manager's methods directly, so you don't usually need a reference (a notable exception is CardLayout). However, you do need to know what the layout manager is going to do with your components as you work with them.
The LayoutManager is consulted whenever a container's doLayout( ) method is called to reorganize the contents. It does its job by calling the setLocation( ) or setBounds( ) methods of the individual child components to arrange them in the container's display area. A container is laid out the first time it is displayed and thereafter whenever the container's revalidate( ) method is called. Containers that are a subclass of the Window class (Frame, JFrame, and JWindow) are automatically validated whenever they are packed or resized. Calling pack( ) sets the window's size as small as possible while granting all its components their preferred sizes.
Every component provides three important pieces of information used by the layout manager in placing and sizing it: a minimum size, a maximum size, and a preferred size. These sizes are reported by the getMinimumSize( ), getMaximumSize( ), and getPreferredSize( ) methods of Component, respectively. For example, a plain JButton object can normally be changed to any size. However, the button's designer can provide a preferred size for a good-looking button. The layout manager might use this size when there are no other constraints, or it might ignore it, depending on its scheme. If we give the button a label, the button may need a new minimum size to display itself properly. The layout manager should generally respect the button's minimum size and guarantee that it has at least that much space. Similarly, a particular component might not be able to display itself properly if it is too large (perhaps it has to scale up an image); it can use getMaximumSize( ) to report the largest size it considers acceptable.
The preferred size of a Container object has the same meaning as for any other type of component. However, since a Container may hold its own components and want to arrange them in its own layout, its preferred size is a function of its layout manager. The layout manager is, therefore, involved in both sides of the issue. It asks the components in its container for their preferred (or minimum) sizes in order to arrange them. Based on those values, it calculates the preferred size for its own container (which can then be communicated to the container's parent and so on).
When a layout manager is called to arrange its components, it is working within a fixed area. It usually begins by looking at its container's dimensions and the preferred or minimum sizes of the child components. It then doles out screen area and sets the sizes of components according to its scheme and specific constraints.
You can set the minimum, preferred, and maximum sizes for a component with the setMinimumSize( ), setMaximumSize( ), and setPreferredSize( ) methods. Interestingly though, these methods were not added until Java 5.0. Prior to that you had to extend the class and override the corresponding "get" methods to do so. Why were these methods added so late to Java? Because those values normally should be calculated based on the real conditions of the component, not just fixed at a static value that looks good in one particular case. You can override the getMinimumSize( ), getMaximumSize( ), and getPreferredSize( ) methods of your own components to allow them to calculate those values, but you should do this only if you are specializing the component and it has new needs. In general, if you find yourself fighting with a layout manager because it's changing the size of one of your components, you are probably using the wrong kind of layout manager or not composing your user interface properly. Often it's easier to use a number of JPanel objects in a given display, each one with its own LayoutManager. try breaking down the problem: place related components in their own JPanel and then arrange the panels in the container. When that becomes unwieldy, use a constraint-based layout manager such as GridBagLayout, which we'll discuss later in this chapter.