Part 2: Creating a GridBagLayout in JBuilder


XYLayout is a feature of JBuilder Professional and Enterprise. If you use JBuilder Foundation, substitute null layout wherever XYLayout is specified.

About the design

This part of the GridBagLayout Tutorial takes you through each step of designing a GridBagLayout container in JBuilder. You will create the following typical dialog box that has several controls on it and a group of three buttons centered at the bottom.

GridBagLayout dialog box

The reason we decided upon this particular design is precisely because of the complications introduced by adding the odd number of buttons at the bottom. Also, this is a situation you will encounter frequently.

As you work through this part of the tutorial, please keep in mind that due to individual differences in drawing and arranging the components, your design may not exactly look like, nor behave like ours. But, it should be similar enough to allow you to achieve the desired results.

Steps 1 through 3 of this tutorial involve creating the layout in the UI designer. Steps 4-6 walk you through converting it to GridBagLayout. Step 7 shows you how to adjust the individual constraints to fine tune your design, and the reasoning behind these modifications.

Take your time, and don't be afraid to experiment. The UI designer makes it easy to try out different things and immediately see the effects. Just save your Frame1.java file before you try anything so you can Undo back to that point after you're done.


Step 1: Design the layout structure

The first step in designing your UI is to sketch the design on paper to plan the container structure and layouts.(See "Sketch your design on paper first" in Part 1 of this tutorial.)

We have already done this step below.

Sketch of proposed design

In our sketch, we grouped the components into three panels which the GridBagLayout will control: two equal sized panels to hold the components in the main part of the UI, and one panel across the bottom for three OK, Cancel, and Help buttons. We did this for two reasons:

The images below demonstrate how JBuilder handles the conversion using this arrangement. Notice that the grid displays only two columns and two rows in the designer. (The background color of the panels has been changed in this example to make it more obvious how JBuilder decided where the divisions should be.)

design time

Runtime, before resizing:

runtime before resizing

Runtime, after resizing:

runtime after resizing

You could also use only one panel for the three buttons at the bottom and let the GridBagLayout control all the other components separately, rather than nesting them inside panels. This can work, as long as you are very careful to make all the upper components the same width on each side. However, GridBagLayout will create more columns based on where each component ends, making a much more complicated design that is more difficult to control.

Notice that the conversion created six columns this time. (Again, the background color has been changed on the transparent components so you can see where they end, illustrating how the columns and rows were calculated.)

grid2.gif - 6840 Bytes

Finally, if you don't use any inner panels to group components, GridBagLayout's job is much harder. In addition to creating even more cells, it makes a determination as to how many of these cells each component uses for a display area, and which components get weight constraints. The more components in the design, the greater the potential for a real mess.

grid4.gif - 6958 Bytes

The images below show how a GridBagLayout panel with no inner panels behaves when it is resized at runtime:

Before resizing:

No panels before resizing

After resizing

No panels after resizing

As you can see, the components don't do what we want, and without grouping them into panels, it's practically impossible to control their placement and size during resizing.

Also, without any panels, if the components are not aligned very carefully and evenly, you could potentially end up with a grid that looks this:

gridmess.gif - 8876 Bytes

When your grid becomes larger than the Frame, it can be an indication that you have too many disparate objects for GridBagLayout to control, and the decisions it makes as to weight constraints, gridwidth, gridheight and anchor cause the design to require a larger grid than the container can hold. When this happens, the display area of the objects are off the edge of the Frame.

If this does happen to you during GridBagLayout conversion, you may not want to waste time trying to go on to correct it. It could take hours and lots of frustration to try clean it up by modifying constraints, and you still might not succeed satisfactorily.

Just do an Undo (CTRL+Z) to reverse the conversion, returning to XYLayout. Readjust the design in XYLayout, then try converting it again.

For a cleaner conversion to GridBagLayout, some changes you can try while the container is in XYLayout are:

