June 21, 2018
Hot Topics:

Building Java GUIs with Matisse: A Gentle Introduction

  • March 8, 2006
  • By Dick Wall
  • Send Email »
  • More Articles »

Making It Work

Close down the running application if it is still up. It's time to add some functionality to the buttons.

The main trick left to demonstrate with Matisse is hooking up the buttons. The rest is simply filling in code, and because this will be a very long article if I go into all of the code in great detail, I will provide all of the code snippets and some brief descriptions, but won't go into enormous detail in this area. Suffice it to say that the backing code is fairly straightforward and should not be surprising to anyone who takes a little time to understand it.

Also, again for brevity, I will put all of the code into the Form Class you created. This is fine for a small app, but in a larger app you will want to think carefully about how you lay out your code for maintainability and good coding practices.

The Timer Thread

One thing you will need for this app is a timer thread that, once the clock is started, keeps updating the timer label periodically with the elapsed time of the recording. You also need a few new attributes for this, so select JFlubberMainFrm.java in the editor, and then click on the Source view near the top of the edit window so that you can see the Java source.

Then, add the following code right after the public class JFlubberMainFrm extends javax.swing.JFrame {:

private Timer timer    = null;
private int labelNo    = 0;
private long currTime  = 0L;
private long startTime = 0L;

class UpdateTimeTask extends TimerTask {
   public void run() {
      long millis = System.currentTimeMillis() - startTime;
      int seconds = (int)(millis/1000);
      int minutes = seconds/60;
      seconds     = seconds % 60;

      timeLabel.setText(String.format("%d:%02d", minutes, seconds));

This code defines four attributes used to track the time and label number, and also a TimerTask that runs a thread that will be used later in the class to update the time label periodically once the start button is pressed. Note that this code uses the new Java 5 convenient text formatting. If you are still using Java 1.4, you will need to use a message formatter instead to get the minutes and seconds formatted nicely.

After you have put in this code and saved it, you will probably see some missing class errors. Just right-click in the editor window and select Fix Imports to add in the required classes. For the TimerTask, select the java.util one rather than the swing one if asked by the Fix Imports wizard.

Button Events

Everything is now in place. You now can add some button event handlers:

This is where my own lack of knowledge of Swing may show though. There are a lot of different events in Swing and I tend to stick with the ones I know that do what I want. Whether they are strictly speaking correct or not is another matter.

In fact, this leads me to my one complaint about Matisse right now, compared with something like Java Studio Creator where you can double-click on a button and get a sensible default event generated for that button. In Matisse, you can't do this; you have to right-click and select the event you want, from a somewhat overwhelming list of options.

For example, given a button, the default event I want is when someone clicks the button. In Java Studio Creator, a double-click gives me a handler for exactly this event, which is really nice and saves me time and worry. In Swing, for the button I have a choice of ActionPerformed, MouseClicked, MousePressed, MouseReleased and many, many others. I am sure there will be times I would want to use some of these other options, but by far the most common is someone clicking the button, so couldn't Matisse be made to select a logical default in this case? If it does do this already, I have not yet found the magic way to do it.

Instead, for buttons I tend to stick with the MouseReleased event (sort of like the MouseUp from the apple way of event handling). In other words, the event is fired not when the button is pressed, but when the button is released after being pressed. This seems to work the best and most consistently.

Knowing this, adding code to the MouseReleased event on the button is pretty easy:

Start Button

  1. Select the Start Button in the Design view of the form.
  2. Right-click on the button, and select Events->Mouse->Mouse Released.
  3. After you click on that option, the view should change to the code view and you should see a new event created for you that you can fill in the handler code.
  4. Add the following event code into the new method (replace the TODO comment):
  5. if(startTime == 0L) {
       startTime = evt.getWhen();
       timer = new Timer();
       timer.schedule(new UpdateTimeTask(), 200, 200);
  6. Save this and fix any import problems with the Fix Imports popup menu option.
  7. Don't worry too much about the code above, but in a nutshell it stores the current time into the startTime attribute, and then creates the timer thread to update the timer label every 1/5 of a second.

You want to add similar handlers for the other buttons, so repeat the above steps to attach the code below to each of the buttons as named:

Stop Button

if(timer != null) {
   startTime = 0L;

Flub Button


Note that this method uses a method called addLabel for convenience. You have not defined it yet, so define it in the class as:

public void addLabel(String label) {
   long millis = System.currentTimeMillis() - startTime;
   double seconds = (double)millis / 1000;
   String labelList = jTextArea1.getText();
   labelList = labelList + Double.toString(seconds) + "t" +
               Double.toString(seconds) + "t" + label + "n";

Save Button

You want to use this to put a filechooser up for the user to let them save the text from the text area:

JFileChooser chooser = new JFileChooser();
int rVal = chooser.showSaveDialog(this);
if(rVal == JFileChooser.APPROVE_OPTION) {
   File outputFile = chooser.getSelectedFile();
   PrintWriter outputStream;

   try {
      outputStream = new PrintWriter(new BufferedWriter(
                     new FileWriter(outputFile)));
   catch(IOException ex) {

Clear Button

This needs to set the text area to empty, and set the label count back to 0 (so that the labels start getting added from 1 again).

this.labelNo = 0;

Again, the above doesn't have a lot of explanation attached, so if you get confused as to what goes where, just take a look at the source code from the zip file included with this article and it should become apparent.

Building and Distribution

Once you have added this code, saved, and fixed any import problems (Fix Imports from the editor popup menu), select build main project from the IDE build menu.

When this builds, you should notice that the IDE reports that it has already wrapped the application up into a jar file for you (called jFlubber.jar in my case) and it also says that you can run the application using java -jar jFlubber.jar (with appropriate path information to tell java where jFlubber.jar is). However, that might not be the case, especially if you take the jar file created out of the dist directory and run it from somewhere else on your machine. Instead, you may see the error:

Exception in thread "main" java.lang.NoClassDefFoundError:

If you see this, the reason is that the GroupLayout that Matisse uses is not part of the standard Java distro, and so when you try and run the jar file with a normal java distribution, the layout manager is not available.

Unfortunately, the fix for this is a little ugly, and the only other real complaint I have about Matisse usage in NetBeans 5.0. One can imagine that this problem will be one that crops up for every Matisse-based GUI created, and how nice it would be to have a simple project property checkbox to say "include Matisse related classes in the distribution jar file", but if there is such an option, I have yet to find it.

Instead, the fix isn't hard, but it's irritating. In a nutshell, you have to alter the ant script used to build the project and add the missing classes required. To do this:

  1. In the top left hand pane, select the Files view.
  2. Open the project nodes as necessary until you see the build.xml file, and then double-click to open it.
  3. At the end, just before the </project> line, add the following code:
  4. <target name="-post-jar">
       <jar update="true" destfile="${dist.jar}">
          <zipfileset src="${libs.swing-layout.classpath}"/>

This defines a task that NetBeans calls automatically to roll the GroupLayout class into the jar file so that it will run anywhere. This is a useful thing to know for other such dependencies, without doubt, but if Matisse is going to be the way to create Java GUIs now, how much better it would be to make this step much easier with a project-level setting.

Note that there is a lib folder that you can keep in the same place relative to the jar file that will also keep this working; however, most developers roll a single jar file for the java -jar type of distribution simply because it is much easier to distribute that way. Your mileage may vary. Perhaps I am way out of line for suggesting the rolling up of GroupLayout into the jar file; it's simply my preference (and it makes things run much better if you are calling the jar file in a different directory).

After you make and save the change, do a Clean and Build from the Build menu; the whole thing should be rolled up with GroupLayout this time. Then, run java -jar jFlubber.jar again.

The Result

So, now you should be able to run your new jFlubber application. From the IDE, just click the run button or menu entry, or you can create a .bat file or .sh file depending what operating system you are using, and run the java -jar jFlubber.jar command that way. Either way you run it, the flubber application should be running just fine.

Here is a screenshot of my version running in Ubuntu 5.10 Linux:


So, this little application is unlikely to win any awards, beauty contests, or just about anything else, but it does serve as a good introduction to Matisse development, and also fills a useful role in my podcasting production tools. I am certain this program could be vastly improved, which is why I stuck it out there under the GPL license, and I hope it will be useful either as an example or an actual application to others.

Note also that it is a very simple application. It demonstrates only a fraction of what Matisse can do; furthermore, it doesn't even touch on the NetBeans Rich Client Platform (RCP) that will save an enormous amount of time for anyone doing more complex applications. Perhaps I will dive into the RCP in a future column, once I have got to know it better, but in the meantime I hope this example has been useful.

If anyone wants to point out improvements in the way this application was written, please feel free to contact me. I am eager to learn more about Swing development now that there is finally a tool that makes it easy enough for me to be interested again. It's hard to believe that it has taken this long for a really good Swing GUI creation solution like this to come along, but I am very glad that it has.

Further Reading/Resources

About the Author

Dick Wall is a Principal Systems Engineer for NewEnergy Associates, A Siemens Company based in Atlanta, GA that provides energy IT and consulting solutions for decision support and energy operations. He can be reached for comment on this and other matters at dick.wall@newenergyassoc.com. He also co-hosts The Java Posse, a podcast devoted to Java news and the Java community, which can be found at http://javaposse.com.

Page 2 of 2

Comment and Contribute


(Maximum characters: 1200). You have characters left.



Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

By submitting your information, you agree that developer.com may send you developer offers via email, phone and text message, as well as email offers about other products and services that developer believes may be of interest to you. developer will process your information in accordance with the Quinstreet Privacy Policy.


We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.
Thanks for your registration, follow us on our social networks to keep up-to-date