ZoneLayout vs. GridBagLayout vs. FormLayout

In this quick comparison, we'll recreate the behavior of BorderLayout using 3 different layout managers. The full listing is here and here is the final result:

BorderLayout Comparison Window

Here's the GridBagLayout code that creates the necessary layout:


51           JPanel p = new JPanel(new GridBagLayout());
52           GridBagConstraints gc = new GridBagConstraints();
53           gc.gridx = 0; gc.gridy = 0;
54           gc.gridwidth = 3; gc.fill = GridBagConstraints.HORIZONTAL;
55           p.add(north, gc);
56           gc.gridx = 0; gc.gridy = 1;
57           gc.gridwidth = 1; gc.fill = GridBagConstraints.VERTICAL;
58           p.add(west, gc);
59           gc.gridx = 2; gc.gridy = 1;
60           gc.gridwidth = 1; gc.fill = GridBagConstraints.VERTICAL;
61           p.add(east, gc);
62           gc.gridx = 0; gc.gridy = 2;
63           gc.gridwidth = 3; gc.fill = GridBagConstraints.HORIZONTAL;
64           p.add(south, gc);
65           gc.gridx = 1; gc.gridy = 1;
66           gc.gridwidth = 1; gc.fill = GridBagConstraints.BOTH;
67           gc.weightx = 1.0; gc.weighty = 1.0;
68           p.add(center, gc);

The GridBagLayout code suffers from a number of problems:

There a couple of libraries out there that alleviate some of GridBagLayout's problems through a wrapper API but they do not the fix the underlying fundamental issues.

Here's the FormLayout code that creates the necessary layout:


28           FormLayout layout = new FormLayout(
29                   "f:d, f:d:g, f:d",
30                   "f:d, f:d:g, f:d"
31           );
32   
33           JPanel p = new JPanel(layout);
34           CellConstraints cc = new CellConstraints();
35           p.add(north, cc.xyw(1, 1, 3));
36           p.add(south, cc.xyw(1, 3, 3));
37           p.add(east, cc.xy(3, 2));
38           p.add(west, cc.xy(1, 2));
39           p.add(center, cc.xy(2, 2));

The FormLayout code improves on the GridBagLayout code. It's not as verbose and cleaner. However, it still uses explicit coordinates, making it difficult to create and modify. The constraints are also split between the layout definition and the bindings, making it difficult for the programmer to mentally visualize the behavior of the panel components. The programmer is also forced to break the layout down into rows and columns before creating the layout. The constraints are also not particularly intuitive.

Here's the ZoneLayout code that creates the necessary layout:


80           ZoneLayout layout = ZoneLayoutFactory.newZoneLayout();
81           JPanel p = new JPanel(layout);
82           layout.addRow("n..-~n");
83           layout.addRow("wc...e");
84           layout.addRow("|.*+.!");
85           layout.addRow("!....|");
86           layout.addRow("w...ce");
87           layout.addRow("s..-~s");
88   
89           p.add(north, "n");
90           p.add(south, "s");
91           p.add(east, "e");
92           p.add(west, "w");
93           p.add(center, "c");

If this is your first time looking at ZoneLayout code, then this probably isn't particularly clear to you, but lines 82-87 tell you all you need to know about the layout. The cheatsheet may come in handy here.

It's important to note here how easy it would be to modify the ZoneLayout code. If you wanted to add another button just below the north button, with ZoneLayout you would add 2 lines of code: the layout definition beneath the 'n' Zone and the binding. With the other 2 layout managers, you would have to spend significantly more time, including updating all explicit coordinates affected by the addition.

Comparing ZoneLayout with JGoodies' FormLayout

In this more advanced comparison, we'll create a complex dialog with both ZoneLayout and FormLayout. Here is the dialog:

ZoneLayout Comparison Window

First, here is the relevant code using FormLayout (full listing here).



