Tuesday, May 17, 2011

 

Desktop Java GUI programming made easy -- An amateur's learning path on Groovy SwingBuilder and Miglayout - part 1

1.The two great helpers for Desktop Java GUI – SwingBuilder and Miglayout

Desktop Java has never been really hot. But Java Swing still can be useful in many cases. After all, it's a mature multiple platform solution, powerful and extendable, with lots of library available around. It’s well known for some that SwingBuilder reduced the complexity of Swing programming greatly, but there is one part that live outside the scope of SwingBuilder – the layout.

Swing provided some layout options, and there are visual designers available, some even have good reputation, like Netbeans. [i] However, the code generated by visual designer is not meant to be read by human. While there is a manual layout approach good enough – Miglayout. I think you don’t need too much effort to build a complex UI with Miglayout, and the result is sure easy to be understood, modified and maintained.

You may ask, how can that be possible?

2. Miglayout, the powerful and simple layout Swing need

First let’s have a look at the examples Swing layout give ...

Does any example there looks like a practical application UI?

Then compare the Miglayout web start demo...

Much better, huh? You can check the source code of all the Miglayout examples right inside the demo. Most of them are pretty concise. I didn’t say simple because they are a little bit weird and not easy to understand at first. Don’t worry, I’ll try to give you a quick summary with my own terms and understandings.

Be warned, I’m not an expert on many of them, so there could be many mistakes. But I do believe my view can provide a quick and clear introduction to the essence of Miglayout. The main documents available on Miglayout official site are either too complex (the white paper and cheat sheet includes everything so are more like reference) or too simple (the quick start guide is simple enough, but didn’t emphasize how should you use them on regular applications).

In simplest terms, Miglayout is a flow layout based on a grid. Which means you need to

1.Define a grid.

2.Put components into cells of the grid according to the "flow".

(The documents could say they have three layout methods, including Grid, docking, absolute positioning. I’ll just discuss with the things I believe most important to give a clear picture.)

To do this you need to build a Miglayout instance with three kinds of parameters called "constraints":

MigLayout layout = new MigLayout(

"fill", // Layout Constraints

"[10][20:30:40][40!][::40]", // Column constraints

"[min!][10::20][40mm!]"); // Row constraints

Constraints may looks weird for newcomers, like nothing you have ever seen before. And there are tons of parameters to learn. I suggest you just focus on the summary I mentioned here first, read the documents and experiment by yourself later. I estimate that you only need about 30 minutes to grasp the central idea, and then you can learn more details you needed through your usage.

