Friday, August 17, 2007

Some tips on developing Swing applications:

I recently developed one small application in swing . Here are some comments on tricks & libraries I used.

1. Using Template Method Pattern to simplify code

ActionTemplate :

JButton button = new JButton("Ok");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event){
//do some stuff here
}
});


This is how usually we handle the actions (I don't believe in separating out actionlisteners code from components declarations, it is always easier/better to keep them as near as possible) Anonymous classes provides easier way of achieving this;

from the above code I noted that,
1. In anonymous class, I will never use ActionEvent class & I don't want see it. It's an intrusive model.
2. Many times I want to handle actions asynchronously (like using SwingUtilitilities.invokeLater() routine), I don't want to repeat in every action listeners code.
3. I also want to display a wait cursor during this time & should be optional way of passing the icon @ runtime;
4. I also want to print the time taken & memory consumed for each actions; & I don;t want to repeat that, I also want to take that out from the code once the application in production.
6. actionPerformed method name is not intuitive, although ActionEvent not used 90% of time I do use it 10% of time when I write inner or separate ActionListener classes, it should be available for me.

Here is how I propose to handle this using template pattern.

ActionTemplate is abstract class having clicked() as abstract method & with default implementation for all other features.

button.addActionListener(new ActionTemplate() {
public void clicked(){
//do some stuff here
}
});
This is fine for me 90% of time.Now code is less & looks simpler. This is clearly non-intrusive model & all the extra features are defaulted and can be easily changed by over-riding when required. (Convention over configurations)

In case to handle the event asynchronously whenever some tasks lot of time, we can over-ride isAsynchronus call. isAsynchronus() will return false by default.

button.addActionListener(new ActionTemplate() {
public void clicked(){
//Some lengthy stuff here, So cannot make AWTDispatch thread to wait till it's completed
}
public boolean isAsynchronus(){
return true;
}
});


Similarly there can be callback methods for setting the wait cursor, monitoring completion status, (getWaitCursorIcon(), done() etc...)
We can also get the ActionEvent which is member variable for ActionTemplate class.

Similarly for WindowTemplate we can add closed, closing, max(), min() etc...
& for MouseTemplate methods like pressed(), clicked(), hover(), rightClikced(), doubleclicked() etc...
These will easier to understand & code than their Listener counterparts. I guess this approach is near to closures. Lack of closure in java is really a bottleneck while coding event handling. We are forced to deal with anonymous & final variables

Although I don't feel these tricks can be used in large projects (As adding more APIs to likely to confuse more) but this type of techniques will be very useful when we write applications in a very small team or by a single person, where using AOP, XML, Properties file based action handling framework using reflection is not an option (because of learning curve & the increase in total java source/jar size)

2. Window Dissolving tricks
While going through the
Swing Hacks book, I came across one dissolving hack. I also wrote some inspired from that, although they are too simple but can give window closing a different experience :-), Feel free to copy & use it from here.

3. External libraries
There is no point re-inventing the wheel, there are abundant high quality JFC components freely available, but the problem is most of them will come big baggage (in terms of size & API complexity).
I used these libraries & had no problem whatsoever with these.
JFreeCharts - For reporting through charts
iText - For Generating the pdf reports
I have used these excellent libraries in the past (but with J2ee applications)
DesignGridLayout - As layout manager

It is always better to code these in such a manner that if even when we take out these jars, application should work properly.It is always better to make these libraries not to be part of core functionalities. I handled these by neglecting ClassNotFoundException in appropriate place where-ever I am using such libraries. In case of applet & webstart it is not practical to use these heavy libraries as they increase the download time & there might be issues with licensing as well. So for an applet/java webstart versions JFree charts, iText & other external jars will not be included, I just remove these jars from library to make it light weight, that's it no code change required :-)

4. Layout Manager
I did not wanted to use the GridBagLayoutManager, it was regarded as un-necessarily complex for developing Swing applications. I tried out generating UI with my favorite IDE NetBeans. I did not like the code it generated. May be I need adjust with this kind of code in future. First the code generated was looking ugly & I wanted to use my custom components which I need to add & I like doing small tasks by defining actionlisteners there itself, the code generated lot of default methods. As of now I can think of using it only for prototype.



I looked into layout managers who all claimed to be better alternative to GridBagLayoutManager, I actually looked into the code of most of these layout managers & they are indeed provide lot ease in developing layouts,some introduce more complexity
I came across this debate on from layout manager showdown, All kudos to John for the splendid job done by comparing layout managers.
I found MigLayout to be best in solving all types of complex requirements, But for my screens I found DesignGridlayout the simplest after going through the sample code. As it is based in standard JDK library, this should be first choice for developing simple grid based screens.

For example, I wanted to design my layout something like this;

& it took 12 lines to do that, & code was too simple.


public void build(DesignGridLayout layout){
layout.row().center().add(title());layout.row().height(10);
layout.row().center().add(new JLabel("Email ")).add(user());
layout.row().height(5);
layout.row().center().add(new JLabel("Password ")).add(pwd());
layout.row().height(5);
layout.row().center().add(new JLabel("Exam List")).add(examList());
layout.row().height(5);
layout.row().center().add(signIn());
layout.row().height(15);
cancelButton = new JButton("Cancel");
loginButton = new JButton("Ok");
layout.row().center().add(loginButton).add(cancelButton);
}


It was such a simple task to develop for other such screens as well

5. Code Samples from Experts
Swing Hacks & Filthy Rich Clients are the best books on swing written by highly credible authors. The source code that comes with these books are also excellent resources.
There are quite very nice ideas explained well for developing custom components. Source code for the both the books can be freely downloaded, but I suggest to buy the books.

These are the excellent code I re-used in my application
Pieter-Jan Savat's wonderful utility on page turning effects in swing.
Simon's flashy & bouncy effects with swing.
Hyper Link listener for handling links

6. Leveraging New JDK1.5 APIs Features:
Using new JDK1.5 features can simplify the coding to great levels; JDK1.5 has been one of the biggest release in java's history.
For example, now it is possible to add data to the to list like this;
List l= list("praveen","manvi","test"); using

public static List list(T... elements) {
List list = new ArrayList(elements.length);
for (T elem : elements){
list.add(elem);
}
return list;
}


These tricks will be useful especially writing Models for UI components. The above sample shows the usage of improved for() loop, generics & variable argument options.
I found this explanation good with samples.
Bookmark and Share