If you do decide to go forward when it is in this state, try enlarging the Frame wide enough to display all the cells (especially the columns) so you can see how many there actually are in the design. That way you can modify how many cells each component is supposed to span, reducing the total number of cells in the grid. Then you can correct the gridx and gridy locations for each display area, as well as where the components are anchored in that display area. Also, in this case, you may want to try removing all weight constraints until the other constraints are fixed.

But, you can save yourself all this hassle if you use nested panels wisely in your design.

One more thought. It might also cross your mind that if you use four panels (three inside one GridBagLayout panel), it would work just as well to use BorderLayout for the main panel instead of GridBagLayout. The image below demonstrates how differently BorderLayout handles the components from GridBagLayout.

Three panels in a BorderLayout panel


Step 2: Create a project for this tutorial

JBuilder uses projects to organize associated files into folders, which are the equivalent of packages. Start a new project using File|New Project to open the Project Wizard.
  1. Start a new project using File|New Project to open the Project wizard.
  2. Modify the path and project name if you like, then click Finish.
  3. Choose File|New|Application to open the Application Wizard.
  4. Accept all defaults and click Finish.
  5. Save the project: choose File|Save all.


Step 3: Add the components to the containers

Let's proceed with creating the UI design that uses three nested panels inside a main GridBagLayout panel to group the components.

