This document was uploaded by user and they confirmed that they have the permission to share
it. If you are author or own the copyright of this book, please report to us by using this DMCA
report form. Report DMCA
Lesson: Laying Out Components Within a Container This lesson tells you how to use the layout managers provided by the Java platform. It also tells you how to use absolute positioning (no layout manager) and gives an example of writing a custom layout manager. For each layout manager (or lack thereof), this lesson points to an example that you can run using JavaTM Web Start. By resizing an example's window, you can see how size changes affect the layout.
Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
A Visual Guide to Layout Managers This section shows examples of the standard layout managers and points to the how-to section for each one.
Using Layout Managers This section gives general rules on using the standard layout managers. It includes how to set the layout manager, add components to a container, provide size and alignment hints, put space between components, and set the orientation of the container's layout so that it is appropriate for the locale in which the program is running. It also has some tips for choosing the right layout manager.
How Layout Management Works This section goes through a typical layout sequence and then describes what happens when a component's size changes.
How to Use ... This series of sections tells you how to use each of the general-purpose layout managers that the Java platform provides.
Creating a Custom Layout Manager Instead of using one of the Java platform's layout managers, you can write your own. Layout managers must implement the LayoutManager interface, which specifies the five methods every layout manager must define. Optionally, layout managers can implement LayoutManager2, which is a subinterface of LayoutManager. 840
Doing Without a Layout Manager (Absolute Positioning) If necessary, you can position components without using a layout manager. Generally, this solution is used to specify absolute sizes and positions for components.
Solving Common Layout Problems Some of the most common layout problems involve components that are displayed too small — or not at all. This section tells you how to fix these and other common layout problems.
Questions and Exercises Try these questions and exercises to test what you have learned in this lesson.
A Visual Guide to Layout Managers Several AWT and Swing classes provide layout managers for general use:
This section shows example GUIs that use these layout managers, and tells you where to find the how-to page for each layout manager. You can find links for running the examples in the how-to pages and in the example index.
Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
BorderLayout
841
Every content pane is initialized to use a BorderLayout. (As Using Top-Level Containers explains, the content pane is the main container in all frames, applets, and dialogs.) A BorderLayout places components in up to five areas: top, bottom, left, right, and center. All extra space is placed in the center area. Tool bars that are created using JToolBar must be created within a BorderLayout container, if you want to be able to drag and drop the bars away from their starting positions. For further details, see How to Use BorderLayout.
BoxLayout
The BoxLayout class puts components in a single row or column. It respects the components' requested maximum sizes and also lets you align components. For further details, see How to Use BoxLayout.
CardLayout
The CardLayout class lets you implement an area that contains different components at different times. A CardLayout is often controlled by a combo box, with the state of the combo box determining which panel (group of components) the CardLayout displays. An alternative to using CardLayout is using a tabbed pane, which provides similar functionality but with a pre-defined GUI. For further details, see How to Use CardLayout.
FlowLayout
FlowLayout is the default layout manager for every JPanel. It simply lays out components in a
single row, starting a new row if its container is not sufficiently wide. Both panels in CardLayoutDemo, shown previously, use FlowLayout. For further details, see How to Use FlowLayout.
GridBagLayout
842
GridBagLayout is a sophisticated, flexible layout manager. It aligns components by placing them
within a grid of cells, allowing components to span more than one cell. The rows in the grid can have different heights, and grid columns can have different widths. For further details, see How to Use GridBagLayout.
GridLayout
GridLayout simply makes a bunch of components equal in size and displays them in the requested
number of rows and columns. For further details, see How to Use GridLayout.
GroupLayout
GroupLayout is a layout manager that was developed for use by GUI builder tools, but it can also be used manually. GroupLayout works with the horizontal and vertical layouts separately. The layout is
defined for each dimension independently. Consequently, however, each component needs to be defined twice in the layout. The Find window shown above is an example of a GroupLayout. For further details, see How to Use GroupLayout.
SpringLayout
843
SpringLayout is a flexible layout manager designed for use by GUI builders. It lets you specify
precise relationships between the edges of components under its control. For example, you might define that the left edge of one component is a certain distance (which can be dynamically calculated) from the right edge of a second component. SpringLayout lays out the children of its associated container according to a set of constraints, as shall be seen in How to Use SpringLayout.
Using Layout Managers A layout manager is an object that implements the LayoutManager interface* and determines the size and position of the components within a container. Although components can provide size and alignment hints, a container's layout manager has the final say on the size and position of the components within the container. Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
This section discusses some of the common tasks related to using layout managers:
Setting the Layout Manager Adding Components to a Container Providing Size and Alignment Hints Putting Space Between Components Setting the Container's Orientation Tips on Choosing a Layout Manager Third-Party Layout Managers
Setting the Layout Manager As a rule, the only containers whose layout managers you need to worry about are JPanels and content panes. Each JPanel object is initialized to use a FlowLayout, unless you specify differently when creating the JPanel. Content panes use BorderLayout by default. If you do not like the default layout manager that a panel or content pane uses, you are free to change it to a different one. However, unless you are using JToolBar, the FlowLayout and BorderLayout managers are only useful for prototyping. Any real application will need to reset the layout manager. Again, you should use an appropriate tool to do this, rather than coding the manager by hand. 844
You can set a panel's layout manager using the JPanel constructor. For example: JPanel panel = new JPanel(new BorderLayout());
After a container has been created, you can set its layout manager using the setLayout method. For example: Container contentPane = frame.getContentPane(); contentPane.setLayout(new FlowLayout());
Although we strongly recommend that you use layout managers, you can perform layout without them. By setting a container's layout property to null, you make the container use no layout manager. With this strategy, called absolute positioning, you must specify the size and position of every component within that container. One drawback of absolute positioning is that it does not adjust well when the top-level container is resized. It also does not adjust well to differences between users and systems, such as different font sizes and locales.
Adding Components to a Container When you add components to a panel or content pane, the arguments you specify to the add method depend on the layout manager that the panel or content pane is using. In fact, some layout managers do not even require you to add the component explicitly; for example, GroupLayout. For example, BorderLayout requires that you specify the area to which the component should be added (using one of the constants defined in BorderLayout) using code like this: pane.add(aComponent, BorderLayout.PAGE_START);
The how-to section for each layout manager has details on what, if any, arguments you need to specify to the add method. Some layout managers, such as GridBagLayout and SpringLayout, require elaborate setup procedures. Many layout managers, however, simply place components based on the order they were added to their container. Swing containers other than JPanel and content panes generally provide API that you should use instead of the add method. For example, instead of adding a component directly to a scroll pane (or, actually, to its viewport), you either specify the component in the JScrollPane constructor or use setViewportView. Because of specialized API like this, you do not need to know which layout manager (if any) many Swing containers use. (For the curious: scroll panes happen to use a layout manager named ScrollPaneLayout.) For information about how to add components to a specific container, see the how-to page for the container. You can find the component how-to pages using How to Use Various Components.
Providing Size and Alignment Hints Sometimes you need to customize the size hints that a component provides to its container's layout manager, so that the component will be laid out well. You can do this by specifying one or more of the minimum, preferred, and maximum sizes of the component. You can invoke the component's methods for setting size hints — setMinimumSize, setPreferredSize, and setMaximumSize. Or you can create a subclass of the component that overrides the appropriate getter methods — getMinimumSize, getPreferredSize, and getMaximumSize. Here is an example of making a component's maximum size unlimited: component.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
845
Many layout managers do not pay attention to a component's requested maximum size. However, BoxLayout and SpringLayout do. Furthermore, GroupLayout provides the ability to set the minimum, preferred or maximum size explicitly, without touching the component. Besides providing size hints, you can also provide alignment hints. For example, you can specify that the top edges of two components should be aligned. You set alignment hints either by invoking the component's setAlignmentX and setAlignmentY methods, or by overriding the component's getAlignmentX and getAlignmentY methods. Although most layout managers ignore alignment hints, BoxLayout honors them. You can find examples of setting the alignment in How to Use BoxLayout.
Putting Space Between Components Three factors influence the amount of space between visible components in a container: The layout manager Some layout managers automatically put space between components; others do not. Some let you specify the amount of space between components. See the how-to page for each layout manager for information about spacing support. Invisible components You can create lightweight components that perform no painting, but that can take up space in the GUI. Often, you use invisible components in containers controlled by BoxLayout. See How to Use BoxLayout for examples of using invisible components. Empty borders No matter what the layout manager, you can affect the apparent amount of space between components by adding empty borders to components. The best candidates for empty borders are components that typically have no default border, such as panels and labels. Some other components might not work well with borders in some lookand-feel implementations, because of the way their painting code is implemented. For information about borders, see How to Use Borders.
Setting the Container's Orientation This website is written in English, with text that runs from left to right, and then top to bottom. However, many other languages have different orientations. The componentOrientation property provides a way of indicating that a particular component should use something different from the default left-to-right, top-to-bottom orientation. In a component such as a radio button, the orientation might be used as a hint that the look and feel should switch the locations of the icon and text in the button. In a container, the orientation is used as a hint to the layout manager. To set a container's orientation, you can use either the Component-defined method setComponentOrientation or, to set the orientation on the container's children as well, applyComponentOrientation. The argument to either method can be a constant such as ComponentOrientation.RIGHT_TO_LEFT, or it can be a call to the ComponentOrientation method getOrientation(Locale). For example, the following code causes all JComponents to be initialized with an Arabic-language locale, and then sets the orientation of the content pane and all components inside it accordingly: JComponent.setDefaultLocale(new Locale("ar")); JFrame frame = new JFrame(); ... Container contentPane = frame.getContentPane(); contentPane.applyComponentOrientation( ComponentOrientation.getOrientation(
846
contentPane.getLocale()));
Here are two pictures showing how FlowLayout lays out components in containers that are exactly the same, except for their orientation.
Default orientation (left-to-right)
Right-to-left orientation The standard layout managers that support component orientation are FlowLayout, BorderLayout, BoxLayout, GridBagLayout, and GridLayout. Note: Care must be taken that the component orientation is applied to renderers, editors and any other components unreachable through normal traversal of the containment hierarchy.
Tips on Choosing a Layout Manager Layout managers have different strengths and weaknesses. This section discusses some common layout scenarios and which layout managers might work for each scenario. However, once again, it is strongly recommended that you use a builder tool to create your layout managers, such as the NetBeans IDE 5.5 Matisse GUI builder, rather than coding managers by hand. The scenarios listed below are given for information purposes, in case you are curious about which type of manager is used in different situations, or in case you absolutely must code your manager manually. If none of the layout managers we discuss is right for your situation and you cannot use a builder tool, feel free to use other layout managers that you may write or find. Also keep in mind that flexible layout managers such as GridBagLayout and SpringLayout can fulfill many layout needs. Scenario: You need to display a component in as much space as it can get. If it is the only component in its container, use GridLayout or BorderLayout. Otherwise, BorderLayout or GridBagLayout might be a good match. If you use BorderLayout, you will need to put the space-hungry component in the center. With GridBagLayout, you will need to set the constraints for the component so that fill=GridBagConstraints.BOTH. Another possibility is to use BoxLayout, making the space-hungry component specify very large preferred and maximum sizes. Scenario: You need to display a few components in a compact row at their natural size. Consider using a JPanel to group the components and using either the JPanel's default FlowLayout manager or the BoxLayout manager. SpringLayout is also good for this. Scenario: You need to display a few components of the same size in rows and columns. GridLayout is perfect for this.
847
Scenario: You need to display a few components in a row or column, possibly with varying amounts of space between them, custom alignment, or custom component sizes. BoxLayout is perfect for this. Scenario: You need to display aligned columns, as in a form-like interface where a column of labels is used to describe text fields in an adjacent column. SpringLayout is a natural choice for this. The SpringUtilities class used by several Tutorial examples defines a makeCompactGrid method that lets you easily align multiple rows and columns of components. Scenario: You have a complex layout with many components. Consider either using a very flexible layout manager such as GridBagLayout or SpringLayout, or grouping the components into one or more JPanels to simplify layout. If you take the latter approach, each JPanel might use a different layout manager.
Third Party Layout Managers Other third party layout managers have been created by the Swing community, to complement those provided by the Java platform. The following list is by no means definitive, but the layout managers listed below are the most popular:
*Way back in JDK 1.1 a second interface, LayoutManager2, was introduced. LayoutManager2 extends LayoutManager, providing support for maximum size and alignment. LayoutManager2 also adds the methods addLayoutComponent, that takes an Object, and invalidateLayout. Layout managers also need the notifications provided by LayoutManager2, so any modern layout manager will need to implement it.
How Layout Management Works Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
Here is an example of a layout management sequence for a container using LayoutManager2. 1. Layout managers basically do two things: o Calculate the minimum/preferred/maximum sizes for a container. o Lay out the container's children. Layout managers do this based on the provided constraints, the container's properties (such as insets) and on the children's minimum/preferred/maximum sizes. If a child is itself a container then its own layout manger is used to get its minimum/preferred/maximum sizes and to lay it out.
848
2. A container can be valid (namely, isValid() returns true) or invalid. For a container to be valid, all the container's children must be laid out already and must all be valid also. The Container.validate method can be used to validate an invalid container. This method triggers the layout for the container and all the child containers down the component hierarchy and marks this container as valid. 3. After a component is created it is in the invalid state by default. The Window.pack method validates the window and lays out the window's component hierarchy for the first time. The end result is that to determine the best size for the container, the system determines the sizes of the containers at the bottom of the containment hierarchy. These sizes then percolate up the containment hierarchy, eventually determining the container's total size. If the size of a component changes, for example following a change of font, the component must be resized and repainted by calling the revalidate and repaint methods on that component. Both revalidate and repaint are thread-safe — you need not invoke them from the event-dispatching thread. When you invoke revalidate on a component, a request is passed up the containment hierarchy until it encounters a container, such as a scroll pane or top-level container, that should not be affected by the component's resizing. (This is determined by calling the container's isValidateRoot method.) The container is then laid out, which has the effect of adjusting the revalidated component's size and the size of all affected components.
How to Use Various Layout Managers Each of the following pages describes how to use a particular kind of layout manager. Another way to get to these pages is through A Visual Guide to Layout Managers. Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
How to Use BorderLayout How to Use BoxLayout How to Use CardLayout How to Use FlowLayout How to Use GridBagLayout How to Use GridLayout How to Use GroupLayout How to Use SpringLayout
How to Use BorderLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
849
The following figure represents a snapshot of an application that uses the BorderLayout class.
Click the Launch button to run BorderLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The complete code of this demo is in the BorderLayoutDemo.java file. As the preceding picture shows, a BorderLayout object has five areas. These areas are specified by the BorderLayout constants:
PAGE_START PAGE_END LINE_START LINE_END CENTER
Version note: Before JDK release 1.4, the preferred names for the various areas were different, ranging from points of the compass (for example, BorderLayout.NORTH for the top area) to wordier versions of the constants we use in our examples. The constants our examples use are preferred because they are standard and enable programs to adjust to languages that have different orientations.
If the window is enlarged, the center area gets as much of the available space as possible. The other areas expand only as much as necessary to fill all available space. Often a container uses only one or two of the areas of the BorderLayout object — just the center, or the center and the bottom. The following code adds components to a frame's content pane. Because content panes use the BorderLayout class by default, the code does not need to set the layout manager. The complete program is in the BorderLayoutDemo.java file. ...//Container pane = aFrame.getContentPane()... JButton button = new JButton("Button 1 (PAGE_START)"); pane.add(button, BorderLayout.PAGE_START); //Make the center component big, since that's the //typical usage of BorderLayout. button = new JButton("Button 2 (CENTER)"); button.setPreferredSize(new Dimension(200, 100)); pane.add(button, BorderLayout.CENTER); button = new JButton("Button 3 (LINE_START)"); pane.add(button, BorderLayout.LINE_START);
850
button = new JButton("Long-Named Button 4 (PAGE_END)"); pane.add(button, BorderLayout.PAGE_END); button = new JButton("5 (LINE_END)"); pane.add(button, BorderLayout.LINE_END);
Specify the component's location (for example, BorderLayout.LINE_END) as one of the arguments to the add method. If this component is missing from a container controlled by a BorderLayout object, make sure that the component's location was specified and no another component was placed in the same location. All tutorial examples that use the BorderLayout class specify the component as the first argument to the add method. For example: add(component, BorderLayout.CENTER)
//preferred
However, the code in other programs specifies the component as the second argument. For example, here are alternate ways of writing the preceding code: add(BorderLayout.CENTER, component) or add("Center", component)
//valid but old fashioned //valid but error prone
The BorderLayout API The following table lists constructors and methods to specify gaps (in pixels). Specifying gaps Constructor or Method Purpose BorderLayout(int horizontalGap, int Defines a border layout with specified gaps verticalGap) between components. setHgap(int)
Sets the horizontal gap between components.
setVgap(int)
Sets the vertical gap between components.
Examples that Use BorderLayout The following table lists code examples that use the BorderLayout class and provides links to related sections. Where Example Notes Described BorderLayoutDemo This page Puts a component in each of the five possible locations. One of many examples that puts a single component in the How to Use TabbedPaneDemo center of a content pane, so that the component is as large as Tabbed Panes possible. Creates a JPanel object that uses the BorderLayout class. How to Use CheckBoxDemo Puts components into the left (actually, LINE_START) and Check Boxes center locations.
851
How to Use BoxLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager. The Swing packages include a general purpose layout manager named BoxLayout. BoxLayout either stacks its components on top of each other or places them in a row — your choice. You might think of it as a version of FlowLayout, but with greater functionality. Here is a picture of an application that demonstrates using BoxLayout to display a centered column of components:
Click the Launch button to run BoxLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
You can see the code in BoxLayoutDemo.java. The following figure shows a GUI that uses two instances of BoxLayout. In the top part of the GUI, a top-to-bottom box layout places a label above a scroll pane. In the bottom part of the GUI, a left-toright box layout places two buttons next to each other. A BorderLayout combines the two parts of the GUI and ensures that any excess space is given to the scroll pane.
You can find links for running ListDialog and for its source files in the example index for Using Swing Components. The following code, taken from ListDialog.java, lays out the GUI. This code is in the constructor for the dialog, which is implemented as a JDialog subclass. The bold lines of code set up the box layouts and add components to them. JScrollPane listScroller = new JScrollPane(list); listScroller.setPreferredSize(new Dimension(250, 80));
852
listScroller.setAlignmentX(LEFT_ALIGNMENT); ... //Lay out the label and scroll pane from top to bottom. JPanel listPane = new JPanel(); listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS)); JLabel label = new JLabel(labelText); ... listPane.add(label); listPane.add(Box.createRigidArea(new Dimension(0,5))); listPane.add(listScroller); listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10)); //Lay out the buttons from left to right. JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10)); buttonPane.add(Box.createHorizontalGlue()); buttonPane.add(cancelButton); buttonPane.add(Box.createRigidArea(new Dimension(10, 0))); buttonPane.add(setButton); //Put everything together, using the content pane's BorderLayout. Container contentPane = getContentPane(); contentPane.add(listPane, BorderLayout.CENTER); contentPane.add(buttonPane, BorderLayout.PAGE_END);
The first bold line creates a top-to-bottom box layout and sets it up as the layout manager for listPane. The two arguments to the BoxLayout constructor are the container that it manages and the axis along which the components will be laid out. The PAGE_AXIS constant specifies that components should be laid out in the direction that lines flow across a page as determined by the target container's ComponentOrientation property. The LINE_AXIS constant specifies that components should be laid out in the direction of a line of text as determined by the target container's ComponentOrientation property. These constants allow for internationalization, by laying out components in their container with the correct left-to-right, right-to-left or top-to-bottom orientation for the language being used. The next three bold lines add the label and scroll pane to the container, separating them with a rigid area — an invisible component used to add space between components. In this case, the rigid area has no width and puts exactly 5 pixels between the label and scroll pane. Rigid areas are discussed later, in Using Invisible Components as Filler. The next chunk of bold code creates a left-to-right box layout and sets it up for the buttonPane container. Then the code adds two buttons to the container, using a rigid area to put 10 pixels between the buttons. To place the buttons at the right side of their container, the first component added to the container is glue. This glue is an invisible component that grows as necessary to absorb any extra space in its container. Glue is discussed in Using Invisible Components as Filler. As an alternative to using invisible components, you can sometimes use empty borders to create space around components, most particularly panels. For example, the preceding code snippet uses empty borders to put 10 pixels between all sides of the dialog and its contents, and between the two parts of the contents. Borders are completely independent of layout managers. They are simply how Swing components draw their edges and provide padding between the content of the component and the edge. See How to Use Borders for more information. The following sections discuss BoxLayout in more detail:
Box layout features 853
Using invisible components as filler Fixing alignment problems Specifying component sizes The box layout API Examples that use box layouts
Do not let the length of the BoxLayout discussion scare you! You can probably use BoxLayout with the information you already have. If you run into trouble or you want to take advantage of BoxLayout's power, read on.
Box Layout Features As said before, BoxLayout arranges components either on top of each other or in a row. As the box layout arranges components, it takes the components' alignments and minimum, preferred, and maximum sizes into account. In this section, we will talk about top-to-bottom layout. The same concepts apply to left-to-right or right-to-left layout. You simply substitute X for Y, height for width, and so on. Version note: Before JDK version 1.4, no constants existed for specifying the box layout's axis in a localizable way. Instead, you specified X_AXIS (left to right) or Y_AXIS (top to bottom) when creating the BoxLayout. Our examples now use the constants LINE_AXIS and PAGE_AXIS, which are preferred because they enable programs to adjust to languages that have different orientations. In the default, left-to-right orientation, LINE_AXIS specifies left-to-right layout and PAGE_AXIS specifies top-to-bottom layout.
When a BoxLayout lays out components from top to bottom, it tries to size each component at the component's preferred height. If the vertical space of the layout does not match the sum of the preferred heights, then BoxLayout tries to resize the components to fill the space. The components either grow or shrink to fill the space, with BoxLayout honoring the minimum and maximum sizes of each of the components. Any extra space appears at the bottom of the container. For a top-to-bottom box layout, the preferred width of the container is that of the maximum preferred width of the children. If the container is forced to be wider than that, BoxLayout attempts to size the width of each component to that of the container's width (minus insets). If the maximum size of a component is smaller than the width of the container, then X alignment comes into play. The X alignments affect not only the components' positions relative to each other, but also the location of the components (as a group) within their container. The following figures illustrate alignment of components that have restricted maximum widths.
854
In the first figure, all three components have an X alignment of 0.0 (Component.LEFT_ALIGNMENT). This means that the components' left sides should be aligned. Furthermore, it means that all three components are positioned as far left in their container as possible. In the second figure, all three components have an X alignment of 0.5 (Component.CENTER_ALIGNMENT). This means that the components' centers should be aligned, and that the components should be positioned in the horizontal center of their container. In the third figure, the components have an X alignment of 1.0 (Component.RIGHT_ALIGNMENT). You can guess what that means for the components' alignment and position relative to their container. You might be wondering what happens when the components have both restricted maximum sizes and different X alignments. The next figure shows an example of this:
As you can see, the left side of the component with an X alignment of 0.0 (Component.LEFT_ALIGNMENT) is aligned with the center of the component that has the 0.5 X alignment (Component.CENTER_ALIGNMENT), which is aligned with the right side of the component that has an X alignment of 1.0 (Component.RIGHT_ALIGNMENT). Mixed alignments like this are further discussed in Fixing Alignment Problems. What if none of the components has a maximum width? In this case, if all the components have identical X alignment, then all components are made as wide as their container. If the X alignments are different, then any component with an X alignment of 0.0 (left) or 1.0 (right) will be smaller. All components with an intermediate X alignment (such as center) will be as wide as their container. Here are two examples:
To get to know BoxLayout better, you can run your own experiments with BoxLayoutDemo2. Try this: 855
1. Click the Launch button to run BoxLayoutDemo2 using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
You can see the code in BoxLayoutDemo2.java. You will see a window like the one above that contains three rectangles. Each rectangle is an instance of BLDComponent, which is a JComponent subclass. 2. Click inside one of the rectangles. This is how you change the rectangle's X alignment. 3. Click the check box at the bottom of the window. This turns off restricted sizing for all the rectangles. 4. Make the window taller. This makes the rectangles' container larger than the sum of the rectangles' preferred sizes. The container is a JPanel that has a red outline, so that you can tell where the container's edges are.
Using Invisible Components as Filler Each component controlled by a box layout butts up against its neighboring components. If you want to have space between components, you can either add an empty border to one or both components, or insert invisible components to provide the space. You can create invisible components with the help of the Box class. The Box class defines a nested class, Box.Filler, that is a transparent component that paints nothing, and is used to provide space between other components. However, Filler is not actually invisible, because setVisible(false) is not invoked. The Box class provides convenience methods to help you create common kinds of filler. The following table gives details about creating invisible components with Box and Box.Filler. Type
Size Constraints
rigid area
glue
How to Create Box.createRigidArea(size)
horizontal
Box.createHorizontalGlue()
vertical
Box.createVerticalGlue()
custom Box.Filler (as specified)
new Box.Filler(minSize, prefSize, maxSize)
Here is how you generally use each type of filler: Rigid area Use this when you want a fixed-size space between two components. For example, to put 5 pixels between two components in a left-to-right box, you can use this code: container.add(firstComponent);
Note: The Box class provides another kind of filler for putting fixed space between components: a vertical or horizontal strut. Unfortunately, struts have unlimited maximum heights or widths (for horizontal and vertical struts, respectively). This means that if you use a horizontal box within a vertical box, for example, the horizontal box can sometimes become too tall. For this reason, we recommend that you use rigid areas instead of struts. Glue Use this to specify where excess space in a layout should go. Think of it as a kind of elastic glue — stretchy and expandable, yet taking up no space unless you pull apart the components that it is sticking to. For example, by putting horizontal glue between two components in a left-to-right box, you make any extra space go between those components, instead of to the right of all the components. Here is an example of making the space in a left-to-right box go between two components, instead of to the right of the components: container.add(firstComponent); container.add(Box.createHorizontalGlue()); container.add(secondComponent);
Custom Box.Filler Use this to specify a component with whatever minimum, preferred, and maximum sizes you want. For example, to create some filler in a left-to-right layout that puts at least 5 pixels between two components and ensures that the container has a minimum height of 100 pixels, you could use this code: container.add(firstComponent); Dimension minSize = new Dimension(5, 100); Dimension prefSize = new Dimension(5, 100); Dimension maxSize = new Dimension(Short.MAX_VALUE, 100); container.add(new Box.Filler(minSize, prefSize, maxSize)); container.add(secondComponent);
Fixing Alignment Problems 857
Two types of alignment problems sometimes occur with BoxLayout:
A group of components all have the same alignment, but you want to change their alignment to make them look better. For example, instead of having the centers of a group of left-to-right buttons all in a line, you might want the bottoms of the buttons to be aligned. Here is an example:
Two or more components controlled by a BoxLayout have different default alignments, which causes them to be mis-aligned. For example, as the following shows, if a label and a panel are in a top-to-bottom box layout, the label's left edge is, by default, aligned with the center of the panel.
In general, all the components controlled by a top-to-bottom BoxLayout object should have the same X alignment. Similarly, all the components controlled by a left-to-right Boxlayout should generally have the same Y alignment. You can set a JComponent's X alignment by invoking its setAlignmentX method. An alternative available to all components is to override the getAlignmentX method in a custom subclass of the component class. Similarly, you set the Y alignment of a component by invoking the setAlignmentY method or by overriding getAlignmentY. Here is an example, taken from an application called BoxAlignmentDemo, of changing the Y alignments of two buttons so that the bottoms of the buttons are aligned: button1.setAlignmentY(Component.BOTTOM_ALIGNMENT); button2.setAlignmentY(Component.BOTTOM_ALIGNMENT);
858
Click the Launch button to run BoxAlignmentDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
By default, most components have center X and Y alignment. However, buttons, combo boxes, labels, and menu items have a different default X alignment value: LEFT_ALIGNMENT. The previous picture shows what happens if you put a left-aligned component such as a label together with a center-aligned component in a container controlled by a top-to-bottom BoxLayout. The BoxAlignmentDemo program gives examples of fixing mismatched alignment problems. Usually, it is as simple as making an offending button or label be center aligned. For example: label.setAlignmentX(Component.CENTER_ALIGNMENT);
Specifying Component Sizes As mentioned before, BoxLayout pays attention to a component's requested minimum, preferred, and maximum sizes. While you are fine-tuning the layout, you might need to adjust these sizes. Sometimes the need to adjust the size is obvious. For example, a button's maximum size is generally the same as its preferred size. If you want the button to be drawn wider when additional space is available, then you need to change its maximum size. Sometimes, however, the need to adjust size is not so obvious. You might be getting unexpected results with a box layout, and you might not know why. In this case, it is usually best to treat the problem as an alignment problem first. If adjusting the alignments does not help, then you might have a size problem. We'll discuss this further a bit later.
Note: Although BoxLayout pays attention to a component's maximum size, many layout managers do not. For example, if you put a button in the bottom part of a BorderLayout, the button will probably be wider than its preferred width, no matter what the button's maximum size is. BoxLayout, on the other hand, never makes a button wider than its maximum size.
You can change the minimum, preferred, and maximum sizes in two ways:
By invoking the appropriate setXxxSize method (which is defined by the JComponent class). For example:
comp.setMinimumSize(new Dimension(50, 25)); comp.setPreferredSize(new Dimension(50, 25)); comp.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); By overriding the appropriate getXxxSize method. For example: ...//in a subclass of a component class: public Dimension getMaximumSize() { size = getPreferredSize(); size.width = Short.MAX_VALUE; return size; }
859
If you are running into trouble with a box layout and you have ruled out alignment problems, then the trouble might well be size-related. For example, if the container controlled by the box layout is taking up too much space, then one or more of the components in the container probably needs to have its maximum size restricted. You can use two techniques to track down size trouble in a box layout:
Add a garish line border to the outside of the Swing components in question. This lets you see what size they really are. For example:
comp.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(Color.red), comp.getBorder())); Use System.out.println to print the components' minimum, preferred, and
maximum sizes, and perhaps their bounds.
The Box Layout API The following tables list the commonly used BoxLayout and Box constructors and methods. The API for using box layouts falls into these categories:
Creating BoxLayout objects Creating space fillers Other useful methods Creating BoxLayout Objects Purpose
Constructor or Method
BoxLayout(Container, int)
Creates a BoxLayout instance that controls the specified Container. The integer argument specifies the axis along which the container's components should be laid out. When the container has the default component orientation, BoxLayout.LINE_AXIS specifies that the components be laid out from left to right, and BoxLayout.PAGE_AXIS specifies that the components be laid out from top to bottom.
Box(int)
Creates a Box — a container that uses a BoxLayout with the specified axis. As of release 1.3, Box extends JComponent. Before that, it was implemented as a subclass of Container.
static Box createHorizontalBox() (in Box)
Creates a Box that lays out its components from left to right.
static Box createVerticalBox() (in Box)
Creates a Box that lays out its components from top to bottom.
Creating Space Fillers These methods are defined in the Box class. Constructor or Method Purpose Component createRigidArea(Dimension)
Create a rigid component.
Component createHorizontalGlue() Create a glue component. Horizontal glue and vertical glue Component createVerticalGlue() can be very useful. Component createGlue()
Create a "strut" component. We recommend using rigid areas instead of struts.
Box.Filler(Dimension, Dimension, Dimension)
Creates a component with the specified minimum, preferred, and maximum sizes (with the arguments specified in that order). See the custom Box.Filler discussion, earlier in this section, for details. Other Useful Methods
Method void changeShape(Dimension, Dimension, Dimension) (in Box.Filler)
Purpose Change the minimum, preferred, and maximum sizes of the recipient Box.Filler object. The layout changes accordingly.
Examples that Use Box Layouts The following table lists some of the many examples that use box layouts. Example Where Described Notes BoxLayoutDemo2 This page Uses a box layout to create a centered column of components. BoxAlignmentDemo This page Demonstrates how to fix common alignment problems. BoxLayoutDemo This page Lets you play with alignments and maximum sizes. ListDialog This page A simple yet realistic example of using both a topto-bottom box layout and a left-to-right one. Uses horizontal glue, rigid areas, and empty borders. Also sets the X alignment of a component. InternalFrameEventDemo How to Write an Uses a top-to-bottom layout to center buttons and Internal Frame a scroll pane in an internal frame. Listener MenuGlueDemo Customizing Shows how to right-align a menu in the menu bar, Menu Layout using a glue component. MenuLayoutDemo Customizing Shows how to customize menu layout by changing Menu Layout the menu bar to use a top-to-bottom box layout, and the popup menu to use a left-to-right box layout. How to Use Aligns two components in different box-layoutConversionPanel.java in Panels controlled containers by setting the components' the Converter demo widths to be the same, and their containers' widths to be the same.
How to Use CardLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the 861
NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
The following figure represents a snapshot of an application that uses the CardLayout class to switch between two panels.
Click the Launch button to run CardLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The complete code of this demo is in the CardLayoutDemo.java file. The CardLayout class manages two or more components (usually JPanel instances) that share the same display space. When using the CardLayout class, let the user choose between the components by using a combo box. The CardLayoutDemo application is an example to illustrate this feature. Another way to accomplish the same task is to use a tabbed pane. The following picture shows a tabbed pane version of the preceding example:
Because a tabbed pane provides its own GUI, using a tabbed pane is simpler than using the CardLayout class. For example, implementing the preceding example using a tabbed pane results in a program with fewer lines of code. Click the Launch button to run TabDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The complete code of this demo is in the TabDemo.java file. Conceptually, each component that a CardLayout manages is like a playing card or trading card in a stack, where only the top card is visible at any time. You can choose the card that is showing in any of the following ways:
By asking for either the first or last card, in the order it was added to the container By flipping through the deck backwards or forwards By specifying a card with a specific name 862
The CardLayoutDemo class uses the last scheme. The following code snippet from the CardLayoutDemo.java application creates the CardLayout object and the components it manages. //Where instance variables are declared: JPanel cards; final static String BUTTONPANEL = "Card with JButtons"; final static String TEXTPANEL = "Card with JTextField"; //Where the components controlled by the CardLayout are initialized: //Create the "cards". JPanel card1 = new JPanel(); ... JPanel card2 = new JPanel(); ... //Create the panel that contains the "cards". cards = new JPanel(new CardLayout()); cards.add(card1, BUTTONPANEL); cards.add(card2, TEXTPANEL); To add a component to a container that a CardLayout object manages, specify a string that identifies the component being added. For example, in this demo, the first panel has the string "Card with JButtons", and the second panel has the string "Card with JTextField". In this demo those
strings are also used in the combo box. To choose which component a CardLayout object shows, put additional code in your code example: //Where the GUI is assembled: //Put the JComboBox in a JPanel to get a nicer look. JPanel comboBoxPane = new JPanel(); //use FlowLayout String comboBoxItems[] = { BUTTONPANEL, TEXTPANEL }; JComboBox cb = new JComboBox(comboBoxItems); cb.setEditable(false); cb.addItemListener(this); comboBoxPane.add(cb); ... pane.add(comboBoxPane, BorderLayout.PAGE_START); pane.add(cards, BorderLayout.CENTER); ... //Method came from the ItemListener class implementation, //contains functionality to process the combo box item selecting public void itemStateChanged(ItemEvent evt) { CardLayout cl = (CardLayout)(cards.getLayout()); cl.show(cards, (String)evt.getItem()); } This example shows that to use the show method of the CardLayout class, you must set the currently visible component. The first argument in the show method is the container the CardLayout controls — that is, the container of the components the CardLayout manages. The second argument is the
string that identifies the component to show. This string is the same string that was used when adding the component to the container.
The CardLayout API The following table lists the CardLayout class methods that are used to choose a component. For each method, the first argument is the container for which the CardLayout is the layout manager (the container of the cards the CardLayout controls). 863
Method
Purpose
first (Container parent)
Flips to the first card of the container.
next (Container parent)
Flips to the next card of the container. If the currently visible card is the last one, this method flips to the first card in the layout.
previous (Container parent)
Flips to the previous card of the container. If the currently visible card is the first one, this method flips to the last card in the layout.
last (Container parent)
Flips to the last card of the container.
show (Container parent, String name)
Flips to the component that was added to this layout with the specified name, using the addLayoutComponent method.
Examples that Use CardLayout Only one example in this trail uses CardLayout, and this is the CardLayoutDemo. Generally, our examples use tabbed panes instead of CardLayout, since a tabbed pane provides its own GUI.
How to Use FlowLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager. The FlowLayout class provides a very simple layout manager that is used, by default, by the JPanel objects. The following figure represents a snapshot of an application that uses the flow layout:
Click the Launch button to run FlowLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The complete code of this demo is in the FlowLayoutDemo.java file. The FlowLayout class puts components in a row, sized at their preferred size. If the horizontal space in the container is too small to put all the components in one row, the FlowLayout class uses multiple rows. If the container is wider than necessary for a row of components, the row is, by default, centered horizontally within the container. To specify that the row is to aligned either to the left or right, use a FlowLayout constructor that takes an alignment argument. Another constructor of the FlowLayout class specifies how much vertical or horizontal padding is put around the components. The code snippet below creates a FlowLayout object and the components it manages. 864
Select either the Left to Right or Right to Left option and click the Apply orientation button to set up the component's orientation. The following code snippet applies the Left to Right components orientation to the experimentLayout. compsToExperiment.setComponentOrientation( ComponentOrientation.LEFT_TO_RIGHT);
The FlowLayout API The following table lists constructors of the FlowLayout class. Constructor
Purpose
FlowLayout()
Constructs a new FlowLayout object with a centered alignment and horizontal and vertical gaps with the default size of 5 pixels.
FlowLayout(int align)
Creates a new flow layout manager with the indicated alignment and horizontal and vertical gaps with the default size of 5 pixels. The alignment argument can be FlowLayout.LEADING, FlowLayout.CENTER, or FlowLayout.TRAILING. When the FlowLayout object controls a container with a left-to right component orientation (the default), the LEADING value specifies the components to be left-aligned and the TRAILING value specifies the components to be right-aligned.
FlowLayout (int align, int hgap, int vgap)
Creates a new flow layout manager with the indicated alignment and the indicated horizontal and vertical gaps. The hgap and vgap arguments specify the number of pixels to put between components.
Examples that Use FlowLayout The following table lists code examples that use the FlowLayout class and provides links to related sections. Example Where Described Notes FlowLayoutDemo This page Sets up a content pane to use FlowLayout. If you set the RIGHT_TO_LEFT constant to true and recompile, you can see how FlowLayout handles a container that has a right-toleft component orientation. CardLayoutDemo How to Use Centers a component nicely in the top part of a CardLayout BorderLayout, and puts the component in a JPanel that uses a FlowLayout. ButtonDemo How to Use Buttons, Uses the default FlowLayout of a JPanel. Check Boxes, and 865
Radio Buttons TextInputDemo How to Use
Formatted Text Fields
Uses a panel with a right-aligned FlowLayout presenting two buttons.
How to Use GridBagLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager. Here is a picture of an example that uses GridBagLayout.
Click the Launch button to run GridBagLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The code for GridBagDemo is in GridBagLayoutDemo.java. GridBagLayout is one of the most flexible — and complex — layout managers the Java platform provides. A GridBagLayout places components in a grid of rows and columns, allowing specified
components to span multiple rows or columns. Not all rows necessarily have the same height. Similarly, not all columns necessarily have the same width. Essentially, GridBagLayout places components in rectangles (cells) in a grid, and then uses the components' preferred sizes to determine how big the cells should be. The following figure shows the grid for the preceding applet. As you can see, the grid has three rows and three columns. The button in the second row spans all the columns; the button in the third row spans the two right columns.
866
If you enlarge the window as shown in the following figure, you will notice that the bottom row, which contains Button 5, gets all the new vertical space. The new horizontal space is split evenly among all the columns. This resizing behavior is based on weights the program assigns to individual components in the GridBagLayout. You will also notice that each component takes up all the available horizontal space — but not (as you can see with button 5) all the available vertical space. This behavior is also specified by the program.
The way the program specifies the size and position characteristics of its components is by specifying constraints for each component. The preferred approach to set constraints on a component is to use the Container.add variant, passing it a GridBagConstraints object, as demonstrated in the next sections. The following sections explain the constraints you can set and provide examples.
Specifying Constraints The following code is typical of what goes in a container that uses a GridBagLayout. You will see a more detailed example in the next section. JPanel pane = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); //For each component to be added to this container: //...Create the component... //...Set instance variables in the GridBagConstraints instance... pane.add(theComponent, c);
As you might have guessed from the above example, it is possible to reuse the same GridBagConstraints instance for multiple components, even if the components have different constraints. However, it is recommended that you do not reuse GridBagConstraints, as this can very easily lead to you introducing subtle bugs if you forget to reset the fields for each new instance. Note: The following discussion assumes that the GridBagLayout controls a container that has a leftto-right component orientation. 867
You can set the following GridBagConstraints instance variables: gridx, gridy
Specify the row and column at the upper left of the component. The leftmost column has address gridx=0 and the top row has address gridy=0. Use GridBagConstraints.RELATIVE (the default value) to specify that the component be placed just to the right of (for gridx) or just below (for gridy) the component that was added to the container just before this component was added. We recommend specifying the gridx and gridy values for each component rather than just using GridBagConstraints.RELATIVE; this tends to result in more predictable layouts. gridwidth, gridheight
Specify the number of columns (for gridwidth) or rows (for gridheight) in the component's display area. These constraints specify the number of cells the component uses, not the number of pixels it uses. The default value is 1. Use GridBagConstraints.REMAINDER to specify that the component be the last one in its row (for gridwidth) or column (for gridheight). Use GridBagConstraints.RELATIVE to specify that the component be the next to last one in its row (for gridwidth) or column (for gridheight). We recommend specifying the gridwidth and gridheight values for each component rather than just using GridBagConstraints.RELATIVE and GridBagConstraints.REMAINDER; this tends to result in more predictable layouts. Note: GridBagLayout does not allow components to span multiple rows unless the component is in the leftmost column or you have specified positive gridx and gridy values for the component. fill
Used when the component's display area is larger than the component's requested size to determine whether and how to resize the component. Valid values (defined as GridBagConstraints constants) include NONE (the default), HORIZONTAL (make the component wide enough to fill its display area horizontally, but do not change its height), VERTICAL (make the component tall enough to fill its display area vertically, but do not change its width), and BOTH (make the component fill its display area entirely). ipadx, ipady
Specifies the internal padding: how much to add to the size of the component. The default value is zero. The width of the component will be at least its minimum width plus ipadx*2 pixels, since the padding applies to both sides of the component. Similarly, the height of the component will be at least its minimum height plus ipady*2 pixels. insets
Specifies the external padding of the component -- the minimum amount of space between the component and the edges of its display area. The value is specified as an Insets object. By default, each component has no external padding. anchor
Used when the component is smaller than its display area to determine where (within the area) to place the component. Valid values (defined as GridBagConstraints constants) are CENTER (the default), PAGE_START, PAGE_END, LINE_START, LINE_END, FIRST_LINE_START, FIRST_LINE_END, LAST_LINE_END, and LAST_LINE_START. 868
Here is a picture of how these values are interpreted in a container that has the default, left-to-right component orientation. ------------------------------------------------|FIRST_LINE_START PAGE_START FIRST_LINE_END| | | | | |LINE_START CENTER LINE_END| | | | | |LAST_LINE_START PAGE_END LAST_LINE_END| -------------------------------------------------
Version note: The PAGE_* and *LINE_* constants were introduced in 1.4. Previous releases require values named after points of the compass. For example, NORTHEAST indicates the top-right part of the display area. We recommend that you use the new constants, instead, since they enable easier localization. weightx, weighty
Specifying weights is an art that can have a significant impact on the appearance of the components a GridBagLayout controls. Weights are used to determine how to distribute space among columns (weightx) and among rows (weighty); this is important for specifying resizing behavior. Unless you specify at least one non-zero value for weightx or weighty, all the components clump together in the center of their container. This is because when the weight is 0.0 (the default), the GridBagLayout puts any extra space between its grid of cells and the edges of the container. Generally weights are specified with 0.0 and 1.0 as the extremes: the numbers in between are used as necessary. Larger numbers indicate that the component's row or column should get more space. For each column, the weight is related to the highest weightx specified for a component within that column, with each multicolumn component's weight being split somehow between the columns the component is in. Similarly, each row's weight is related to the highest weighty specified for a component within that row. Extra space tends to go toward the rightmost column and bottom row. The next section discusses constraints in depth, in the context of explaining how the example program works.
The Example Explained Here, again, is a picture of the GridBagLayoutDemo application.
869
Click the Launch button to run GridBagLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
The following code creates the GridBagLayout and the components it manages. You can find the entire source file in GridBagLayoutDemo.java. JButton button; pane.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); if (shouldFill) { //natural height, maximum width c.fill = GridBagConstraints.HORIZONTAL; } button = new JButton("Button 1"); if (shouldWeightX) { c.weightx = 0.5; } c.fill = GridBagConstraints.HORIZONTAL; c.gridx = 0; c.gridy = 0; pane.add(button, c); button = new JButton("Button 2"); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 0.5; c.gridx = 1; c.gridy = 0; pane.add(button, c); button = new JButton("Button 3"); c.fill = GridBagConstraints.HORIZONTAL; c.weightx = 0.5; c.gridx = 2; c.gridy = 0; pane.add(button, c); button = new JButton("Long-Named Button 4"); c.fill = GridBagConstraints.HORIZONTAL; c.ipady = 40; //make this component tall c.weightx = 0.0; c.gridwidth = 3; c.gridx = 0; c.gridy = 1; pane.add(button, c); button = new JButton("5"); c.fill = GridBagConstraints.HORIZONTAL; c.ipady = 0; //reset to default c.weighty = 1.0; //request any extra vertical space
870
c.anchor = GridBagConstraints.PAGE_END; //bottom of space c.insets = new Insets(10,0,0,0); //top padding c.gridx = 1; //aligned with button 2 c.gridwidth = 2; //2 columns wide c.gridy = 2; //third row pane.add(button, c);
This example uses one GridBagConstraints instance for all the components the GridBagLayout manages, however in real-life situations it is recommended that you do not reuse GridBagConstraints, as this can very easily lead to you introducing subtle bugs if you forget to reset the fields for each new instance. Just before each component is added to the container, the code sets (or resets to default values) the appropriate instance variables in the GridBagConstraints object. It then adds the component to its container, specifying the GridBagConstraints object as the second argument to the add method. For example, to make button 4 be extra tall, the example has this code: c.ipady = 40;
And before setting the constraints of the next component, the code resets the value of ipady to the default: c.ipady = 0;
If a component's display area is larger than the component itself, then you can specify whereabouts in the display area the component will be displayed by using the GridBagConstraints.anchor constraint. The anchor constraint's values can be absolute (north, south, east, west, and so on), or orientation-relative (at start of page, at end of line, at the start of the first line, and so on), or relative to the component's baseline. For a full list of the possible values of the anchor constraint, including baseline-relative values,see the API documentation for GridBagConstraints.anchor. You can see in the code extract above that Button 5 specifies that it should be displayed at the end of the display area by setting an anchor at GridBagConstraints.PAGE_END.
Note: The Tutorial's examples used to specify the constraints object a different way, which you might see in other programs as well. Rather than specifying the constraints with the add method, our examples used to invoke the setConstraints method on the GridBagLayout object. For example: GridBagLayout gridbag = new GridBagLayout(); pane.setLayout(gridbag); ... gridbag.setConstraints(button, c); pane.add(button); However, we recommend you use the Container.add method since it makes for cleaner code than if you were to use setConstraints.
Here is a table that shows all the constraints for each component in GridBagLayoutDemo's content pane. Values that are not the default are marked in boldface. Values that are different from those in the previous table entry are marked in italics. Component Constraints All components ipadx = 0 fill = GridBagConstraints.HORIZONTAL
GridBagLayoutDemo has two components that span multiple columns (buttons 4 and 5). To make button 4 tall, we added internal padding (ipady) to it. To put space between buttons 4 and 5, we used insets to add a minimum of 10 pixels above button 5, and we made button 5 hug the bottom edge of its cell. All the components in the pane container are as wide as possible, given the cells that they occupy. The program accomplishes this by setting the GridBagConstraints fill instance variable to GridBagConstraints.HORIZONTAL, leaving it at that setting for all the components. If the program did not specify the fill, the buttons would be at their natural width, like this:
When you enlarge GridBagLayoutDemo's window, the columns grow proportionately. This is because each component in the first row, where each component is one column wide, has weightx = 1.0. The actual value of these components' weightx is unimportant. What matters is that all the components, and consequently, all the columns, have an equal weight that is greater than 0. If no component managed by the GridBagLayout had weightx set, then when the components' container was made wider, the components would stay clumped together in the center of the container, like this:
872
If the container is given a size that is smaller or bigger than the prefered size, then any space is distributed according to the GridBagContainer weights. Note that if you enlarge the window, the last row is the only one that gets taller. This is because only button 5 has weighty greater than zero.
The GridBagLayout API The GridBagLayout and GridBagConstraints classes each have only one constructor, with no arguments. Instead of invoking methods on a GridBagConstraints object, you manipulate its instance variables, as described in Specifying Constraints. Generally, the only method you invoke on a GridBagLayout object is setConstraints, as demonstrated in The Example Explained.
Examples that Use GridBagLayout You can find examples of using GridBagLayout throughout this tutorial. The following table lists a few. Example Where Described Notes GridBagLayoutDemo This section Uses many features — weights, insets, internal padding, horizontal fill, exact cell positioning, multi-column cells, and anchoring (component positioning within a cell). TextSamplerDemo Using Text Aligns two pairs of labels and text fields, plus adds a label Components across the full width of the container. ContainerEventDemo How to Write a Positions five components within a container, using Container Listener weights, fill, and relative positioning.
873
How to Use GridLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager. The following figure represents a snapshot of an application that uses the GridLayout class.
Click the Launch button to run GridLayoutDemo using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index. The complete code of this demo is in the GridLayoutDemo.java file. A GridLayout object places components in a grid of cells. Each component takes all the available space within its cell, and each cell is exactly the same size. If the GridLayoutDemo window is resized, the GridLayout object changes the cell size so that the cells are as large as possible, given the space available to the container. The code snippet below creates the GridLayout object and the components it manages. GridLayout experimentLayout = new GridLayout(0,2); ... compsToExperiment.setLayout(experimentLayout); compsToExperiment.add(new JButton("Button 1")); compsToExperiment.add(new JButton("Button 2")); compsToExperiment.add(new JButton("Button 3")); compsToExperiment.add(new JButton("Long-Named Button 4")); compsToExperiment.add(new JButton("5")); The constructor of the GridLayout class creates an instance that has two columns and as many rows
as necessary. Use combo boxes to set up how much vertical or horizontal padding is put around the components. Then click the Apply gaps button. The following code snippet shows how your selection is processed by using the setVgap and setHgap methods of the GridLayout class: applyButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e){
874
//Get the horizontal gap value String horGap = (String)horGapComboBox.getSelectedItem(); //Get the vertical gap value String verGap = (String)verGapComboBox.getSelectedItem(); //Set up the horizontal gap value experimentLayout.setHgap(Integer.parseInt(horGap)); //Set up the vertical gap value experimentLayout.setVgap(Integer.parseInt(verGap)); //Set up the layout of the buttons experimentLayout.layoutContainer(compsToExperiment); } });
The GridLayout API The following table lists constructors of the GridLayout class that specify the number of rows and columns.
Constructor GridLayout(int rows, int cols)
The GridLayout class constructors Purpose Creates a grid layout with the specified number of rows and columns. All components in the layout are given equal size. One, but not both, of rows and cols can be zero, which means that any number of objects can be placed in a row or in a column. Creates a grid layout with the specified number of rows and columns.
GridLayout(int rows, In addition, the horizontal and vertical gaps are set to the specified int cols, int hgap, int values. Horizontal gaps are places between each of columns. Vertical vgap)
gaps are placed between each of the rows. The GridLayout class has two constructors:
Examples that Use GridLayout The following table lists code examples that use the GridLayout class and provides links to related sections. Example Where Described Notes GridLayoutDemo This page Uses a 2-column grid. ComboBoxDemo2 How to Use Combo One of many examples that use a 1x1 grid to make a Boxes component as large as possible. LabelDemo How to Use Labels Uses a 3-row grid. DragPictureDemo Introduction to DnD Uses a 4-row grid to present 12 components that display photographs.
875
How to Use GroupLayout roupLayout is a layout manager that was developed for GUI builders such as Matisse, the GUI
builder provided with the NetBeans IDE. Although the layout manager was originally designed to suit the GUI builder needs, it also works well for manual coding. This discussion will teach you how GroupLayout works and show you how you can use GroupLayout to build GUIs, whether you choose to use a GUI builder like Matisse or write your own code.
Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager.
Design Principle: Independent Dimensions GroupLayout works with the horizontal and vertical layouts separately. The layout is defined for
each dimension independently. You do not need to worry about the vertical dimension when defining the horizontal layout, and vice versa, as the layout along each axis is totally independent of the layout along the other axis. When focusing on just one dimension, you only have to solve half the problem at one time. This is easier than handling both dimensions at once. This means, of course, that each component needs to be defined twice in the layout. If you forget to do this, GroupLayout will generate an exception.
Layout Organization: Hierarchical Groups GroupLayout uses two types of arrangements -- sequential and parallel, combined with hierarchical
composition. 1. With sequential arrangement, the components are simply placed one after another, just like BoxLayout or FlowLayout would do along one axis. The position of each component is defined as being relative to the preceding component. 2. The second way places the components in parallel—on top of each other in the same space. They can be baseline-, top-, or bottom-aligned along the vertical axis. Along the horizontal axis, they can be left-, right-, or center-aligned if the components are not all the same size. Usually, components placed in parallel in one dimension are in a sequence in the other, so that they do not overlap. What makes these two arrangements powerful is that they can be nested hierarchically. For this purpose GroupLayout defines layout groups. A group is either sequential or parallel and may contain components, other groups, and gaps (discussed below). The size of a sequential group is the sum of the sizes of the contained elements, and the size of a parallel group corresponds to the size of the largest element (although, depending on the elements and where the baseline lands, the size of a baseline-aligned group may be a bit larger than the largest element). 876
Defining a layout means defining how the components should be grouped by combining the sequential and parallel arrangements. Let us use a simple example to see how it works in practice.
An Example Let us start with something simple, just three components in a row:
We will express this layout using groups. Starting with the horizontal axis it is easy to see there is a sequential group of 3 components arranged from left to right. Along the vertical axis there is a parallel group of the same 3 components with the same location, size, and baseline:
In pseudo code, the layout specification might look like this (the real code is in the Writing Code section below): horizontal layout = sequential group { c1, c2, c3 } vertical layout = parallel group (BASELINE) { c1, c2, c3 }
This illustrates a principle mentioned earlier: components grouped sequentially in one dimension usually form a parallel group in the other dimension. Now let us add one more component, C4, left-aligned with C3:
Along the horizontal axis the new component occupies the same horizontal space as C3 so that it forms a parallel group with C3. Along the vertical axis C4 forms a sequential group with the original parallel group of the three components.
In pseudo code, the layout specification now looks like this: horizontal layout = sequential group { c1, c2, parallel group (LEFT) { c3, c4 } } vertical layout = sequential group { parallel group (BASELINE) { c1, c2, c3 }, c4 }
877
Now you understand the most important aspects of designing layouts with GroupLayout. There are just a few more details to explain: how to add gaps, how to define size and resize behavior, how to define justified layout, and how to write real code.
Gaps A gap can be thought of as an invisible component of a certain size. Gaps of arbitrary size can be added to groups just like components or other groups. Using gaps you can precisely control the distance between components or from the container border. GroupLayout also defines automatic gaps that correspond to preferred distances between
neighboring components (or between a component and container border). The size of such a gap is computed dynamically based on the look and feel the application is using (the LayoutStyle class is used for this). There are two advantages to using automatic (preferred) gaps: you do not have to specify the pixel sizes of the gaps, and they automatically adjust to the look and feel the UI runs with, reflecting the actual look and feel guidelines. GroupLayout distinguishes between (a) the preferred gap between two components and (b) the
preferred gap between a component and the container border. There are corresponding methods in the GroupLayout API for adding these gaps (addPreferredGap and addContainerGap). There are three types of component gaps: related, unrelated and indented. The LayoutStyle.ComponentPlacement enum defines corresponding constants to be used as parameters of the addPreferredGap method: RELATED, UNRELATED and INDENT. The difference between related and unrelated gaps is just in size—the distance between unrelated components is a bit bigger. Indented represents a preferred horizontal distance of two components when one of them is positioned underneath the second with an indent.
As mentioned above, GroupLayout can insert gaps automatically—if you do not add your own gaps explicitly, it adds the related preferred gaps for you. This is not the default behavior, however. You have to turn this feature on by invoking setAutoCreateGaps(true) and setAutoCreateContainerGaps(true) on the GroupLayout. Then you will get correct spacing automatically.
Writing Code Now, let us take a look at the actual code to create the layout described above. Let us assume we have a container named panel and the same four components already presented (c1, c2, c3, and c4). First, we create a new GroupLayout object and associate it with the panel: GroupLayout layout = new GroupLayout(panel); panel.setLayout(layout);
We specify automatic gap insertion: layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true);
878
Then, we define the groups and add the components. We establish a root group for each dimension using the setHorizontalGroup and setVerticalGroup methods. Groups are created via createSequentialGroup and createParallelGroup methods. Components are added to groups by using the addComponent method. layout.setHorizontalGroup( layout.createSequentialGroup() .addComponent(c1) .addComponent(c2) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(c3) .addComponent(c4)) ); layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(c1) .addComponent(c2) .addComponent(c3)) .addComponent(c4) );
You can specify the alignment for parallel groups. It can be one of the following constants defined in the GroupLayout.Alignment enum: LEADING, TRAILING, CENTER, and BASELINE. These constants are used for both dimensions and depend on whether the component orientation is left-to-right or right-to-left (top-to-bottom or bottom-to-top). For example, if the horizontal (vertical) component orientation is left-to-right (top-to-bottom) LEADING means left (top) while TRAILING means right (bottom). CENTER means "centered" in both dimensions. If you do not specify the alignment, LEADING will be used. The BASELINE alignment is valid only in the vertical dimension.
Note: Alignment in the layout of a group only has meaning for components of different sizes. Components of the same size will be automatically aligned for each of the GroupLayout.Alignment constants.
Some comments about the code:
You do not need to add the component directly to the container—that is done for you implicitly when using one of the addComponent methods. Note the chained calls of the addComponent methods used to fill the groups. The addComponent method always returns the group on which it is called. Thanks to this you do not need to use local variables to hold the groups. It is a good idea to indent the code so it is easy to see the hierarchical structure of the groups. Give each component a new line, add one level of indent for each new group in the hierarchy. A good source editor will help you with pairing the parenthesis to close the createXXXGroup methods. By following these simple rules, it is easier to add a new component or remove an existing one.
Component Size and Resizability There is no limit on the number of resizable components in a layout.
879
The size of each component in a GroupLayout is constrained by three values; minimum size, preferred size and maximum size. These sizes control how the component resizes within the layout. The GroupLayout.addComponent(...) method allows the size constraints to be specified. If not specified explicitly, the layout asks the component for its default sizes (by using the component's getMinimumSize(), getPreferredSize() and getMaximumSize() methods). You do not need to specify anything for most of the components, like making JTextField resizable or JButton fixed, because the components themselves have the desired resizing behavior as default. On the other hand you can override the default behavior. For example you can make a JTextField fixed or JButton resizable. GroupLayout defines constants that provide precise control over resize behavior. They can be used as parameters in the addComponent(Component comp, int min, int pref, int max) method.
Here are two examples: 1. To force a component to be resizable (allow shrinking and growing): 2.
This allows the component to resize between zero size (minimum) to any size (Short.MAX_VALUE as maximum size means "infinite"). If we wanted the component not to shrink below its default minimum size, we would use GroupLayout.DEFAULT_SIZE instead of 0 in the second parameter. 3. To make a component fixed size (suppress resizing): 4.
In these examples the initial size of the component is not altered, its default size is the component's preferred size. If we wanted a specific size for the component, we would specify it in the second parameter instead of using GroupLayout.DEFAULT_SIZE. Resizable gaps Specifying size and resizability applies to gaps as well, including the preferred ones. For example, you can specify a preferred gap between two components that acts like a spring pushing the components away from each other (to the opposite sides of the container). The preferred distance of the two components is only used as the minimum size of the gap. See the following snippet: layout.createSequentialGroup() .addComponent(c1) .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(c2);
Sizing in Parallel Groups Resizable elements placed in a parallel group are stretched to fill the space of the group determined by the largest element in the group, so they end up aligned with the same size. GroupLayout also provides control over whether the enclosing parallel group itself should resize. If group resizing is suppressed, it prevents the contained elements from growing over the preferred size of the group.
880
This way you can make a block of components align on both sides, or constrain individual components to have the same size. Let us try to achieve the same size for two components from our example (c3 and c4 in the horizontal dimension): layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) .addComponent(c3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(c4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE);
The underlying mechanism works as follows: 1. The size of the parallel group is set to the preferred size of the largest element; so to the preferred size of c4 in our example. 2. Resizable elements are stretched to the size of the group. In our example, only c3 is effectively stretched, the size of c4 already corresponds to the size of the group. As a result, c3 and c4 would have the same width. The components would not resize further because the parallel group itself is not resizable (the second parameter of the createParallelGroup method, above, is false).
Question for attentive readers: Why do we define both components in the parallel group as resizable in this example? It seems enough to have just c3 resizable since c4 is not stretched anyway... The answer is: because of platform and localization independence. Otherwise we would have to rely on that c4 component always being bigger than c3. But this may change when the application runs on different platform or is translated to another language. By having both components resizeable they adjust to each other, no matter which one is bigger at a given moment.
Making Components the Same Size The previous case is special because the components are in the same parallel group. But what if we wanted unrelated components to have the same size? Clearly, the same size cannot always be ensured by grouping. The OK and Cancel buttons in a row at the bottom of a dialog are a good example. For this purpose GroupLayout provides a linkSize method. This method allows the size of arbitrary components to be linked regardless of where they are placed. The resulting size of the linked components is set according to the largest component. For example: layout.linkSize(SwingConstants.HORIZONTAL, c3, c4);
In this example, the size is linked selectively for the horizontal dimension.
881
Runtime Changes to Your GUI There are two important methods that you can use to make changes to your GUI at runtime, replace() and setHonorsVisibility(). Using these two methods, you can exchange components or change the visibility of components at runtime and have the GUI rearrange itself accordingly. replace(Component existingComponent, Component newComponent) replaces an existing
component with a new one. One of the common operations needed for dynamic layouts is the ability to replace components like this. For example, perhaps a check box toggles between a component displaying a graph or a tree. GroupLayout makes this scenario simple with the replace() method. You can swap components without recreating all the groups. Another common operation in user interfaces is to dynamically change the visibility of components. Perhaps components are shown only as a user completes earlier portions of a form. To avoid components shuffling around in such a scenario, space should be taken up regardless of the visibility of the components. GroupLayout offers two ways to configure how invisible components are treated. The setHonorsVisibility(boolean) method globally sets how invisible components are handled. A value of true, the default, indicates invisible components are treated as if they are not there. On the other hand, a value of false provides space for invisible components, treating them as though they were visible. The setHonorsVisibility(Component,Boolean) method can be used to configure the behavior at the level of a specific component. To determine how visibility is handled, GroupLayout first checks if a value has been specified for the Component, if not, it checks the setting of the global property.
Some history: GroupLayout in the Java Standard Edition 6 consists of three distinct bodies of work: the ability to get the baseline for a component, the ability to get the preferred gap between components (LayoutStyle), and GroupLayout. This work was originally done as an open source project at http://swing-layout.dev.java.net NetBeans 5.0 supports GroupLayout by way of the swing-layout project. Because of the success of this work, all three portions have been rolled into GroupLayout in Java Standard Edition version 6. The main difference between the GroupLayout in Java SE 6 and swing-layout is in the package name and method names. NetBeans 5.5 provides the ability to target either the GroupLayout in Java SE 6, or the GroupLayout in swing-layout. Which version NetBeans targets is determined by the version of the Java platform your project targets. A project targeting Java SE 6 uses the GroupLayout in Java SE, otherwise GroupLayout in swing-layout is used.
A GroupLayout Example Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager. As an example of GUI creation with GroupLayout, let us create a layout for this "Find" dialog box:
882
Horizontal layout Examining the horizontal dimension from left to right, we can see there are 3 groups in a sequence. The first one is actually not a group, just a component -- the label. The second one is a group containing the text field and the check boxes (we will decompose it later). And the third is a group of the two buttons. As illustrated here:
Let us sketch out the sequential group in code. Note that GroupLayout.Alignment.LEADING corresponds to left alignment in the horizontal dimension. Also note we do not specify gaps, assuming the gap auto-insertion feature is turned on. layout.setHorizontalGroup(layout.createSequentialGroup() .addComponent(label) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)) );
Now let us decompose the group in the middle. This is the hardest one. There is a text field in parallel with a sequence of two parallel groups each containing two check boxes. See the following illustration:
Let us add the corresponding code: layout.setHorizontalGroup(layout.createSequentialGroup() .addComponent(label) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(textField) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(caseCheckBox) .addComponent(wholeCheckBox)) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(wrapCheckBox)
We want the text field to be resizable, but that happens automatically since JTextField returns the right maximum size by default. The remaining group on the right is trivial: it contains just two buttons. Here is the code: layout.setHorizontalGroup(layout.createSequentialGroup() .addComponent(label) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(textField) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(caseCheckBox) .addComponent(wholeCheckBox)) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(wrapCheckBox) .addComponent(backCheckBox)))) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(findButton) .addComponent(cancelButton)) );
And finally, we would like the buttons to be always the same size, so let us link them: layout.linkSize(SwingConstants.HORIZONTAL, findButton, cancelButton);
Now we are done with the horizontal dimension. Let us switch to the vertical dimension. From now, we will only need to think about the y axis.
Vertical layout In the vertical dimension, we examine the layout from top to bottom. We definitely want all the components on the first line aligned on the baseline. So along the vertical axis there is a sequence of the baseline group, followed by a group of the remaining components. See the following picture.
Let us sketch out the code. First, we need to define two parallel groups. Note that GroupLayout.Alignment.LEADING corresponds to the top alignment in the vertical dimension. layout.setVerticalGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)) );
We can fill the baseline group right away: layout.setVerticalGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
Now let us look at the bottom group. Note the Cancel button is not on a shared baseline with the check boxes; it is aligned at the top. So the second parallel group comprises the button and a sequential group of two baseline groups with check boxes:
So, we have created a complete layout, including resize behavior, without specifying a single number in pixels—a true cross platform layout. Note that we do not need to specify gaps between components, we get correct spacing automatically and according to the look and feel guidelines. Here is the complete code for the Find dialog's layout: GroupLayout layout = new GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setAutocreateGaps(true); layout.setAutocreateContainerGaps(true); layout.setHorizontalGroup(layout.createSequentialGroup() .addComponent(label) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(textField) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(caseCheckBox) .addComponent(wholeCheckBox)) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(wrapCheckBox) .addComponent(backCheckBox)))) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addComponent(findButton) .addComponent(cancelButton)) ); layout.linkSize(SwingConstants.HORIZONTAL, findButton, cancelButton); layout.setVerticalGroup(layout.createSequentialGroup()
Here is the complete Find.java file. You can compile and run it. Try resizing the dialog horizontally to see how the layout automatically adjusts to the new size.
How to Use SpringLayout Note: This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the GroupLayout layout manager combined with a builder tool to lay out your GUI. One such builder tool is the NetBeans IDE. Otherwise, if you want to code by hand and do not want to use GroupLayout, then GridBagLayout is recommended as the next most flexible and powerful layout manager. The SpringLayout class was added in JDK version 1.4 to support layout in GUI builders. SpringLayout is a very flexible layout manager that can emulate many of the features of other layout managers. SpringLayout is, however, very low-level and as such you really should only use it with a GUI builder, rather than attempting to code a spring layout manager by hand. This section begins with a simple example showing all the things you need to remember to create your first spring layout — and what happens when you forget them! Later it presents utility methods that let you lay out components in a couple of different types of grids. Here are pictures of some of the layouts we will cover:
886
How Spring Layouts Work Spring layouts do their job by defining directional relationships, or constraints, between the edges of components. For example, you might define that the left edge of one component is a fixed distance (5 pixels, say) from the right edge of another component. In a SpringLayout, the position of each edge is dependent on the position of just one other edge. If a constraint is subsequently added to create a new binding for an edge, the previous binding is discarded and the edge remains dependent on a single edge. Unlike many layout managers, SpringLayout does not automatically set the location of the components it manages. If you hand-code a GUI that uses SpringLayout, remember to initialize component locations by constraining the west/east and north/south locations. Depending on the constraints you use, you may also need to set the size of the container explicitly. Components define edge properties, which are connected by Spring instances. Each spring has four properties — its minimum, preferred, and maximum values, and its actual (current) value. The springs associated with each component are collected into a SpringLayout.Constraints object. An instance of the Spring class holds three properties that characterize its behavior: the minimum, preferred, and maximum values. Each of these properties may be involved in defining its fourth, value, property based on a series of rules. An instance of the Spring class can be visualized as a mechanical spring that provides a corrective force as the spring is compressed or stretched away from its preferred value. This force is modelled as linear function of the distance from the preferred value, but with two different constants -- one for the compressional force and one for the tensional one. Those constants are specified by the minimum and maximum values of the spring such that a spring at its minimum value produces an equal and opposite force to that which is created when it is at its maximum value. The difference between the preferred and minimum values, therefore, represents the ease with which the spring can be compressed. The difference between its maximum and preferred values indicates the ease with which the Spring can be extended. Based on this, a SpringLayout can be visualized as a set of objects that are connected by a set of springs on their edges.
887
Example: SpringDemo This section takes you through the typical steps of specifying the constraints for a container that uses SpringLayout. The first example, SpringDemo1.java, is an extremely simple application that features a label and a text field in a content pane controlled by a spring layout. Here is the relevant code: public class SpringDemo1 { public static void main(String[] args) { ... Container contentPane = frame.getContentPane(); SpringLayout layout = new SpringLayout(); contentPane.setLayout(layout); contentPane.add(new JLabel("Label: ")); contentPane.add(new JTextField("Text field", 15)); ... frame.pack(); frame.setVisible(true); } }
Click the Launch button to run SpringDemo1 using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
Here is what the GUI looks like when it first comes up:
Here is what it looks like when it is resized to be bigger:
Obviously, we have some problems. Not only does the frame come up way too small, but even when it is resized the components are all located at (0,0). This happens because we have set no springs specifying the components' positions and the width of the container. One small consolation is that at least the components are at their preferred sizes — we get that for free from the default springs created by SpringLayout for each component. Our next example, SpringDemo2.java, improves the situation a bit by specifying locations for each component.Click the Launch button to run SpringDemo2 using Java™ Web Start (download JDK 6). Alternatively, to compile and run the example yourself, consult the example index.
In this example, we will specify that the components should appear in a single row, with 5 pixels between them. The following code specifies the location of the label: //Adjust constraints for the label so it's at (5,5). layout.putConstraint(SpringLayout.WEST, label,
888
5, SpringLayout.WEST, contentPane); layout.putConstraint(SpringLayout.NORTH, label, 5, SpringLayout.NORTH, contentPane); The first putConstraint call specifies that the label's left (west) edge should be 5 pixels from its container's left edge. This translates to an x coordinate of 5. The second putConstraint call sets up
a similar relationship between the top (north) edges of the label and its container, resulting in a y coordinate of 5. Here is the code that sets up the location of the text field: //Adjust constraints for the text field so it's at //(