Every container has a layout manager that is responsible for arranging its components . The container's setLayout method can be used to set a layout manager. Certain types of containers have default layout managers. For instance, the content pane of JFrame or JApplet uses BorderLayout , and JPanel uses FlowLayout .
The layout manager places the components in accordance with its own rules and property settings, and with the constraints associated with each component. Every layout manager has its own specific set of rules. For example, the FlowLayout manager places components in rows from left to right and starts a new row when the previous row is filled. The BorderLayout manager places components in the north, south, east, west, or center of the container. The GridLayout manager places components in a grid of cells in rows and columns from left to right in order.
Some layout managers have properties that can affect the sizing and location of the components in the container. For example, BorderLayout has properties called hgap (horizontal gap) and vgap (vertical gap) that determine the distance between components horizontally and vertically. FlowLayout has properties that can be used to specify the alignment (left, center, right) of the components and properties for specifying the horizontal or vertical gap between the components. GridLayout has properties that can be used to specify the horizontal or vertical gap between columns and rows and properties for specifying the number of rows and columns. These properties can be retrieved and set using their accessor and mutator methods .
The size of a component in a container is determined by many factors, such as:
The type of layout manager used by the container.
The layout constraints associated with each component.
The size of the container.
Certain properties common to all components (such as preferredSize , minimumSize , maximumSize , alignmentX , and alignmentY ).
The preferredSize property indicates the ideal size at which the component looks best. Depending on the rules of the particular layout manager, this property may or may not be considered . For example, the preferred size of a component is used in a container with a FlowLayout manager, but ignored if it is placed in a container with a GridLayout manager.
The minimumSize property specifies the minimum size at which the component is useful. For most GUI components, minimumSize is the same as preferredSize . Layout managers generally respect minimumSize more than preferredSize .
The maximumSize property specifies the maximum size needed by a component, so that the layout manager won't wastefully give space to a component that does not need it. For instance, BorderLayout limits the center component's size to its maximum size, and gives the space to edge components.
The alignmentX ( alignmentY ) property specifies how the component would like to be aligned relative to other components along the x -axis ( y -axis). This value should be a number between and 1 , where represents alignment along the origin, 1 is aligned the farthest away from the origin, 0.5 is centered, and so on. These two properties are used in the BoxLayout and OverlayLayout .
Java provides a variety of layout managers. You have learned how to use BorderLayout , FlowLayout , and GridLayout . The following sections introduce CardLayout , GridBagLayout , Null layout, BoxLayout , OverlayLayout , and SpringLayout .
Tip
If you set a new layout manager in a container, invoke the container's validate() method to force the container to again lay out the components. If you change the properties of a layout manager in a JFrame or JApplet , invoke the doLayout() method to force the container to again layout the components using the new layout properties. If you change the properties of a layout manager in a JPanel , invoke either doLayout() or revalidate() method to force it to again layout the components using the new layout properties, but it is better to use revalidate() . Note that validate() is a public method defined in java.awt.Container , revalidate() is a public method defined in javax.swing.JComponent , and doLayout() is a public method defined in java.awt.Container . |
CardLayout places components in the container as cards. Only one card is visible at a time, and the container acts as a stack of cards. The ordering of cards is determined by the container's own internal ordering of its component objects. You can specify the size of the horizontal and vertical gaps surrounding a stack of components in a CardLayout manager, as shown in Figure 28.2.
CardLayout defines a set of methods that allow an application to flip through the cards sequentially or to show a specified card directly, as shown in Figure 28.3.
To add a component into a container, use the add(Component c, String name ) method defined in the Container class interface. The String parameter, name , gives an explicit identity to the component in the container.
Listing 28.1 gives a program that creates two panels in a frame. The first panel uses CardLayout to hold six labels for displaying images. The second panel uses FlowLayout to group four buttons named First, Next , Previous, and Last, and a combo box labeled Image , as shown in Figure 28.4.
These buttons control which image will be shown in the CardLayout panel. When the user clicks the button named First, for example, the first image in the CardLayout panel appears. The combo box enables the user to directly select an image.
1 import java.awt.*; 2 import java.awt.event.*; 3 import javax.swing.*; 4 5 public class ShowCardLayout extends JApplet { 6 private CardLayout cardLayout = new CardLayout( 20 , 10 ); 7 private JPanel cardPanel = new JPanel(cardLayout) ; 8 private JButton jbtFirst, jbtNext, jbtPrevious, jbtLast; 9 private JComboBox jcboImage; 10 private final int NUM_OF_FLAGS = 6 ; 11 12 public ShowCardLayout() { 13 cardPanel.setBorder( 14 new javax.swing.border.LineBorder( Color .red)); 15 16 // Add 9 labels for displaying images into cardPanel 17 for ( int i = 1 ; i <= NUM_OF_FLAGS; i++) { 18 JLabel label = 19 new JLabel( new ImageIcon( "image/flag" + i + ".gif" )); 20 cardPanel.add(label, String.valueOf(i)); 21 } 22 23 // Panel p to hold buttons and a combo box 24 JPanel p = new JPanel(); 25 p.add(jbtFirst = new JButton( "First" )); 26 p.add(jbtNext = new JButton( "Next" )); 27 p.add(jbtPrevious= new JButton( "Previous" )); 28 p.add(jbtLast = new JButton( "Last" )); 29 p.add( new JLabel( "Image" )); 30 p.add(jcboImage = new JComboBox()); 31 32 // Initialize combo box items 33 for ( int i = 1 ; i <= NUM_OF_FLAGS; i++) 34 jcboImage.addItem(String.valueOf(i)); 35 36 // Place panels in the frame 37 add(cardPanel, BorderLayout.CENTER); 38 add(p, BorderLayout.SOUTH); 39 40 // Register listeners with the source objects 41 jbtFirst.addActionListener( new ActionListener() { 42 public void actionPerformed(ActionEvent e) { 43 // Show the first component in cardPanel 44 cardLayout.first(cardPanel); 45 } 46 }); 47 jbtNext.addActionListener( new ActionListener() { 48 public void actionPerformed(ActionEvent e) { 49 // Show the first component in cardPanel 50 cardLayout.next(cardPanel); 51 } 52 }); 53 jbtPrevious.addActionListener( new ActionListener() { 54 public void actionPerformed(ActionEvent e) { 55 // Show the first component in cardPanel 56 cardLayout.previous(cardPanel); 57 } 58 }); 59 jbtLast.addActionListener( new ActionListener() { 60 public void actionPerformed(ActionEvent e) { 61 // Show the first component in cardPanel 62 cardLayout.last(cardPanel); 63 } 64 }); 65 jcboImage.addItemListener( new ItemListener() { 66 public void itemStateChanged(ItemEvent e) { 67 // Show the component at specified index 68 cardLayout.show(cardPanel, (String)e.getItem()); 69 } 70 }); 71 } 72 } |
An instance of CardLayout is created in line 6, and a panel of CardLayout is created in line 7. You have already used such statements as setLayout(new FlowLayout()) to create an anonymous layout object and set the layout for a container, instead of declaring and creating a separate instance of the layout manager, as in this program. The cardLayout object, however, is useful later in the program to show components in cardPanel . You have to use cardLayout.first(cardPanel) (line 44), for example, to view the first component in cardPanel .
The statement in lines 17 “21 adds the image label with the identity String.valueOf(i) . Later, when the user selects an image with number i , the identity String.valueOf(i) is used in the show method (line 68) to view the image with the specified identity.
28.3.2. (Optional) The GridBagLayout Manager |
The GridBagLayout manager is the most flexible and the most complex. It is similar to the GridLayout manager in the sense that both layout managers arrange components in a grid. The components of GridBagLayout can vary in size, however, and can be added in any order. For example, with GridBagLayout you can create the layout shown in Figure 28.5.
The constructor GridBagLayout() is used to create a new GridBagLayout . In GridLayout , the grid size (the number of rows and columns) may be specified in the constructor. It is not specified in GridBagLayout . The actual size is dynamically determined by the constraints associated with the components added to the container of GridBagLayout .
Each GridBagLayout uses a dynamic rectangular grid of cells, with each component occupying one or more cells called its display area . Each component managed by a GridBagLayout is associated with a GridBagConstraints instance that specifies how the component is laid out within its display area. How a GridBagLayout places a set of components depends on the GridBagConstraints and minimum size of each component, as well as the preferred size of the component's container.
To use GridBagLayout effectively, you must customize the GridBagConstraints of one or more of its components. You customize a GridBagConstraints object by setting one or more of its public instance variables. These variables specify the component's location, size, growth factor, anchor, inset, filling, and padding.
The variables gridx and gridy specify the cell at the upper left of the component's display area, where the upper-leftmost cell has the address gridx=0 , gridy=0 . Note that gridx specifies the column in which the component will be placed, and gridy specifies the row in which it will be placed. In Figure 28.5, Button 1 has a gridx value of 1 and a gridy value of 3 , and Label has a gridx value of 0 and a gridy value of 0.
You can assign GridBagConstraints.RELATIVE to gridx to specify that the component be placed immediately after the component that was just added to the container. You can assign GridBagConstraints.RELATIVE to gridy to specify that the component be placed immediately below the component that was just added to the container.
The variables gridwidth and gridheight specify the number of cells in a row (for gridheight ) or column (for gridwidth ) in the component's display area. The default value is 1 . In Figure 28.5, the JPanel in the center occupies two columns and two rows, so its gridwidth is 2 , and its gridheight is 2 . Text Area 2 occupies one row and one column; therefore its gridwidth is 1 , and its gridheight is 1 .
You can assign GridBagConstraints.RELATIVE.REMAINDER to gridwidth ( gridheight ) to specify that the component is to be the last one in its row (column).
The variables weightx and weighty specify the extra horizontal and vertical space to allocate for the component when the resulting layout is smaller horizontally than the area it needs to fill.
The GridBagLayout manager calculates the weight of a column to be the maximum weightx ( weighty ) of all the components in a column (row). The extra space is distributed to each column (row) in proportion to its weight.
Unless you specify a weight for at least one component in a row ( weightx ) and a column ( weighty ), all the components clump together in the center of their container. This is because, when the weight is zero (the default), the GridBagLayout puts any extra space between its grid of cells and the edges of the container. You will see the effect of these parameters in Listing 28.2.
The variable anchor specifies where in the area the component is placed when it does not fill the entire area. Valid values are:
GridBagConstraints.CENTER (the default) GridBagConstraints.NORTH GridBagConstraints.NORTHEAST GridBagConstraints.EAST GridBagConstraints.SOUTHEAST GridBagConstraints.SOUTH GridBagConstraints.SOUTHWEST GridBagConstraints.WEST GridBagConstraints.NORTHWEST
The variable fill specifies how the component should be resized if its viewing area is larger than its current size. Valid values are GridBagConstraints.NONE (the default), GridBagConstraints.HORIZONTAL (makes the component wide enough to fill its display area horizontally, but doesn't change its height), GridBagConstraints.VERTICAL (makes the component tall enough to fill its display area vertically, but doesn't change its width), and GridBagConstraints.BOTH (makes the component totally fill its display area).
The variable insets specifies the external padding of the component, the minimum amount of space between the component and the edges of its display area. The default value is new Insets(0, 0, 0, 0) .
The variables ipadx and ipady specify the internal padding of the component: how much space to add to its minimum width and height. The width of the component is at least its minimum width plus ( ipadx * 2) pixels, and the height of the component is at least its minimum height plus ( ipady * 2) pixels. The default value of these variables is . The insets variable specifies the external padding, while the ipadx and ipady variables specify the internal padding, as shown in Figure 28.6.
There are two constructors for creating a GridBagConstraints object:
public GridBagConstraints()
Constructs a GridBagConstraints object with all of its fields set to their default values.
public GridBagConstraints( int gridx, int gridy, int gridwidth, int gridheight, double weightx, double weighty, int anchor, int fill, Insets insets, int ipadx, int ipady)
Constructs a GridBagConstraints object with the specified field values.
To add a component to the container of GridBagLayout , use the following method in the container:
public void add(Component comp, Object gbConstraints)
Adds a component to the container with the specified GridBagConstraints .
Listing 28.2 gives a program that uses the GridBagLayout manager to create a layout for Figure 28.5. The output of the program is shown in Figure 28.7.
1 import java.awt.*; 2 import javax.swing.*; 3 4 public class ShowGridBagLayout extends JApplet { 5 private JLabel jlbl = new JLabel( 6 "Resize the Window and Study GridBagLayout" , JLabel.CENTER); 7 private JTextArea jta1 = new JTextArea( "Text Area 1" , 5 , 15 ); 8 private JTextArea jta2 = new JTextArea( "Text Area 2" , 5 , 15 ); 9 private JTextField jtf = new JTextField( "JTextField" ); 10 private JPanel jp = new JPanel(); 11 private JButton jbt1 = new JButton( "Button 1" ); 12 private JButton jbt2 = new JButton( "Button 2" ); 13 14 public ShowGridBagLayout() { 15 // Set GridBagLayout in the container 16 setLayout( new GridBagLayout() ); 17 18 // Create an GridBagConstraints object 19 GridBagConstraints gbConstraints = new GridBagConstraints(); 20 21 gbConstraints.fill = GridBagConstraints.BOTH; 22 gbConstraints.anchor = GridBagConstraints.CENTER; 23 24 Container container = getContentPane(); 25 26 // Place JLabel to occupy row 0 (the first row) 27 addComp(jlbl, container, gbConstraints, , , 1 , 4 , , ); 28 29 // Place text area 1 in row 1 and 2, and column 0 30 addComp(jta1, container, gbConstraints, 1 , , 2 , 1 , 5 , 1 ); 31 32 // Place text area 2 in row 1 and column 3 33 addComp(jta2, container, gbConstraints, 1 , 3 , 1 , 1 , 5 , 1 ); 34 35 // Place text field in row 2 and column 3 36 addComp(jtf, container, gbConstraints, 2 , 3 , 1 , 1 , 5 , ); 37 38 // Place JButton 1 in row 3 and column 1 39 addComp(jbt1, container, gbConstraints, 3 , 1 , 1 , 1 , 5 , ); 40 41 // Place JButton 2 in row 3 and column 2 42 addComp(jbt2, container, gbConstraints, 3 , 2 , 1 , 1 , 5 , ); 43 44 // Place Panel in row 1 and 2, and column 1 and 2 45 jp.setBackground(Color.red); 46 jp.setBorder( new javax.swing.border.LineBorder(Color.black)); 47 gbConstraints.insets = new Insets( 10 , 10 , 10 , 10 ); 48 addComp(jp, container, gbConstraints, 1 , 1 , 2 , 2 , 10 , 1 ); 49 } 50 51 /** Add a component to the container of GridBagLayout */ 52 private void addComp(Component c, Container container, 53 GridBagConstraints gbConstraints, 54 int row, int column, 55 int numberOfRows, int numberOfColumns, 56 double weightx, double weighty) { 57 // Set parameters 58 gbConstraints.gridx = column; 59 gbConstraints.gridy = row; 60 gbConstraints.gridwidth = numberOfColumns; 61 gbConstraints.gridheight = numberOfRows; 62 gbConstraints.weightx = weightx; 63 gbConstraints.weighty = weighty; 64 65 // Add component to the container with the specified layout 66 container.add(c, gbConstraints); 67 } 68 } |
The program defines the addComp method (lines 52 “67) to add a component to the container of GridBagLayout with the specified constraints. The GridBagConstraints object gbConstraints created in line 19 is used to specify the layout constraints for each component. Before adding a component to the container, set the constraints in gbConstraints and then use container.add(c, gbConstraints) (line 66) to add the component to the container.
What would happen if you change the weightx parameter for jbt2 to 10 in line 41? Now jbt2 's weightx is larger than jbt1 's. When you enlarge the window, jbt2 will get larger horizontally than jbt1 .
The weightx and weighty for all the other components are . Whether the size of these components grows or shrinks depends on the fill parameter. The program defines fill = BOTH for all the components added to the container (line 27).
Consider this scenario: Suppose that you enlarge the window. The display area for text area jta1 will increase. Because fill is BOTH for jta1 , jta1 fills in its new display area. If you set fill to NONE for jta1 , jta1 will not expand or shrink when you resize the window.
The insets parameter is ( , , , ) by default. For the panel jp , insets is set to ( 10 , 10 , 10 , 10 ) (line 47).
If you have used a Windows-based RAD tool like Visual Basic, you know that it is easier to create user interfaces with Visual Basic than with Java. This is mainly because the components are placed in absolute positions and sizes in Visual Basic, whereas they are placed in containers using a variety of layout managers in Java. Absolute positions and sizes are fine if the application is developed and deployed on the same platform, but what looks fine on a development system may not look right on a deployment system. To solve this problem, Java provides a set of layout managers that place components in containers in a way that is independent of fonts, screen resolutions , and platform differences.
For convenience, Java also supports an absolute layout that enables you to place components at fixed locations. In this case, the component must be placed using the component's instance method setBounds() (defined in java.awt.Component ), as follows :
public void setBounds( int x, int y, int width, int height);
This sets the location and size for the component, as in the next example:
JButton jbt = new JButton( "Help" ); jbt.setBounds( 10 , 10 , 40 , 20 );
The upper-left corner of the Help button is placed at (10, 10); the button width is 40, and the height is 20.
You perform the following steps in order not to use a layout manager:
1. | Use this statement to specify no layout manager: setLayout( null ); |
2. | Add the component to the container: add(jbt); |
3. | Specify the location where the component is to be placed, using the setBounds method: JButton jbt = new JButton( "Help" ); jbt.setBounds( 10 , 10 , 40 , 20 ); |
Listing 28.3 gives a program that places the same components in the same layout as in the preceding example but without using a layout manager. Figure 28.8 contains the sample output.
1 import java.awt.*; 2 import javax.swing.*; 3 4 public class ShowNoLayout extends JApplet { 5 private JLabel jlbl = 6 new JLabel( "Resize the Window and Study No Layout" , 7 JLabel.CENTER); 8 private JTextArea jta1 = new JTextArea( "Text Area 1" , 5 , 10 ); 9 private JTextArea jta2 = new JTextArea( "Text Area 2" , 5 , 10 ); 10 private JTextField jtf = new JTextField( "TextField" ); 11 private JPanel jp = new JPanel(); 12 private JButton jbt1 = new JButton( "Cancel" ); 13 private JButton jbt2 = new JButton( "Ok" ); 14 private GridBagLayout gbLayout; 15 private GridBagConstraints gbConstraints; 16 17 public ShowNoLayout() { 18 // Set background color for the panel 19 jp.setBackground(Color.red); 20 21 // Specify no layout manager 22 setLayout( null ); 23 24 // Add components to frame 25 add(jlbl); 26 add(jp); 27 add(jta1); 28 add(jta2); 29 add(jtf); 30 add(jbt1); 31 add(jbt2); 32 33 // Put components in the right place 34 jlbl.setBounds( , 10 , 400 , 40 ); 35 jta1.setBounds( , 50 , 100 , 100 ); 36 jp.setBounds( 100 , 50 , 200 , 100 ); 37 jta2.setBounds( 300 , 50 , 100 , 50 ); 38 jtf.setBounds( 300 , 100 , 100 , 50 ); 39 jbt1.setBounds( 100 , 150 , 100 , 50 ); 40 jbt2.setBounds( 200 , 150 , 100 , 50 ); 41 } 42 } |
If you run this program on Windows with 640 x 480 resolution, the layout size is just right. When the program is run on Windows with a higher resolution, the components appear very small and clump together. When it is run on Windows with a lower resolution, they cannot be shown in their entirety.
If you resize the window, you will see that the location and size of the components are not changed, as shown in Figure 28.9.
Tip
Do not use the no-layout-manager option to develop platform-independent applications. |
javax.swing.BoxLayout is a Swing layout manager that arranges components in a row or a column. To create a BoxLayout , use the following constructor:
public BoxlayLayout(Container target, int axis)
This constructor is different from other layout constructors. It creates a layout manager that is dedicated to the given target container. The axis parameter is BoxLayout.X_AXIS or BoxLayout.Y_AXIS , which specifies whether the components are laid out horizontally or vertically. For example, the following code creates a horizontal BoxLayout for panel p1 :
JPanel p1 = new JPanel(); BoxLayout boxLayout = new BoxLayout(p1, BoxLayout.X_AXIS); p1.setLayout(boxLayout);
You still need to invoke the setLayout method on p1 to set the layout manager.
You can use BoxLayout in any container, but it is simpler to use the Box class, which is a container of BoxLayout . To create a Box container, use one of the following two static methods:
Box box1 = Box.createHorizontalBox(); Box box2 = Box.createVerticalBox();
The former creates a box that contains components horizontally, and the latter creates a box that contains components vertically.
You can add components to a box in the same way that you add them to the containers of FlowLayout or GridLayout using the add method, as follows:
box1.add(new JButton( "A Button" ));
You can remove components from a box in the same way that you drop components to a container. The components are laid left to right in a horizontal box, and top to bottom in a vertical box.
BoxLayout is similar to GridLayout but has many unique features.
First, BoxLayout respects a component's preferred size, maximum size, and minimum size. If the total preferred size of all the components in the box is less than the box size, then the components are expanded up to their maximum size. If the total preferred size of all the components in the box is greater than the box size, then the components are shrunk down to their minimum size. If the components do not fit at their minimum width, some of them will not be shown. In the GridLayout , the container is divided into cells of equal size, and the components are fit in regardless of their preferred maximum or minimum size.
Second, unlike other layout managers, BoxLayout considers the component's alignmentX or alignmentY property. The alignmentX property is used to place the component in a vertical box layout, and the alignmentY property is used to place it in a horizontal box layout.
Third, BoxLayout does not have gaps between the components, but you can use fillers to separate components. A filler is an invisible component. There are three kinds of fillers: struts, rigid areas, and glues.
A strut simply adds some space between components. The static method createHorizontalStrut(int) in the Box class is used to create a horizontal strut, and the static method createVerticalStrut(int) to create a vertical strut. For example, the code shown below adds a vertical strut of 8 pixels between two buttons in a vertical box:
box2.add( new JButton( "Button 1" )); box2.add(Box.createVerticalStrut( 8 )); box2.add( new JButton( "Button 2" ));
A rigid area is a two-dimensional space that can be created using the static method createRigidArea(dimension) in the Box class. For example, the next code adds a rigid area 10 pixels wide and 20 pixels high into a box.
box2.add(Box.createRigidArea( new Dimension( 10 , 20 ));
A glue separates components as much as possible. For example, by adding a glue between two components in a horizontal box, you place one component at the left end and the other at the right end. A glue can be created using the Box.createGlue() method.
Listing 28.4 shows an example that creates a horizontal box and a vertical box. The horizontal box holds two buttons with print and save icons. The vertical box holds four buttons for selecting flags. When a button in the vertical box is clicked, a corresponding flag icon is displayed in the label centered in the applet, as shown in Figure 28.10.
1 import java.awt.*; 2 import java.awt.event.*; 3 import javax.swing.*; 4 5 public class ShowBoxLayout extends JApplet { 6 // Create two box containers 7 private Box box1 = Box.createHorizontalBox(); 8 private Box box2 = Box.createVerticalBox(); 9 10 // Create a label to display flags 11 private JLabel jlblFlag = new JLabel(); 12 13 // Create image icons for flags 14 private ImageIcon imageIconUS = 15 new ImageIcon(getClass().getResource( "/image/us.gif" )); 16 private ImageIcon imageIconCanada = 17 new ImageIcon(getClass().getResource( "/image/ca.gif" )); 18 private ImageIcon imageIconNorway = 19 new ImageIcon(getClass().getResource( "/image/norway.gif" )); 20 private ImageIcon imageIconGermany = 21 new ImageIcon(getClass().getResource( "/image/ germany .gif" )); 22 private ImageIcon imageIconPrint = 23 new ImageIcon(getClass().getResource( "/image/print.gif" )); 24 private ImageIcon imageIconSave = 25 new ImageIcon(getClass().getResource( "/image/save.gif" )); 26 27 // Create buttons to select images 28 private JButton jbtUS = new JButton( "US" ); 29 private JButton jbtCanada = new JButton( "Canada" ); 30 private JButton jbtNorway = new JButton( "Norway" ); 31 private JButton jbtGermany = new JButton( "Germany" ); 32 33 public ShowBoxLayout() { 34 box1.add( new JButton(imageIconPrint)); 35 box1.add(Box.createHorizontalStrut( 20 )); 36 box1.add( new JButton(imageIconSave)); 37 38 box2.add(jbtUS); 39 box2.add(Box.createVerticalStrut( 8 )); 40 box2.add(jbtCanada); 41 box2.add(Box.createGlue()); 42 box2.add(jbtNorway); 43 box2.add(Box.createRigidArea( new Dimension( 10 , 8 ))); 44 box2.add(jbtGermany); 45 46 box1.setBorder( new javax.swing.border.LineBorder(Color.red)); 47 box2.setBorder( new javax.swing.border.LineBorder(Color.black)); 48 49 add(box1, BorderLayout.NORTH); 50 add(box2, BorderLayout.EAST); 51 add(jlblFlag, BorderLayout.CENTER); 52 53 // Register listeners 54 jbtUS.addActionListener( new ActionListener() { 55 public void actionPerformed(ActionEvent e) { 56 jlblFlag.setIcon(imageIconUS); 57 } 58 }); 59 jbtCanada.addActionListener( new ActionListener() { 60 public void actionPerformed(ActionEvent e) { 61 jlblFlag.setIcon(imageIconCanada); 62 } 63 }); 64 jbtNorway.addActionListener( new ActionListener() { 65 public void actionPerformed(ActionEvent e) { 66 jlblFlag.setIcon(imageIconNorway); 67 } 68 }); 69 jbtGermany.addActionListener( new ActionListener() { 70 public void actionPerformed(ActionEvent e) { 71 jlblFlag.setIcon(imageIconGermany); 72 } 73 }); 74 } 75 } |
Two containers of the Box class are created in lines 7 “8 using the convenient static methods createHorizontalBox() and createVerticalBox() . You could also create it using the constructor Box(int axis) . The box containers always use the BoxLayout manager. You cannot reset the layout manager for the box containers.
The image icons are created from image files (lines 14 “25) through resource bundles, which was introduced in §16.9, "Locating Resource Using the URL Class."
Two buttons with print and save icons are added into the horizontal box (lines 34 “36). A horizontal strut with size 20 is added between these two buttons (line 35).
Four buttons with texts US, Canada, Norway, and Germany are added into the vertical box (lines 38 “44). A horizontal strut with size 8 is added to separate the US button and the Canada button (line 39). A rigid area is inserted between the Norway button and the Germany button (line 43). A glue is inserted to separate the Canada button and the Norway button as far as possible in the vertical box (line 41).
The strut, rigid area, and glue are instances of Component , so they can be added to the box container. In theory, you can add them to a container other than the box container. But they may be ignored and have no effect in other containers.
OverlayLayout is a Swing layout manager that arranges components on top of each other. To create an OverlayLayout , use the following constructor:
public OverlayLayout(Container target)
The constructor creates a layout manager that is dedicated to the given target container. For example, the following code creates an OverlayLayout for panel p1 :
JPanel p1 = new JPanel(); OverlayLayout overlayLayout = new OverlayLayout(p1); p1.setLayout(overlayLayout);
You still need to invoke the setLayout method on p1 to set the layout manager.
A component is on top of another component if it is added to the container before the other one. Suppose components p1 , p2 , and p3 are added to a container of the OverlayLayout in this order, then p1 is on top of p2 , and p2 is on top of p3 .
Listing 28.5 gives an example that overlays two buttons in a panel of OverlayLayout , as shown in Figure 28.11. The first button is on top of the second button. The program enables the user to set the alignmentX and alignmentY properties of the two buttons dynamically. You can also set the opaque (blocked) property of the first button. When the opaque property is set to true , the first button blocks the scene of the second button, as shown in Figure 28.11(a). When the opaque property is set to false , the first button becomes transparent to allow the second button to be seen through the first button, as shown in Figure 28.11(b).
1 import java.awt.*; 2 import java.awt.event.*; 3 import javax.swing.*; 4 5 public class ShowOverlayLayout extends JApplet { 6 private JButton jbt1 = new JButton( "Button 1" ); 7 private JButton jbt2 = new JButton( "Button 2" ); 8 9 private JTextField jtfButton1AlignmentX = new JTextField( 4 ); 10 private JTextField jtfButton1AlignmentY = new JTextField( 4 ); 11 private JTextField jtfButton2AlignmentX = new JTextField( 4 ); 12 private JTextField jtfButton2AlignmentY = new JTextField( 4 ); 13 private JComboBox jcboButton1Opaque = new JComboBox( 14 new Object[]{ new Boolean( true ), new Boolean( false )}); 15 16 // Panel p1 to hold two buttons 17 private JPanel p1 = new JPanel(); 18 19 public ShowOverlayLayout() { 20 // Add two buttons to p1 of OverlayLayout 21 p1.setLayout( new OverlayLayout(p1)); 22 p1.add(jbt1); 23 p1.add(jbt2); 24 25 JPanel p2 = new JPanel(); 26 p2.setLayout( new GridLayout( 5 , 1 )); 27 p2.add( new JLabel( "Button 1's alignmentX" )); 28 p2.add( new JLabel( "Button 1's alignmentY" )); 29 p2.add( new JLabel( "Button 2's alignmentX" )); 30 p2.add( new JLabel( "Button 2's alignmentY" )); 31 p2.add( new JLabel( "Button 1's opaque" )); 32 33 JPanel p3 = new JPanel(); 34 p3.setLayout( new GridLayout( 5 , 1 )); 35 p3.add(jtfButton1AlignmentX); 36 p3.add(jtfButton1AlignmentY); 37 p3.add(jtfButton2AlignmentX); 38 p3.add(jtfButton2AlignmentY); 39 p3.add(jcboButton1Opaque); 40 41 JPanel p4 = new JPanel(); 42 p4.setLayout( new BorderLayout( 4 , 4 )); 43 p4.add(p2, BorderLayout.WEST); 44 p4.add(p3, BorderLayout.CENTER); 45 46 add(p1, BorderLayout.CENTER); 47 add(p4, BorderLayout.WEST); 48 49 jtfButton1AlignmentX.addActionListener( new ActionListener() { 50 public void actionPerformed(ActionEvent e) { 51 jbt1.setAlignmentX( 52 Float.parseFloat(jtfButton1AlignmentX.getText())); 53 p1.revalidate(); // Cause the components to be rearranged 54 p1.repaint(); // Cause the viewing area to be repainted 55 } 56 }); 57 jtfButton1AlignmentY.addActionListener( new ActionListener() { 58 public void actionPerformed(ActionEvent e) { 59 jbt1.setAlignmentY( 60 Float.parseFloat(jtfButton1AlignmentY.getText())); 61 p1.revalidate(); // Cause the components to be rearranged 62 p1.repaint(); // Cause the viewing area to be repainted 63 } 64 }); 65 jtfButton2AlignmentX.addActionListener( new ActionListener() { 66 public void actionPerformed(ActionEvent e) { 67 jbt2.setAlignmentX( 68 Float.parseFloat(jtfButton2AlignmentX.getText())); 69 p1.revalidate(); // Cause the components to be rearranged 70 p1.repaint(); // Cause the viewing area to be repainted 71 } 72 }); 73 jtfButton2AlignmentY.addActionListener( new ActionListener() { 74 public void actionPerformed(ActionEvent e) { 75 jbt2.setAlignmentY( 76 Float.parseFloat(jtfButton2AlignmentY.getText())); 77 p1.revalidate(); // Cause the components to be rearranged 78 p1.repaint(); // Cause the viewing area to be repainted 79 } 80 }); 81 jcboButton1Opaque.addActionListener( new ActionListener() { 82 public void actionPerformed(ActionEvent e) { 83 jbt1.setOpaque(((Boolean)(jcboButton1Opaque. 84 getSelectedItem())).booleanValue()); 85 p1.revalidate(); // Cause the components to be rearranged 86 p1.repaint(); // Cause the viewing area to be repainted 87 } 88 }); 89 } 90 } |
A panel p1 of OverlayLayout is created (line 21) to hold two buttons (lines 22 “23). Since Button 1 is added before Button 2, Button 1 is on top of Button 2.
The alignmentX and alignmentY properties specify how the two buttons are aligned relative to each other along the x -axis and y -axis (lines 51, 59). These two properties are used in BoxLayout and OverlayLayout , but are ignored by other layout managers. Note that the alignment is a float type number between and 1 .
The opaque property is defined in JComponent for all Swing lightweight components. By default, it is true for JButton , which means that the button is nontransparent. So if Button 1's opaque is true , you cannot see any other components behind JButton 1. To enable the components behind Button 1 to be seen, set Button 1's opaque property to false (lines 83 “86).
SpringLayout is a new Swing layout manager introduced in JDK 1.4. The idea of SpringLayout is to put a flexible spring around a component. The spring may compress or expand to place the components in desired locations.
To create a SpringLayout , use its no-arg constructor:
public SpringLayout()
A spring is an instance of the Spring class that can be created using one of the following two static methods:
public static Spring constant( int pref)
Returns a spring whose minimum, preferred, and maximum values each have the value pref .
public static Spring constant( int min, int pref, int max)
Returns a spring with the specified minimum, preferred, and maximum values.
Each spring has a preferred value, minimum value, maximum value, and actual value. The getPreferredValue() , getMinimumValue() , getMaximumValue() , and getValue() methods retrieve these values. The setValue(int value) method can be used to set an actual value.
The Spring class defines the static sum(Spring s1, Spring s2) to produce a combined new spring, the static minus(Spring s) to produce a new spring running in the opposite direction, and the static max(Spring s1, Spring s2) to produce a new spring with larger values from s1 and s2 .
To add a spring or a fixed space to separate components in the container, use one of the following two methods in the SpringLayout class:
public void putConstraint(String e1, Component c1, Spring s, String e2, Component c2)
Places a spring between edge e1 of component c1 and edge e2 of component c2 , anchored on c2 . Each edge must have one of the following values: SpringLayout.NORTH , SpringLayout.SOUTH , SpringLayout.EAST , SpringLayout.WEST .
public void putConstraint(String e1, Component c1, int pad, String e2, Component c2)
Places a fixed pad between edge e1 of component c1 and edge e2 of component c2 , anchored on c2 .
In the preceding methods, e2 and c2 are referred to as the anchor edge and anchor component , and e1 and c1 as the dependent edge and dependent component .
Listing 28.6 gives an example that places a button in the center of the container, as shown in Figure 28.12.
1 import java.awt.*; 2 import javax.swing.*; 3 4 public class ShowSpringLayout extends JApplet { 5 public ShowSpringLayout() { 6 SpringLayout springLayout = new SpringLayout(); 7 JPanel p1 = new JPanel(springLayout); 8 JButton jbt1 = new JButton( "Button 1" ); 9 p1.add(jbt1); 10 11 Spring spring = Spring.constant( , 1000 , 2000 ); 12 springLayout.putConstraint(SpringLayout.WEST, jbt1, spring, 13 SpringLayout.WEST, p1); 14 springLayout.putConstraint(SpringLayout.EAST, p1, spring, 15 SpringLayout.EAST, jbt1); 16 springLayout.putConstraint(SpringLayout.NORTH, jbt1, spring, 17 SpringLayout.NORTH, p1); 18 springLayout.putConstraint(SpringLayout.SOUTH, p1, spring, 19 SpringLayout.SOUTH, jbt1); 20 21 add(p1, BorderLayout.CENTER); 22 } 23 } |
A SpringLayout named springLayout is created in line 6 and is set in JPanel p1 (line 7). An instance of Spring is created using the static constant method with minimum value , preferred value 1000 , and maximum value 2000 (line 11).
Like icons and borders, an instance of Spring can be shared. The putConstraint method in SpringLayout puts a spring between two components. In lines 12 “13, the spring is padded between the west of the button and the west of the panel p1 , anchored at p1 . In lines 14 “15, the same spring is padded between the east of the panel and the east of the button, anchored at the button. The selection of the anchor components is important. For example, if lines 12 “13 were replaced by the following code,
springLayout.putConstraint(SpringLayout.WEST, p1, spring, SpringLayout.WEST, jbt1);
the button would be pushed all the way to the east, as shown in Figure 28.13(a). If lines 12 “13 were replaced by the following code,
springLayout.putConstraint(SpringLayout.NORTH, p1, spring, SpringLayout.NORTH, jbt1);
the button would be pushed all the way to the top, as shown in Figure 28.13(b).
If lines 14 “15 were replaced by the following code,
springLayout.putConstraint(SpringLayout.EAST, p1, Spring.sum(spring, spring) , SpringLayout.EAST, jbt1);
the spring on the right of the button is twice strong as the left spring of the button, as shown in Figure 28.13(c).