Add the main panel to the UI Frame

  1. Select the frame file in your project (Frame1.java) and click the Design tab to open the UI designer. (Its default layout is BorderLayout which you won't change.)
  2. Click the Swing Containers tab on the component palette and select a JPanel component. Click in the center of the frame to add this panel. This will place the panel (jPanel1) in the center and fill the entire frame.
  3. Select jPanel1 in the component tree or in the UI designer. Select the Layout property in the Inspector and change the layout to XYLayout.

    Change layout in Inspector

Create the left panel and add its components

  1. Add a jPanel component from the Swing Containers tab to the upper left area of jPanel1. Stretch it to fit almost halfway across and about two thirds down, leaving some margin between it and the left edge of jPanel1 as shown below. This will be jPanel2.

    jPanel2

  2. Change the layout for jPanel2 to XYLayout.
  3. Starting in the upper left corner of jPanel2, add the following components from the Swing tab: jLabel, jList, jButton, and jCheckbox.

    jPanel2 components

  4. In the Inspector, change the text property for these components as follows:

    jLabel1 = "Sorted Columns"
    jButton1 = "Remove from Sort"
    jCheckbox1 = "Descending"

    Change text property

  5. Match the font for jLabel1, jButton1, and jCheckbox1:

    1. Hold down the CTRL key, then select jLabel1, jButton1, and jCheckbox1.
    2. Click on the font property in the Inspector.
    3. Click the ellipsis button to bring up the Font dialog
    4. Change the font from 12 pts. to 11 pts.
    5. Uncheck Bold, then press OK.

    Font dialog

    textfont.gif - 4158 Bytes

  6. Select jLabel1 and change its foreground property to black.
Even up the component sizes and alignment
  1. Align the components using the XYLayout pop-up menu. Hold down the CTRL key and click on jLabel1, jList1, jButton1, and jCheckbox1. Right-click to bring up the pop-up menu and choose Align Left.

    align_left.gif - 4977 Bytes

    NoteTip:  Keep in mind the following when working with multiple components:
    • You can select multiple components in either the component tree or in the UI designer by holding down the CTRL key as you click each component.

    • When modifying the alignment for multiple components, the first component selected is the one to which the others will match.

    • To release a multiple selection, click on any unselected component in the designer or in the component tree.

    • To display the right-click pop-up window for a panel containing multiple components, select the panel, place your cursor over the middle nib, where a four-sided arrow cursor appears, then right-click.

      Select middle nib

  2. Now make the jLabel1, jButton1, and jCheckbox1 the same width as jList1:

    1. Holding down the CTRL key, select all four components, starting with jList1.
    2. Right-click and choose Same Size Horizontally.

    Since jList1 was the first component selected in the group, the other components will match its width.

    Note: While it's actually not necessary to make the components the same width, it is preferable in this case because it will simplify the grid created during the conversion to GridBagLayout. This will decrease the number of columns generated for GridBagLayout, as demonstrated earlier in Step 1.

    Important tip  For best results, when laying out components in XYLayout for conversion to GridBagLayout, you should try to match the start and end of components evenly. Wherever possible, try to make the components conform more to an even grid design, rather than a stair-step design.

    When the end of a component on one row overlaps the start of another component on a different row, GridBagLayout has a more difficult time calculating how many cells to create, which cells should have weights, how many cells the component should span, and how to apply insets and padding. The results are often not quite right, and it can be a real chore to clean up.

Create the right panel and add its components

Below is a quick way to create the right panel, since it is pretty much identical to the existing one.
  1. Right-click on jPanel2 and choose Copy from the pop-up menu.
  2. Place the cursor to the right of jPanel2, approximately at the location you want the upper-left corner of jPanel3 to appear.
  3. Right-click and choose Paste. A new panel called jPanel3 is created, containing jLabel2, jList2, jButton2, and jCheckbox2.
  4. Change the text property values for jLabel2, jButton2, and jCheckbox2 as follows:

    jLabel2 = "Available Columns"
    jButton2 = "Add to Sort"
    jCheckbox2 = "Case Sensitive

  5. Now lets align the two panels vertically. Hold down the CTRL key and select jPanel2 first, then jPanel3. Right-click and choose Align top from the pop-up menu.
  6. Expand the width of jButton2 to match jButton1 as closely as possible, then center jButton2 under jList2 as you did in jPanel2.

Create the bottom panel and add its components

  1. Now add the final panel, jPanel4. Drag a new jPanel component across the entire width of jPanel1 at the bottom, roughly aligning its left edge with jPanel1's left edge above, and its right edge with jPanel2's right edge.

    Draw bottom panel

  2. Right-click jPanel4 and choose Align Center.
  3. Drop three jButton components into jPanel4.
  4. Change the font for these buttons to match the other components.

    Add buttons

    Important  Since the default layout for a jPanel is FlowLayout, you can take advantage of this temporarily to add the buttons to the panel. As you drop the buttons into a FlowLayout panel, the layout manager centers the buttons in the panel with an even horizontal distance between them. You can then go directly to GridLayout without needing to use XYLayout.

  5. Change the text property (not the label property which has been deprecated) for each of the three buttons to display OK, Cancel, and Help, in that order. The layout manager will adjust the width of the buttons to fit the text. Don't bother with resizing or positioning them because when you convert this panel to GridLayout, the buttons will all be the same size, height, and width.

    Congratulations! You're all done with the initial layout. Save your file before proceeding.


Step 4: Convert the outer panel to GridBagLayout

You're ready to convert jPanel1 to GridBagLayout.

  1. First, do a final check to make sure all three panels are aligned well, and that there is nice amount of space between the inner panels and the edges of jPanel1.

    Important  For best results, don't crowd the components in their containers, otherwise you may have a mismatch between the collective minimum or preferred size of the components and the minimum or preferred size of the containers, including the Frame.

  2. Now select jPanel1 and change its layout property to GridBagLayout. This should result in very little visible change to your layout, especially since you grouped the components nicely into three panels that are easy for the GridBagLayout manager to manage.

    You should see a grid of only two columns and two rows, as shown below:

    Columns after conversion



Step 5: Convert the upper panels to GridBagLayout

In this design, it really doesn't make a difference whether you convert the main container to GridBagLayout first, or these two panels. Since the intended layout for jPanel2 and jPanel3 is GridBagLayout, the size of the panels will not change when they are converted (unlike changing to GridLayout or FlowLayout which resize to fit their components.)

To change these two panels to GridBagLayout:

  1. First make sure the alignment of the components in these panels is as clean and as simple as possible (the goal being to minimize the number of columns needed for the grid.)

  2. Select both jPanel2 and jPanel3 and change their layouts to GridBagLayout.

Note   If you find it difficult to select a component in the UI designer, you can always select it in the component tree.

You should see little difference in these panels after you convert them to GridBagLayout, although you might lose some of the margin, or gap, around the outside of the two panels.

Don't worry if things are not perfect yet. We'll make final adjustments in Step 7.


Step 6: Convert the lower panel to GridLayout

The final conversion is to the bottom panel:
  1. Select jPanel4 and change its layout to GridLayout.

    Note   Note: Since jPanel4 was left in XYLayout during jPanel1's GridBagLayout conversion, and since it was stretched across the entire width of jPanel1, it should have stayed nicely centered in the container after converting to GridBagLayout, spanning all columns. JBuilder assumes for conversion purposes that jPanel1's preferred size is the size it was in XYLayout, and assigns constraints assignments during the conversion based on this pixel size (for example, by giving it a gridwidth value equal to the number of columns generated during the conversion, or its ipadx value.)

    Now, as you change the layout to GridLayout, the panel stays the same width as it was in XYLayout, but notice that the buttons expand to fill the panel, and there is no gap between them. Also, the panel is larger than necessary. We'll fix these things in Step 7, where we'll make minor adjustments to the constraints for all the components.

  2. Save your file now, before you start making modifications.


Step 7: Make final adjustments

Rather than just give you the constraint values that make this UI design work, we're going to examine each component separately to show why we made the decisions we did. This approach should give you a much better understanding of how the constraints affect the components, cells, and other components.

One thing to keep in mind as we continue is that all the discussion about how the constraints affect the components during resizing is relevant only if at least one component has weight constraints. In fact, it is unlikely that you would ever create an outer GridBagLayout container without using weight constraints. When none of the components have weight, resizing has no affect on the placement of the components. All the components clump at their minimum or preferred size in the center of the GridBagLayout panel, and any extra space given to the container by enlarging it gets put between the outside edges of the components and the edge of their container.

Another important point is that there is more than one combination of constraint values that can accomplish the same results. For example, as with the GridLayout panel below, to keep it centered and a consistent size at the bottom of the GridBagLayout container, you can use insets, anchor, padding, or a combination of these.

Most of the modifications you will make during the rest of this part of the tutorial will be made in the GridBagConstraints Editor.

GridBagConstraints Editor

To open the GridBagConstraints Editor,

  1. Select a component that is in a GridBagLayout container. If the component is a panel containing other components, select it, then move the cursor over the middle nib where a four-headed arrow appears.
  2. Right-click and choose Constraints from the pop-up menu.

After finishing this tutorial, do some experimentation with this UI. Open the GridBagConstraints Editor and try out different constraint values to see what happens.

Now, let's continue by fixing the GridLayout panel (jPanel4) first.


GridLayout panel

As soon as you converted jPanel4 to GridLayout, the buttons inside it expanded to completely fill the panel with no gaps. Let's first put a little gap between the buttons, which is easy to do. It's just a matter of setting a value for the hgap property for the GridLayout itself.

To change the horizontal gap value,

  1. Click on the gridLayout1 node in the component tree (immediately below the jPanel4 node.)
  2. Click gridLayout1's hgap property in the Inspector and enter a pixel value (we used 6 pixels in our example).
Next, the buttons need to be smaller. Since this is a GridLayout, the buttons fill up the grid, and if you enlarge the frame, the buttons also expand, as demonstrated below.

Before resizing:

Fill on GridLayout

After resizing:

Fill in GridLayout

This is the expected behavior of GridLayout: the components it contains fill up the panel, no matter what size it is (honoring, of course, any values specified for horizontal and vertical gap surrounding the buttons.)

Therefore, to control the size of the buttons in the grid, you must restrict the size of the GridLayout panel itself, using its GridBagConstraints.

fill

If you look in the GridBagConstraints Editor at the constraint values assigned to jPanel4, you'll see that both its horizontal and vertical fill constraints are turned on. When this is the case, GridBagLayout stretches the panel to completely fill its display area, up to the edge of any insets that are set. If there are no insets, the panel fills up the display area all the way to the edge of the cells.

This is definitely not the behavior we want for this GridLayout panel since we want the buttons in the panel to be their preferred size. To accomplish this, you need to remove the panel's fill constraints.

To remove both the horizontal and vertical fill constraints at the same time, right-click the GridLayout panel and do one of the following:

Notice that as soon as you do this, the buttons shrink to their preferred size.

anchor

To make sure the panel always stays centered in its display area, you need to set the anchor constraint to Center. Since we centered the panel before we converted to GridBagLayout, the anchor constraint is probably already set to Center. But, open the GridBagConstraints Editor and make sure it looks like the following:

Anchor

Now that the fill is None, and the anchor is Center, when the container is resized, the buttons stay small and centered when the Frame is resized.

insets

Insets simply define an area between the component and the edges of its display area into which the component cannot enter. It is just like setting margins in a document. No matter how the container is resized, the number of pixels for the insets remains constant, and work like brakes to keep the component away from the edge of the display area.

If you have fill turned on for the component, it fills the display area up to the insets. As you resize the container, the component expands to stay up against those insets.

In the case of this GridLayout panel, since you removed the fill and anchored it in the center of its display area, nothing would be gained by adding any insets to the left and right edges of the display area. The only thing you need to do here is make sure both the left and right Inset values match (set to zero.)

You do, however, want to set the top and bottom insets to put some space above and below the panel. Set each of these values in the GridBagConstraints Editor to 15 pixels.

Insets

Padding

Padding (ipadx, ipady) changes the actual size of the panel component by adding a specified number of pixels to its minimum width and/or height. The minimum size of the GridLayout panel is just large enough to display the buttons at their minimum size, plus any width you specified in the hgap property for GridLayout. (In the case of buttons, there is a margin property that is also included in the calculation of the button's minimum size.)

If you like the size of the buttons and the panel at their minimum size, then you don't need to do anything more with the padding. If you want the buttons in the GridLayout panel to be larger than their minimum size, you can specify how many additional pixels to add to the panel to accomplish this. You can even make the buttons smaller by using negative values.

Example of different padding values

For this tutorial, since the size of the buttons in jPanel4 is acceptable at their minimum size, set both padding values to zero.

Padding

Note You can remove all padding values quickly by right-clicking jPanel4 in the designer and choosing Remove Padding.

That takes care of the GridLayout panel. Now, lets move on to the upper panels.


Upper panels

You mostly need to do some clean-up and constraint matching for theses panels ( jPanel2 and jPanel3) and their components.

gridwidth and gridheight

First, open the GridBagConstraints Editor, and in the Grid Position area, check that each component in these two panels only specifies one cell in the Width and Height values (gridwidth and gridheight) . If not, correct this.

gridwidth and gridheight

Do the same thing for jPanel2 and jPanel3.

fill

Next, you want all the components and their containers to fill up their display area, except for the buttons. As in the GridLayout panel, we don't want the buttons to expand as the Frame is resized.

Again, working with one panel at a time, select all the components inside the panel, except the button, and check Both for the fill constraint.

Lastly, you want the panels themselves to fill up their display area in the main GridBagLayout container. So, make sure jPanel2 and jPanel3 have a fill constraint of Both as well.

insets

Since the components in both of these panels happen to be the same, matching insets for all of them will ensure the components look the same in both panels. None of the components need left and right insets, as the panels containing them are invisible and will control the spacing in the main container. However, top and bottom insets will put some spacing between each of the components within the panels.

Set the Inset values for the components in each panel as follows:

Labels: Top = 0, Left = 0, Bottom = 4, Right = 0
Lists: Top = 0, Left = 0, Bottom = 0, Right = 0
Buttons: Top = 10, Left = 0, Bottom = 0, Right = 0
Checkboxes: Top = 6, Left = 0, Bottom = 0, Right = 0

Finally, to put a little space between the top and sides of these two panels and the outer container (jPanel1), set the following insets for jPanel2 and jPanel3:

Top = 10, Left = 10, Bottom = 0, Right =10

Note: you don't need to set insets for the bottom, since the GridLayout panel's top insets are taking care of that space.

anchor

The two panels, and their label, list, and checkbox components all have fill constraints of Both. Since each of these components fills its display area both horizontally and vertically, anchor constraints have no effect. There is simply no room inside the display area for the component to move.

If you want to verify this, try changing some of the anchor constraints for these components, then running your program and resizing the container. You'll see there is no change.

The only components in these panels for which anchor will have an effect are the buttons, which have no fill. Since they do not fill up their display area, they can be moved around inside it. To make sure these buttons stay centered in their display area, set their anchor constraint to Center.

ipadx, ipady

One place in this design where ipadx is appropriate is for controlling the size of the Add to Sort button in jPanel3. If you leave the ipadx at zero for both buttons, then the Add to Sort button will display at it's minimum size which won't match the size of the Remove from Sort button.

You can use Horizontal Padding (ipadx) to increase the width of the button and make it the same width as the other button.

  1. First, open the GridBagConstraints Editor and make sure the Padding Height value is set to zero for the Remove from Sort button. Also check that its fill constraints still say None.
  2. Next, select the Add to Sort button, and type in a pixel value of 33 for the Padding Width. This amount made the buttons match width in our example UI. If it has a different result for you, experiment with different values until you find one works.

    Add to Sort button without ipadx

    No padding

    Add to Sort button with ipadx

    Padding

Another thing you could do here, if you like, is make these buttons a bit smaller vertically than their preferred size by using a negative value in Padding Height (we used a -3). This is, of course, personal preference.

None of the other components in these panels need padding, nor do the panels themselves. Since the jPanel2 and jPanel3 have fill constraints, these override any padding that might be assigned.

You'll also notice that the list components use ipadx and ipady, which you might not want in reality. Since you did nothing to populate the lists with items in this example, if you remove the ipadx and ipady constraints along with the fill, the lists will disappear. Their minimum size is determined by the number of items in the list. We just added ipadx and ipady constraints to force a particular size for demonstration purposes.

weight

As we said earlier, if you want the components in a container to change size as the container is resized, you need to assign weight constraints values to at least one component horizontally and vertically. weight constraints specify how to distribute the extra container space created when resizing the container.

You need to set both the weight and fill constraints for a component if you want it to grow. For example, if a component has a horizontal weight constraint, but no horizontal fill constraint, then the extra space goes to the padding between the left and right edges of the component and the edges of the cell. It enlarges the width of the cell without changing the size of the component. If a component has both weight and fill constraints, then the extra space is added to the cell, plus the component expands to fill the new cell dimension in the direction of the fill constraint (horizontal in this case).

First, we took all the weight constraint values off that the conversion had set. This is what happened:

No weights
Before resizing

weights7.gif - 4957 Bytes

No weights
After resizing

weights8.gif - 5966 Bytes

Notice how the components are all clumped in the middle. This is easier to see when the container is resized as in the second image.

We determined that the components we want to grow are jPanel2 and jPanel3, and both list components inside them. We tried various weight constraint combinations on these components to see the results. Below are links to these results:

  1. weight constraints on both panels and lists
  2. weight constraints on the panels, but not on the lists
  3. weight constraints on the lists, but not on the panels
  4. Horizontal weight constraints only
  5. Vertical weight constraints only
  6. weight constraints on only one panel and list component in the row

Number 1 is the behavior we want. Set the weight constraints to 1.0 in the GridBagConstraints Editor for all four components: jPanel1, jPanel2, jList1, and jList2.


Conclusion

Well, that's all you need to do to this UI example. Hopefully this process helped you better understand GridBagLayout and the function of each of the GridBagConstraints.

One thing that should be obvious from this exercise is that each GridBagLayout is going to require experimentation with the constraints until you achieve just the look and behavior you want. However, JBuilder can assist in that process by quickly getting you past the initial GridBagLayout coding and on to the fine tuning. And, just like anything else, the more you practice, the easier it gets.