Constraints are quoted strings which were taken as parameters. They are commands to control certain aspect of the layout. In the example above we have layout constraints to control some global parameters for the whole layout, and column/row constraints control every column/row accordingly. (Because column and row are symmetrical, and my examples are mainly based on column, I'll just talk about column from now on.)

Besides the layout and column constraints specified in the Miglayout constructor, every component can also have their own Component constraints, which may affect itself and components around them. You add the constraints for the component here in Swing:

panel.add(comp, "width 10:20:40");

panel.add(comp3, "wrap") // Wrap to next row

panel.add(comp4)

So what’s inside the constraints? What do those words mean?

Let’s take some constraints as examples.

"fill, flowy, push 10, split 2, align right".

I’ll call every comma separated words group inside constraints as “command”. Constraints are made from 0, 1 or more commands. You can use “” for empty, single quoted word for single command (“fill”), comma separated words group for multiple commands("fill, flowy, push 10, split 2, align right").

Every command affects the layout through certain aspect. It could be single word(“fill”), or looks like single word, but actually a word with default parameters(“push”) so only written as one word. It could also be several words connected by space(“push 10”, this is the longer format of the same command).

I say the words inside one command are “connected” because they are parts of one single function. You can think multiple words command as a method with parameters, and one word command as a method without parameter, or a method with default so omitted parameters.

So the format of the commands is very flexible. There are often full/ abbreviated format of same word (“p/para/paragraph”), or you can use 0, 1 or more parameters for same word (like the “push” example I mentioned above). I think as long as you follow the order of the words in same command you can write them freely.

Each command only affects one aspect of the layout, so you often need several commands together for one receiver as one quoted string constraint. You just write as many commands as your need separated by comma (the order is not important), and then you have a constraint. Of course you should be clear that all the commands inside one constraint are targeting for same receiver.

Some of the commands and parameters format (like parameter type unitValue, 10 means 10 pixels, and directions, “right” etc) are shared among three types of the constraints, while each type of constraint has their own specific commands (except column and row are symmetrical), so this could cause some confusion. I think you just need to be clear which the target of your every command is.

I’ll briefly discuss several often used commands for each types of constraint to give you an idea how Miglayout control the layout.


1. Layout constraints. This is the global settings for the whole layout.

In summary, you can use "fill", or "fill, debug" or "fill, flowy, debug" here. As mentioned before, the order inside the quote is not important


2. Column constraints /row constraints. Here you can define how many columns/rows you want in your grid. This is a rough blue print for the grid. I think you don’t have to be very accurate to count the columns. You can always start from a rough number and adjust them later.

The basic format of column constraints is like this.

[]40[]20[]15[fill]

Each [] define commands targeted for one column, and the order of them match to the columns, so the last "fill" command is for the 4th column. You can use default parameters so just write an empty [] as a place holder to keep the column order right. Multiple commands need to be comma separated inside the [], for example [grow, fill]. So the format of constraints I summarized above only applies to each column. The number between every two columns are gaps of pixels(you could use other unitValue, default is pixels so you can write only the number without the unit) between them. That means you can move your components together as column to adjust locations, and they will be keep aligned together!

3. Component constraints. When putting component into the next cell, the component can control the relationship between itself and cell, or change the flow for following components.

"fill, flowy, push 10, split 2, align right".

On one hand the component can override the normal rule of one component for one cell. On the base of the grid defined by column/row constraints, you can "split" a cell so that multiple components can be put in that cell - with “split 2” the current and the next component will share this cell. Or you can make a component "span" multiple cells. The quick start guides have very clear examples for these so I’ll not repeat too much.

Now you may found we can define the grid in different ways: we can start from a fine sized grid, so every component will take at least one cell, while bigger component will span on cells. Or we can define a big rough grid so every "cell" is actually an area/panel, and we can still organize components inside the cell. For me, I'll choose a balanced point between these two extremes, so that as much as possible components can just flow into cells "naturally” with default flow directions.

On the other hand the component can change the global flow direction we defined earlier in Layout constraints. They can

We saw another example that there could be similar commands in global layout, column layout and component layout, which gives you method to change the behavior of components as groups, or change local component temporally.

This will be all for now. I neglected certain parts of Miglayout purposefully, like docking, absolute positioning, size group, because I think they are just minor details compared to the big picture above. I may mention more commands in later examples when I feel needed.

You can always check the white paper or try it by yourself. The web start Swing demo in Miglayout website is very helpful, you can right click anywhere in the application to see the constraints for that component. You can even change them as you want! You can also check the source code of the demo in the source pane. For me, after I found out how to use Miglayout with SwingBuilder, I wrote a "template" file that have all the scraffold setup just waiting for the layout and components, then I tried to build a UI similar to the Demo, and then compared my code with the demo source code. The simplicity of SwingBuilder make the effort as light as possible. The "debug" command I mentioned earlier is also very helpful.

Update:

Somebody reminded me about WindowBuilder Pro for Eclipse, so I checked it. It provided great support for MigLayout. Before I have to modify layout constraints and start application again and again to test them, now with WindowBuilder Pro you can see the result immediately. It's very easy to use, good for experimenting all kinds of constraints, and you can get accurate measurement instead of guess. Even I'm using IDEA for Groovy and SwingBuilder, I can still use WindowBuilder to do the layout, then copy the constraints to IDEA. Of course if IDEA can provide similar support for MigLayout, or even can generate SwingBuilder code (which should be trivial after MigLayout is supported, because SwingBuilder code is much simpler), that could be a programmer's dream for Swing/SWT programming tools! Do you agree?

[i] Someone even mentioned that you can create the UI with Netbeans visual designer then take the view to be used by Griffon. I didn’t take this path for personal considerations: my recent leisure project is pretty simple. Griffon seems a little bit overkill for that. I do can get a lot of stuff for free with Griffon, but they are not really needed, while I have to take some burden for them: the starting time for newly created application is about 15 seconds, to deploy it into JAR took about 5 minutes, mostly on signing JAR. I’m pretty sure the latter can be configured and improved, but that means a lot of learning before I start the real work. And there is not too much documents available yet, Griffon is relatively new compared to SwingBuilder and Miglayout.

Last but not least, Griffon is built on MVC architecture. Despite of what everyone said, I read here (A Swing Architecture Overview) that Swing is actually built in a model - view/controller model. I think that in many cases views and control are deeply coupled anyway. You didn't reduce the complexity by separate them into two parts. On the contrary, Groovy SwingBuilder + Miglayout are a right balance in all aspects. SwingBuilder make most part of programming as easy as possible. Griffon gives you applet, web start version for free, and there are many plug-ins can be useful, but you still need to do the real work by yourself. For Desktop Java GUI, SwingBuilder is good enough.


Comments:
Thank you for this nice explanation of MigLayout. I, too, have been searching for any tutorial data on the Swingbuilder/MigLayout combination.

In your next post, could you please provide a simple, but complete Groovy example?

Thanks again,
Tom.
 
I just completed the rest 2 part of the article. There is a template in part 2 which are complet, ready to compile, without any components. You can add any components with combinations of Miglayout constraints on the base of that template.
 
This is a complete example with Miglayout


import groovy.swing.SwingBuilder
import net.miginfocom.swing.MigLayout
import javax.swing.WindowConstants as WC

def swing = new SwingBuilder()

swing.build {
frame(title: "test", pack: true, show: true, defaultCloseOperation: WC.DISPOSE_ON_CLOSE,
layout: new MigLayout("fill", "", "")) {
label(text: "Configuration", constraints: "")
}
}
 
Post a Comment

Subscribe to Post Comments [Atom]





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]