23       public JPanel buildPanel() {
24           FormLayout layout = new FormLayout(
25                   "d:g",
26                   "d, 3dlu, f:d:g, 3dlu, d"
27   
28           );
29   
30           FormLayout nameLayout = new FormLayout(
31                   "r:d, 3dlu, d:g, 7dlu, r:d, 3dlu, d:g",
32                   "d, 3dlu, d, 3dlu, d"
33   
34           );
35   
36           FormLayout emailLayout = new FormLayout(
37                   "r:d, 3dlu, d:g, 3dlu, d",
38                   "d, 3dlu, d, 3dlu, d, 3dlu, d, f:d:g"
39   
40           );
41   
42           PanelBuilder builder = new PanelBuilder(nameLayout);
43           CellConstraints cc = new CellConstraints();
44           builder.addLabel("First Name:", cc.xy(1, 1));
45           builder.add(new JTextField(15), cc.xy(3, 1));
46           builder.addLabel("Last Name:", cc.xy(5, 1));
47           builder.add(new JTextField(15), cc.xy(7, 1));
48           builder.addLabel("Title:", cc.xy(1, 3));
49           builder.add(new JTextField(15), cc.xy(3, 3));
50           builder.addLabel("Nickname:", cc.xy(5, 3));
51           builder.add(new JTextField(15), cc.xy(7, 3));
52           builder.addLabel("Display Format:", cc.xy(1, 5));
53           builder.add(new JComboBox(values), cc.xyw(3, 5, 5));
54           JPanel namePanel = builder.getPanel();
55           namePanel.setBorder(BorderFactory.createCompoundBorder(
56                   BorderFactory.createTitledBorder("Name"),
57                   BorderFactory.createEmptyBorder(5, 5, 5, 5)));
58   
59           builder = new PanelBuilder(emailLayout);
60           cc = new CellConstraints();
61           builder.addLabel("Email Address:", cc.xy(1, 1));
62           builder.add(new JTextField(15), cc.xy(3, 1));
63           builder.add(new JButton("Add"), cc.xy(5, 1));
64   
65           builder.add(new JList(values), cc.xywh(1, 3, 3, 6));
66   
67           builder.add(new JButton("Edit"), cc.xy(5, 3));
68           builder.add(new JButton("Remove"), cc.xy(5, 5));
69           builder.add(new JButton("Advanced"), cc.xy(5, 7));
70   
71           JPanel emailPanel = builder.getPanel();
72           emailPanel.setBorder(BorderFactory.createCompoundBorder(
73                   BorderFactory.createTitledBorder("E-mail"),
74                   BorderFactory.createEmptyBorder(5, 5, 5, 5)));
75   
76           builder = new PanelBuilder(layout);
77           builder.add(namePanel, cc.xy(1, 1));
78           builder.add(emailPanel, cc.xy(1, 3));
79           builder.add(ButtonBarFactory.buildOKCancelBar(
80                   new JButton("OK"), new JButton("Cancel")),
81                   cc.xywh(1, 5, 1, 1, "right, bottom"));
82           JPanel p = builder.getPanel();
83           p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
84   
85           return p;
86       }

Imagine this code was handed off to you by your predecessor and now the customer wants you to add a button along the right side of the panel. What steps would you take? You'd probably:

  1. Run the code so you can get a mental picture of the layout
  2. Find the components in the source that are near where the new button needs to go
  3. Find the coordinates of those near-by components
  4. Figure out the row and/or column definitions for the near-by components
  5. Figure out what row(s) and/or column(s) the new button needs to go in
  6. Spend some time figuring out how adding rows or columns affects every other component in the layout
  7. Add the new row and/or column definitions
  8. Add the new button to the panel with it's coordinate
  9. Verify and update the coordinates of every other component in the layout
  10. Run through the test/fix cycle a few times since the explicit coordinate system is accident prone

Now let's look at the functionally equivalent code using ZoneLayout (full listing here):


