Making Swing Groovy, Part I

I’ve been spending a lot of time lately trying to understand how much Groovy improves Swing user interfaces. Like so many Java developers, I rarely write client-side programs. Almost all of my time and effort with Java over at least the last five years has been on the server side, mostly with frameworks like Struts, and then open source projects like Spring and Hibernate. During all that time, the client-side interface was restricted to what HTML and CSS can handle.

Of course, the growth of Ajax changed a lot of that. I like several of the major Ajax libraries, like prototype, scriptaculous, jQuery, YUI, and so on. One of the interesting trends I’ve been watching in the industry is whether browser-based GUIs in the future will be written in a Java component library that renders in HTML (like JSF), or written in Java libraries that translate to Ajax/JavaScript (like GWT), or are written using JavaScript components directly (like in YUI or dojo).

Still, I used to write user interfaces in Swing. Several years ago I wrote a Jeopardy-like game that I still use in training classes, where the game board itself is a Swing UI and the clients all have “buzzers” that connect to the game board via RMI. One of my most interesting, and certainly most fragile, projects was about ten years ago when I had to put a Swing GUI on top of a Fortran program (shudder). In case you’re wondering, you can’t get there from here. You have to go Java to C via JNI, and then C to Fortran (even though they handle arrays differently). It’s basically like putting an notch in a beam and telling it to break right here.

But those are stories for another day. Suffice it to say that I feel quite comfortable writing Swing GUIs in raw code (as opposed to graphical drag-and-drop interfaces), complete with inner classes to implement ActionListeners or the like for each button, and so on. I still struggle a bit with the intricacies of GridBagLayout, but I can get pretty far with lots of JPanels and combinations of BorderLayout and FlowLayout.

So when it came time to see what Groovy brought to the table, I thought I was quite prepared. Imagine my surprise to find that, believe it or not, in the last several years the field actually moved on, despite the fact that I almost never get requests for training classes in Swing or related technologies. A lot of people (Andres Almiray, Danno Ferrin, Geertjan, and many more) have spent considerable time and effort applying Groovy to user interfaces and making all kinds of progress.

The culmination of all of this effort is the new, seriously cool, Griffon framework. Griffon promises to bring Grails-like productivity improvements to client-side Java applications. The more I learn about it, the more impressed I am. It’s been a bit disheartening, however, to find out how much I still have to learn before I really understand it.

No matter. I like to say that I’m often wrong, but I rarely stay wrong. My next few posts here will be dedicated to principles that I’ve been learning about user interfaces in Groovy. Hopefully, in addition to providing a nice record for me of what I’m learning, maybe this will help someone else avoid my mistakes.

In this first post, let’s get the really well-known stuff out of the way. That’s the wonderful elegance of Groovy’s groovy.swing.SwingBuilder. SwingBuilder is a great Groovy class, well-described in GinA, that let’s you create Swing interfaces almost declaratively.

Here’s a trivial Swing application in Java. All it does is take text typed in a text field and echoes it in the text of a label. It’s about a simple as Swing applications come.

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.WindowConstants;

public class EchoGUI extends JFrame {
	private JTextField input = new JTextField(20);
	private JLabel prompt = new JLabel("Input text: ");
	private JLabel echo = new JLabel("Echo: ");
	private JLabel output = new JLabel();
	
	private Container cp = this.getContentPane();
	
	public EchoGUI() {
		super("Echo GUI");
		cp.setLayout(new GridLayout(0,2));  // 2 cols, as many rows as necessary
		
		cp.add(prompt);
		cp.add(input);
		cp.add(echo);
		cp.add(output);
		
		input.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				output.setText(input.getText());
			}
		});
		
		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		setSize(300,100);
		setVisible(true);
	}
	
	public static void main(String[] args) {
		new EchoGUI();
	}
}

First of all, this shows me to be an “old school” Swing developer, because I made my application extend JFrame rather than contain a JFrame instance. The trend these days is always to favor composition over inheritance, partly to reduce the coupling and partly to make testing easier (I’ll have more to say about testing Swing UIs in a future post). Still, this is the way I learned to do it, and I still fall into old habits, especially for trivial examples.

Second, I’m also arguably violating best practices because my main method instantiates the GUI directly, rather than calling SwingWorker.invokeLater to make sure all user interface updates are done in the Event Dispatch Thread (EDT). You can generally get away with this here, though, because until the GUI is actually rendered it isn’t much of a problem. Understanding the details of running on the EDT or not, however, is a big issue for later, too.

Also, I’m still in the habit of adding components to the content pane. I remember vividly when I had to rewrite all my existing Swing apps to add to content pane instead of directly to the frame itself, and I still do that, even though I don’t think it’s strictly necessary any more.

Finally, I did something here I generally don’t do, which is to use an anonymous inner class to handle the action listener for the button. Normally I create an actual (named) inner class for each component that generates events, and add an instance of each to the relevant component. Here, for example, I would normally write an inner class called something like EchoListener, and then add an instance of it to the button. Still, with only one button and an almost trivial inner class, it seemed easier to add it directly, even though arguably this is the ugliest syntax in all of Java, with the possible exception of generics.

Groovy’s SwingBuilder cuts this example down to practically nothing.

import groovy.swing.SwingBuilder
import javax.swing.WindowConstants as WC

SwingBuilder.build() {
	frame(title:'Echo GUI', size:[300,100],  
		visible:true, defaultCloseOperation:WC.EXIT_ON_CLOSE) {
		gridLayout(rows: 0, cols: 2)
		label 'Input text: '
		input = textField(columns:10, 
			actionPerformed: { output.text = input.text })
		label 'Echo: '
		output = label()
	}
}

The SwingBuilder means that I can just write words like frame, label, and textField, and, through the magic of metaprogramming, they are interpreted by the builder as Swing components. The other great part is that Groovy doesn’t force you to create an inner class just to hold a method. Closures are perfectly valid objects in their own right, so I can just assign one to the actionPerformed property of the text field, and I’m all set.

That’s an amazing savings, and it’s just the beginning. The combination of builder methods and closures cuts the amount of code required in a typical Swing GUI by a huge amount.

(I don’t know exactly how much, but I’m going to say 80%, because, as you may have heard, 80% of all statistics are made up on the spot.)

For more details, see GinA (Groovy in Action for the uninitiated, and if you are uninitiated and find Groovy cool, get thee hence to a bookstore and buy thee a copy) and the wiki pages at groovy.codehaus.org.

Still, this much I knew when I started my recent adventure. I had no idea about Groovy’s cool bind method, which dramatically simplifies the use of PropertyChangeListeners and is a great step along the way to a pure MVC architecture. Then there’s the @Bindable annotation that’s part of Groovy 1.6 beta 2, which makes it even easier. Then there’s the elegant way that Griffon lets you handle scheduling tasks inside or outside the EDT. I also realized that Swing hasn’t been idle, too, with the creation of the whole SwingX project and its associated set of components.

It’s been a wild ride for me recently, and I don’t feel I’m anywhere near finished. My feeling, though, is that Griffon is eventually going to be a very big deal. I know it’s making a big difference in how I think about Swing, and I’m looking forward to playing with it more and more.

Follow

Get every new post delivered to your Inbox.

Join 1,374 other followers

%d bloggers like this: