I’ve been heads down at work lately developing a desktop application, something that is a bit refreshing considering I’ve mainly been working on web applications or distributed systems for some time. Desktop applications have some nice things but developing them also means they have their own fair share of pain (acceptance testing a multithreaded app is not fun!).
The last time I had developed anything remotely serious desktop wise was back during University days when I was using Borland’s JBuilder 2 (yes that means Java 1.1 and AWT) and then slightly later, the terrible Microsoft’s J++ (and non-java classes). I have repeated found that IDEs are wonderful things for building prototype UIs, but anyone who has had the horror of maintaining any of the automagically created code in the background, will agree with me at how terrible it can be.
Not willing to repeat mistakes of the past, on my current project we have had great successes leveraging the JGoodies Forms Layout Manager. The makers of this opensource component specifically designed it with maintenance and addressing the common tasks of simple forms without being excessively verbose about it. They have an excellent white paper on their site and their APIs are intuitive, but I thought it might be useful stepping through a step by step guide anyway.
In less than a screen of code, we are going to create something that looks like the form below.
Although this form is really simple, the same principles apply even when building more complex ones.
1. Composing the Form
The first thing you should do is tell the builder what the form itself will look like by splitting up the screen into its significant columns and rows (as you can see by the red lines in the above diagram). You tell the builder the form’s constraints by using the FormLayout class and further specifying these with the column and then the row constraints. The constraints can be specified programmatically, or more conveniently as a string given the correct syntax. The following form layout declares the above form that we want.
FormLayout loginFormLayout = new FormLayout(“right:40dlu, 5dlu, pref:grow, 5dlu, pref:grow”,
“pref, 5dlu, pref, 5dlu, pref, 5dlu, pref”);
The first argument for a new form layout is a column specification which is a string containing a description for each column all separated by a comma. Our example uses the second and fourth columns as spacers between components, while the first column is a right aligned fixed width column of 40dlus. The third and fifth columns will take their default component sizes and should resize the same way when someone resizes the form.
In this example, I chose to use the DLU (Dialog Unit) for specifying measurements of items although JGoodies is flexible and understands pixels (px), points (pt), inches (in), millimetres (mm) and centimetres (cm).
You specify row definitions in exactly the same way as columns. I have copied the full syntax for your convenience (as published in the JGoodies whitepaper).
columnSpec ::= [columnAlignment:] size [:resizeBehavior]
rowSpec ::= [rowAlignment :] size [:resizeBehavior]
columnAlignment ::= LEFT | CENTER | RIGHT | FILL | L | C | R | F
rowAlignment ::= TOP | CENTER | BOTTOM | FILL | T | C | B | F
size ::= constantSize | componentSize | boundedSize
componentSize ::= MIN | PREF | DEFAULT | M | P | D
constantSize ::= {integer}integerUnit | {double}doubleUnit
integerUnit ::= PX | PT | DLU
doubleUnit ::= IN | MM | CM
boundedSize ::= MIN(constantSize;componentSize) | MAX(constantSize;componentSize)
resizeBehavior ::= NONE | GROW | GROW({double) | G({double})
2. Adding Components to your Form
After declaring a FormLayout, we now have a grid for the basis of the form and to make it useful, we will add the rest of the components which we accomplish with the DefaultFormBuilder class.
FormBuilder builder = new DefaultFormBuilder(loginFormLayout);
builder.addLabel(“Username:”, “1, 1, 1, 1”);
builder.add(userNameField, “3, 1, 3, 1, FILL, FILL”);
builder.addLabel(“Password:”, “1, 3, 1, 1”);
builder.add(passwordNameField, “3, 3, 3, 1, FILL, FILL”);
builder.addLabel(“Log onto:”, “1, 5, 1, 1”);
builder.add(domainField, “3, 5, 3, 1, FILL, FILL”);
builder.add(okButton, “3, 7, 1, 1, FILL, FILL”);
builder.add(cancelButton, “5, 7, 1, 1, FILL, FILL”);
The add method here takes a cell constraint that is a string similar to the column and row specification but is more relevant to a component and its relationship to the form. In this example, the constraint associated with the user name label tells it that it will be in column 1, row 1 and only has a column and row span of 1. In contrast to this, the username text field will be placed in column 3, row 1, spans 3 columns and only 1 row and will fill its cells both horizontally and vertically. I have once again repeated the full syntax for cells for your convenience (once again taken from the JGoodies whitepaper).
constraints ::= column, row [, colSpan, rowSpan][, hAlign, vAlign]
column ::= {an integer}
row ::= {an integer}
colSpan ::= {an integer}
rowSpan ::= {an integer}
hAlign ::= LEFT | CENTER | RIGHT | DEFAULT | FILL | L | C | R | D | F
vAlign ::= TOP | CENTER | BOTTOM | DEFAULT | FILL | T | C | B | D | F
3. That’s It? Now What?
After you have finished adding all your components to the builder, you use builder.getPanel() to return you the now-configured swing JPanel that you can either render or further nest to form even more complex screens. It’s as simple as that.
Once you get your head around the two basic concepts of first specifying the general appearance of the form, and then the second of adding components and describing each of their own particular constraints, we have found that understanding and further modifying the layout of the application is extremely easy to do. Please note that the JGoodies team also provide alternative ways of specifying constraints and adding them that may be more readable to you, but we have found that having a simple cheat sheet readily at hand helps to mitigate the confusion of the arbitrary strings.
The runnable example code and build file is avilable here.