23       public JPanel buildPanel() {
24           ZoneLayout layout = ZoneLayoutFactory.newZoneLayout();
25           layout.addRow("a-~a");
26           layout.addRow("b+*b");
27           layout.addRow("6...");
28           layout.addRow("c>.c");
29   
30           ZoneLayout aLayout = ZoneLayoutFactory.newZoneLayout();
31           aLayout.addRow("a>a2b-~b3c>c2d-~d", "valueRow");
32           aLayout.addRow("6................", "valueRow");
33           aLayout.addRow("e>e2f......-~...f");
34   
35           ZoneLayout bLayout = ZoneLayoutFactory.newZoneLayout();
36           bLayout.addRow("a>a2b-~b2c-c");
37           bLayout.addRow("...........6");
38           bLayout.addRow("g........d-d");
39           bLayout.addRow("...........6");
40           bLayout.addRow("...+*....e-e");
41           bLayout.addRow("...........6");
42           bLayout.addRow(".........f-f");
43           bLayout.addRow(".......g...!");
44   
45           JPanel namePanel = new JPanel(aLayout);
46           aLayout.insertTemplate("valueRow");
47           namePanel.add(new JLabel("First Name:"), "a");
48           namePanel.add(new JTextField(15), "b");
49           namePanel.add(new JLabel("Last Name:"), "c");
50           namePanel.add(new JTextField(15), "d");
51           System.out.println(new JTextField(15).getMinimumSize());
52           aLayout.insertTemplate("valueRow");
53           namePanel.add(new JLabel("Title:"), "a");
54           namePanel.add(new JTextField(15), "b");
55           namePanel.add(new JLabel("Nickname:"), "c");
56           namePanel.add(new JTextField(15), "d");
57           namePanel.add(new JLabel("Display Format:"), "e");
58           namePanel.add(new JComboBox(values), "f");
59           namePanel.setBorder(BorderFactory.createCompoundBorder(
60                   BorderFactory.createTitledBorder("Name"),
61                   BorderFactory.createEmptyBorder(5, 5, 5, 5)));
62   
63           JPanel emailPanel = new JPanel(bLayout);
64           emailPanel.add(new JLabel("Email Address:"), "a");
65           emailPanel.add(new JTextField(15), "b");
66           emailPanel.add(new JButton("Add"), "c");
67           emailPanel.add(new JButton("Edit"), "d");
68           emailPanel.add(new JButton("Remove"), "e");
69           emailPanel.add(new JButton("Advanced"), "f");
70           emailPanel.add(new JList(values), "g");
71   
72           emailPanel.setBorder(BorderFactory.createCompoundBorder(
73                   BorderFactory.createTitledBorder("E-mail"),
74                   BorderFactory.createEmptyBorder(5, 5, 5, 5)));
75   
76   
77           JPanel p = new JPanel(layout);
78   
79           p.add(namePanel, "a");
80           p.add(emailPanel, "b");
81           p.add(ButtonBarFactory.buildOKCancelBar(
82                   new JButton("OK"),
83                   new JButton("Cancel")), "c");
84   
85           p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
86           return p;
87       }

If this is your first time looking at ZoneLayout code, you're probably wondering what the gibberish is between lines 25 - 43. That's the 2-dimensional regular-expression-like language that ZoneLayout uses to define layouts. Each pair of letters defines the upper-left and lower-right corners of a zone and any control characters within those boundaries modify that zone (periods are for spacing only and are ignored). Digits are preset spacer zones. If you're wondering what the control characters do, check out the cheatsheet or the modifiers in the manual. When adding components, the constraint is just the letter of the zone to fill with the component.

The line count is essentially the same, but with ZoneLayout on lines 25 - 43 you effectively get a pseudo-visual picture of what the layout looks like:

Comparison ZoneLayout

This picture allows you to quickly grasp the layout of the panel. Here's the final result for the ZoneLayout implementation with the zone areas highlighted so you can see the resemblance:

ZoneLayout Comparison Window

If you're really paying attention, you probably noticed a little bit of trickery in the ZoneLayout code. On lines 31 - 32 we use a template so that we don't have to repeat ourselves for the "First Name/Last Name" and "Title/Nickname" component layouts. We just insert the template twice when adding the components.

A picture's worth a thousand words, right? After working with ZoneLayout for a short period, you'll be able to look at a layout and immediately deduce what's going on and what goes where. When a customer wants you to add a button, you would just:

  1. Find the components that are nearby where the new button needs to go
  2. Find the zones with those components in the layout
  3. Add a row or two to contain the new zone for the button
  4. Add the button to the panel and bind it to the new zone
  5. Run through the test/fix cycle once or twice

These steps can be accomplished very quickly in most layouts, significantly reducing the time it takes to refactor layouts when compared to FormLayout or other traditional layout managers.

Here are the final results for the FormLayout and ZoneLayout implementations respectively (more below):

ZoneLayout Comparison Window
ZoneLayout Comparison Window

The focus so far has been on refactoring an existing layout, but ZoneLayout shines when creating layouts as well. Traditional layout managers are so hard to use that many recommend that you start out on paper first, break everything down into columns and rows, and then convert the layout into code. They also attempt to compensate for their deficiencies by using panel builders but in the end you still have code that is very difficult to read. ZoneLayout is simple enough that you can go directly to code and quickly build up advanced layouts since you have a mental picture to work with as you go.

Many suggest using visual GUI builders to compensate for the difficulty in using traditional layout managers, but they have problems as well: