Building Java GUIs with Matisse: A Gentle Introduction
It is a curiosity that when Java was first introduced, it made a name (some would say "became synonymous with") simple interactive GUIs and animations in applets embedded on Web pages. It was the first foothold for the language. At the time, the GUI construction was simpler than a lot of the alternatives out there (it was the time of Motif, windows 3.1 and does anyone remember NeWS?).
It was also a little limiting—AWT provided a decent way of getting a few buttons and menus put into an applet or even a Java application if you were ambitious, but the results were fairly limited and simple.
To combat this and bring the possibility of richer desktop applications and a higher abstraction to GUI building in Java, Swing was created. Swing learned much from other GUI toolkits available at the time and brought in a lot more flexibility... and some more complexity.
Fast forward to a year ago and the state of the art for Swing creation revolved around numerous layout managers, the most flexible (and notorious) being the GridBagLayout. This is an amazing layout manager that lets you control just about every aspect of every widget placement in every form. The problem is, it requires you to.
Netbeans, and various other IDEs, already had good support to help with creating grid bag layouts, but these usually involved a different view of the UI from the WYSIWYG one, and it takes a while to learn all the controls and settings even in these helper tools.
In NetBeans 5.0, however, a new option is available. Matisse is a new GUI builder that uses the GroupLayout manager. This is a new layout manager that is not a part of standard Swing (yet), but can be freely distributed with your application.
Matisse is Java UI building done right. It is familiar, offers drag and drop placement, and sensible defaults that can still be altered and overridden easily for flexibility. It is a GUI builder as easy to use (with a couple of minor exceptions—more on those later) as Visual Basic, but has a huge advantage over VB in that the resulting forms and panels can be re-sized in a meaningful way, without having to write all sorts of resize event handling code.
This article will outline some of the new features in Matisse and is intended to give you a taster of what is possible with it. It details a real application (albeit very small and simple) that I recently wrote to solve a real-world need. My hope is that this will demonstrate what is possible with Matisse, and whet your appetite to find out more.
I will also point out that I am not generally a Swing or even a GUI developer. I may commit atrocities in this article from a UI perspective and, if I do, apologies. However, my point here is to show that anyone can use Matisse to make an application—even a server-side hacker like me.
Before following along with this article, you will need to grab NetBeans IDE 5.0 from http://www.netbeans.org/ and install it. I also recommend reading or printing out the Matisse GUI Builder Legend at http://www.netbeans.org/kb/50/quickstart-gui_legend.html; it provides a handy cheat sheet of what the various guidelines mean when creating a GUI in Matisse.
The source code for this example can be downloaded from here.
About the Application
You may or may not be aware, but I am co-host of a podcast all about Java called the Java Posse along with three other guys (Tor Norbye, Carl Quinn, and Joe Nuxoll). We record at least once a week and have adopted the approach of everyone recording their own microphone feed, and then mixing the results into a single audio file using a multi-track audio editor. This produces great results, but also takes a fair chunk of time. The longest part of the editing process is removing the mistakes or "Flubs" as we call them. This can be long pauses, words that are tripped over, more than one of us talking at the same time, and so forth. Because we are not audio professionals, and are using skype (with the inherent lag), there tends to be quite a lot of these.
As a result, I try and capture these flubs as they happen. To start with, I just wrote them down, with a rough time, but this took too long and too much attention. So, I decided to write a simple GUI app to make the job easier, and to spit out the results in the correct format for Audacity labels, which is the multitrack editor I use to edit the shows.
The format for the labels file in Audacity is very simple. It consists of a start time and end time, in seconds (with decimal fractions), and then a text table.
To keep my "flubber" application simple, I make the start and end time the same, so markings refer to an instant, not a time period. Also my labels are simply a counter, starting at 1 and counting up (this way I don't miss any when scrolling through the audio).
So, my file might look like this:
My flubber application (or jflubber as I call it, to avoid any legal issues with Robin Williams) is a simple way of starting a clock, marking flub points as instantaneous moments of time, and spitting out these moments as a text file that can be loaded into Audacity. Simple, but useful and effective.
Okay. Now it's time to build jFlubber. First, you need to create a Java Application, so fire up NetBeans 5.0 and:
- From the file menu, select New Project...
- In the new project dialog, select General in the left pane, and Java Application in the right pane, Click Next.
- Choose a project name (jFlubber?) and a package and name for the main class (I chose org.bldc.jflubber.Main because I own the bldc.org domain). Click Finish.
- The Main class will be created and fleshed out with a minimal implementation.
Create the UI
Now for the fun part. You will paint your UI using Matisse. First, you need a new form:
- In the projects pane (top left), right-click on the package you created with the Main class inside of it (note that you need to click on the package node, not the Main class), and select New -> JFrame Form...
- Name the form. I suggest something like JFlubberMainFrm (don't use a lowercase j at the beginning of the name because the convention in Java is for classes to start with uppercase).
- Leave everything else as default and click Finish.
- You should see a new empty grey form on the page, ready to paint the GUI into.
Your GUI needs a text label, five buttons, and a text area. The buttons are:
- Start: To start the timer
- Stop: To stop the timer
- Flub: Nice big easy to hit button to mark a flub point in the recording
- Save: Save the labels from the text area out to a text file
- Clear: Clear the text area ready for a new flub session
The text label will be at the top and will show the current time elapsed since the start button was pressed, in minutes and seconds. Why not hours, minutes, and seconds? Why not microseconds? Well, because I prefer minutes and seconds. You are, of course, free to format this stopwatch display however you like.
The text area will hold the flub points formatted in the Audacity label format. It's not strictly necessary to see this, but it is a nicety to be able to get visual feedback when a flub point is added.
To see what you are aiming towards, the finished UI should look like this:
To create this UI in Matisse, first set up the time label:
- From the component palette in the top right of the IDE, drag a jLabel over the form but don't drop it right away. Instead, move it towards the top left of the form until you see two dashed lines about a quarter of an inch from the top and the left side of the form appear. These are your guidelines in Matisse; they help you position components in a way that the GroupLayout manager knows how to resize them.
- Next, grab the right end of the new label component and drag it towards the right of the form, until you see another guideline appear about a quarter of an inch from the right side of the form.
- By doing this, you have told Matisse to keep this label anchored to the top, left, and right sides of the form, expanding the label as necessary when the form is resized.
- Next, you want a nice big font for the timer label. Select the label by clicking on it (if it is not already selected), and look in the jLabel1 properties (right side of the IDE) for the Font property. Click on the ... .
- I suggest Dialog, Bold, 18 pts for the text fonts, but you can use anything you like here.
- Click OK, You also want the label text centered (it looks nicer), so select CENTER for the HorizontalAlignment property.
- I suggest renaming the label to something more meaningful than jLabel1, so find it in the inspector pane (left side of the IDE) and click on jLabel1 there and leave the mouse over it for a second or two. It should go into rename mode. Iype in a better name, like timerLabel, and click return.
- Finally, the label still reads jLabel1 on the form. It would be better if it read 0:00, so in the properties (or by double-clicking on the label itself) change the text to read 0:00.
That seems like a lot of steps, but that's because I took the description very deliberately. In fact, with a little practice you will be creating and placing UI components very quickly and easily in Matisse. Next, you can add the buttons:
- Grab a jButton from the component palette and drag it to the top left of the form again, under the timer label this time, until the guideline appears on both the top and left of the button. Drop it.
- Do the same for a second button, but this time drop it on the right side of the form. This time, however, instead of a guideline above the button from the label, you will probably see a new type of guideline that runs through both buttons just under the text label (to align the text) as well as the familiar anchor guideline on the right of the button. When you see this, drop it.
- Now, for the clever bit: Starting with the left button, drag the right hand side into approximately the middle of the form. You will not see a guideline yet. Don't worry, and you also don't have to get it exactly half way, just approximate.
- Next, grab the left side of the right button and drag it left until it is about a quarter of an inch from the other button. This time, you should see a guideline appear. When you do, let go.
- Next, you want to make the buttons actually be the same size. Select both buttons by first selecting one, then shift-clicking on the other. Check that only these two buttons are selected and no other components (this is important).
- Right-click on one of the buttons and select Same Size->Same Width from the popup menu.
- When you do this, you should see both buttons resize a bit. You might also see the form change size slightly as everything gets re-aligned. Don't worry about the form size changing; you easily can change it later.
- Change the names of these buttons to startButton and stopButton (in the same manner as for the label); also, change the text of the buttons to Start and Stop.
- Now, you want the nice big flub button. This should be the full width of the form and about twice the height of the start and stop buttons (just make it look nice and big), so grab another button and lay it out anchored to the left and right of the form, and below the start and stop buttons, then drag the height down to make it big. You should be getting the hang of the layout by now. Nice, isn't it?
- Rename the component flubButton in the inspector pane, and also change the button text to read Flub. I also recommend changing the font size in the button font properties to 18pt or something nice and big.
- The final two buttons need to be added at the bottom of the form, side by side, like the start and stop buttons. Just drag two buttons down there and lay them out just like the start and stop buttons, but anchored to the bottom of the form instead. Call these saveButton and clearButton, and rename the labels to Save and Clear respectively.
- Unlike before, instead of making these buttons the same width as each other, I find it easier to select the Save and Start buttons at the same time (shift-click until these are the only two components selected) and then right-click and make them the same width as each other, then do the same for the Stop and Clear buttons. This will get all of the buttons even sized.
- Finally, now that all of these buttons are the same width, select all four of the smaller buttons on the page, and then click the <-> icon in the toolbar at the top of the so that it is toggled on.
- By doing this, you are ensuring that these buttons will resize and retain their proportionate sizes to one another when resizing the whole form. You can test this out very easily by dragging the bottom right corner of the form to resize it. You should see all of the components resizing in realtime as you would expect. If not, double check the steps here and see if you missed any.
Finally, you want to add the text area to hold the flub points and display them.
- Grab a jTextArea from the component palette and drag it into the form until guidelines appear on the left side of the form and under the big Flub button.
- Drag the bottom right of the text area to resize it until guidelines appear on the right side of the form and above the Save and Clear buttons.
- Rename the text area to flubPointsTextArea.
At this point, it is a good idea to save all (in fact, Matisse is still fairly young, so I recommend saving a lot just as a precaution).
Again, try resizing the form in the GUI editor to make sure everything re-sizes as you would expect.
Testing It Out
You have constructed your new GUI. To give it a test, you need to add a little code to the main class to cause it to be displayed:
- In the IDE, find the Main class that the application creation wizard made for you and double-click on it to open it.
- In the main method in here, replace the TODO comment with the following two lines:
- Note that if you called your form class something other than JFlubberMainFrm, you will naturally have to adapt the above code to use that name instead.
- Finally, run the project (Run from the menu or the toolbar).
- You should see your form appear for real, and it should resize as you would expect. Of course, none of the buttons are hooked up yet, so it will be a bit useless; however, you have used Matisse to lay out a GUI with resizing events already handled for you.
JFlubberMainFrm jFlubberFrm = new JFlubberMainFrm(); jFlubberFrm.setVisible(true);