MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Java Programming on the Mac

Building a Simple Java Application in Mac OS X

by Daniel H. Steinberg
08/03/2001

One of the cool things about writing a column on Java for Mac OS X is that there are many different types of people interested in the subject. Two of the biggest divisions are Java developers who are interested in what Mac OS X has to offer, and developers interested in creating Mac OS X applications who think that Java might be the right vehicle.

Last month's column addressed the first group. The goal of that article was for Windows developers and Unix experts to stop and say, "I didn't know you could do that on a Mac." If you are in this first group, you may want to skip to the end of this piece and play with this month's challenge.

In this article, we'll take things a little bit more slowly and create a simple Java application that doesn't do very much at all. This will allow you to see the basic steps needed for getting a simple GUI application up and running. This is not an intro to Java or object-oriented programming, but I will take time to discuss some of the start-up issues and point you in the direction of other references.

The current plan for this column is to alternate between the two target groups. As always, if there are topics you'd like to see covered or any thoughts about future articles, drop me a line at dsteinberg@core.com.

Note: Apple has posted a developers' preview of Java 1.3.1 on the ADC. Even free members are entitled to download the upgrade and the documentation. Check it out. Borland recommends that you have this version installed before using its JBuilder 5 beta version for Mac OS X.

Getting started

Comment on this articleTry building this application and let us know how it went.
Post your comments

Also in Java Programming on the Mac:

A Rendezvous with Java

Integrating Ant with Xcode

Transforming iCal Calendars with Java

Apple Releases Java 1.4.1 for Mac OS X

Getting Fit for the Holidays

First create a directory inside your user directory. You can do this by opening up the Finder and navigating to

Mac OS X/Users/<user name>/

In my case, my user name is "dhs" so I will create a new folder named "NineSquares" inside of Mac OS X/Users/dhs/. Now open up the Terminal application. As a reminder, you can find this in Mac OS X/Applications/Utilities/. You may find it useful to drag Terminal to your dock. When Terminal starts up you should see a prompt that looks something like

[localhost:~] <user name>%

Type in the command ls and you will get a listing of the contents of the current directory. You should see NineSquares in the list.

Now we need a simple text editor. You can use emacs, vi, BBEdit, Alpha, or your personal favorite text editor. As your programs get more complicated, it's nice to have a text editor that does context coloring for you and perhaps matches brackets. If you want a smarter editor that does code completion (for example, suggesting the methods that are available to an object), you may want to check out one of the IDEs now available on the Mac. In this article, I'll just use Text Edit. It's free. It comes with Mac OS X and there's nothing really to learn or install.

Start up Text Edit. You'll find it in Mac OS X/Applications. We aren't going to want to save this as Rich Text so select "Make Plain Text" from the Format menu. Now choose "Save As" from the File menu and navigate to the NineSquares folder you just created. Name the file NineSquares.java and click "Save". Text Edit wants to save this as a .txt file but it knows that something may be wrong and it presents you with the following warning.

Screen shot.

Select "Don't append" and you will have created and saved the appropriately named empty document NineSquares.java. Now go ahead and type in the following.

package NineSquares;

import javax.swing.JFrame;

public class NineSquares {
 public static void main( String args[]) {
  JFrame myFrame = new JFrame("Nine Squares");
  myFrame.setVisible(true);
  myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 }
}

The execution of the program will begin with the body of the main() method. In this case, we're creating a new JFrame named myFrame that will have "Nine Squares" in the title bar. It would be nice to see what we've created, so we need to make myFrame visible.

The third step ensures that the red "close" button behaves properly. We don't just want the red button to close the window we're creating -- we want it to quit our application. So we set the default close operation to EXIT_ON_CLOSE. There's no way you can keep everything in your head when you're programming. You're going to forget what a method is called or what arguments it takes. Use the JavaDocs that come with the Java installation. You'll find them at: file://localhost/System/Library/Frameworks/ JavaVM.framework/Versions/A/Resources/English.lproj /Documentation/Java/index.html.

For example, to find out what's available to you in JFrame navigate down the list in the lower left until you get to it. Click on it and you will see a complete description in the lower-right corner. By following the links you can usually answer the questions you have.

Back to our first program.... If you tried compiling and running with just the class declaration and the main() method it contains, you would get a complaint from the compiler that it has no idea what you mean by JFrame. You need the import statement to acquire the information about JFrame.

You could have imported the entire javax.swing package with the command import javax.swing.*. I tend to be specific about what I'm importing. This used to be recommended for performance reasons. Although performance is no longer affected, I like to do this so I know the dependencies of this class. It also helps me when I'm cleaning up after removing a reference or two.

The final addition to NineSquares.java is the package declaration. I'm going to end up with several files so I'd like to keep them all in a single package. Physically, this means that I will keep them all in the directory NineSquares, but programmatically I'd also like to think of them as a unit.

To compile your file, go to the Terminal application and type javac NineSquares/NineSquares.java. Then hit return. This indicates that you want to compile the NineSquares.java file inside the NineSquares directory. The next prompt should appear and now you can run your application by typing java NineSquares/NineSquares. A small window will appear. Resize the window and drag it around -- press the green, yellow, and red buttons and you'll see that you get the proper behavior of a window for free.

Refactoring

Related Reading

Learning JavaLearning Java
By Pat Niemeyer & Jonathan Knudsen
Table of Contents
Index
Sample Chapter
Full Description
Read Online -- Safari

Before moving on, let's reorganize NineSquares.java so the functionality is split between two files. This process of improving existing working code is called refactoring. For more on this process check out Martin Fowler's Refactoring Web Site. For now, we aren't going to do anything fancy. We'll use inheritance to create a subclass of JFrame called a MainFrame that sets its default close operation and visibility when it is created.

All I want from NineSquares.java is that it create an instance of MainFrame that we can still call myFrame. Here's the code that you'll put in the file MainFrame.java.

package NineSquares;

import javax.swing.JFrame;

public class MainFrame extends JFrame{

  public MainFrame() {
    super("Nine Squares");
    setVisible(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
}

You'll notice that MainFrame extends JFrame. The first thing we do, therefore, inside of the MainFrame constructor (the method with the name MainFrame()) is call the JFrame constructor that takes a String as input. In our first version of the program, we did this directly with the call new JFrame("Nine Squares"). You can see that the constructor sets up the JFrame just as it did before. What's left is to modify the NineSquares.java file as follows.

package NineSquares;

public class NineSquares {
  public static void main( String args[]) {
    new MainFrame();
  }
}

There's almost nothing here. All that we do is create a new MainFrame() and then walk away. Notice that we don't even need to import the JFrame class because we are creating a MainFrame and MainFrame belongs to the same package as NineSquares.

Save your changes and then compile these files from the Terminal. Run the application exactly as before. This example is very trivial, however the change we just made illustrates an important point. We shouldn't have to make any changes to NineSquares.java again. Its job is well defined: It gets the application up and running by creating a special JFrame called a MainFrame. Similarly, all of the ways in which we have specialized JFrame are contained in the simple class MainFrame. Now we know where to go to add functionality and features.

Living color

Now we are going to add a panel to the MainFrame. In our first iteration, we'll start out with a blue panel and then we'll give the user the ability to choose the color of the panel. We'll create an extension of the JPanel class that is in the javax.swing package. Go to the JavaDocs and select JPanel.

You'll see that it inherits the setBackground() method from JComponent. Click on setBackground() and you'll see that this method takes a color as its argument. Follow the link by clicking on "Color" and you'll see that there are several ways of specifying a color. We'll start by using the constant Color.blue. Create a file called EachSquare.java and save it as before to the NineSquares directory. It should look like this.

package NineSquares;

import javax.swing.JPanel;
import java.awt.Color;

public class EachSquare extends JPanel {
  public EachSquare() {
      setBackground(Color.blue);
  }
}

By now you should be an old hand at this. We've specified that EachSquare is a subclass of JPanel and in the constructor, we've set the background color to blue. In addition, we've had to import the information about JPanel and Color and we've assigned EachSquare to belong to the NineSquares package. What's remains is for us to modify MainFrame.java to add a blue JPanel to itself. This is a bit more complicated with JFrame than it is for other components. You need to add the EachSquare object to the JFrame content pane and not directly to the JFrame. The new code looks like this.

package NineSquares;

import javax.swing.JFrame;

public class MainFrame extends JFrame{
  public MainFrame() {
    super("Nine Squares");
    getContentPane().add(new EachSquare());
    setVisible(true);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
}

The additional line is highlighted above. We created a new instance of EachSquare and added it to the content pane. Now when you compile these files and run the application, you should get the same little application window but now the background will be blue.

Let the user pick the color

Instead of hard-coding the color into the application, we could allow the user to select the background color when the application starts up. We'll do this by displaying a JColorChooser that allows the user to pick from a palette of colors. One nice resource for viewing your widget options for Swing components is the Visual Index to the Swing Components. It is part of the Java Tutorial. Just remember -- they all look better running on a Mac.

The JColorChooser allows the user to select a color and it returns a color that can be used as the argument to the setBackground() method that we used in our first iteration of EachSquare. Alter the EachSquare.java code as follows.


package NineSquares;

import javax.swing.JPanel;
import java.awt.Color;
import javax.swing.JColorChooser;

public class EachSquare extends JPanel {
 public EachSquare() {
  setBackground( JColorChooser.showDialog(
    this, "Choose Background Color", getBackground()));
 }
}

To run the JColorChooser from a dialog box, we call the JColorChooser.showDialog() method. It takes three arguments. The first is the component that we're setting the color for. The second is a useful message to the user. The third is the initial color of the component being colored in. It is up to you to make sure these items all match.

If you are getting and setting the background color and send a message to the user such as "Choose Font Color," you will baffle the user. Notice that you have isolated the changes from the other objects. Your NineSquares object and MainFrame object do not need to know how the color is being selected. Save your changes, compile and run the application, and the following screen will pop up before you see your floating window.

Screen shot.

NineSquares

Now let's add more than one square to our frame. When you are arranging visual components within a container you are leaving the world of the Aqua interface guidelines. The Java way is for you to rely on Sun's Layout Managers. You can choose how you want the basic container to be laid out, and then add components to the container and let the Layout Manager decide where they go.

In our case, we will add nine squares in a three-by-three grid using the GridLayout. Instead of creating one EachSquare object, we will create nine of them and add them to the the MainFrame content pane. The MainFrame.java file needs to be modified like this.

package NineSquares;

import javax.swing.JFrame;
import java.awt.GridLayout;

public class MainFrame extends JFrame{
 public MainFrame() {
  super("Nine Squares");
  getContentPane().setLayout(new GridLayout(3,3));
  for (int i = 0; i<9; i++) {
   getContentPane().add(new EachSquare());
  }
  setVisible(true);
  setDefaultCloseOperation(EXIT_ON_CLOSE);
 }
}

We need to import the GridLayout class, and then we set the layout of the content pane to a new GridLayout that is three by three. Next, we create nine EachSquares and add them to the content pane. We're getting a lot with very few code changes. Now let's take a look at the changes to EachSquare.java.

package NineSquares;

import javax.swing.JPanel;
import java.awt.Color;

public class EachSquare extends JPanel {
 public EachSquare() {
  setBackground(new Color((int)(Math.random()* 16777215)) );
 }
}

We've just created a color in a new way. Instead of specifying a constant color or using a JColorChooser, we're picking a random number between 0 and 2^24-1. You'll notice that we didn't have to import the Math class. This is because it is part of the java.lang package and is automatically available to you. When you run the application, you get a window that looks something like this.

Screen shot.

Responding to mouse clicks

So far, our application isn't very responsive. In this next version, we'll still bring up nine squares where the colors are randomly selected, but this time we'll give the user the chance to alter them. If a user clicks on a square, a JColorChooser dialog will pop-up and allow them to change the background color. The only changes we will make are in the EachSquare.java file. It will now look like this.


package NineSquares;

import javax.swing.JPanel;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JColorChooser;

public class EachSquare extends JPanel {
  public EachSquare() {
    setBackground(new Color((int)(Math.random()* 16777215)) );
    addMouseListener(new MouseClickListener());
  }
  public class MouseClickListener extends MouseAdapter{
    public void mouseClicked(MouseEvent e) {
      setBackground(JColorChooser.showDialog(
        EachSquare.this, "Choose Background Color", getBackground()));
    }
  }
}

The big change here is the MouseClickListener. This subclass of MouseAdapter waits around for a message that there's been a mouse click that it cares about. It then creates a JColorChooser with a beginning color matching the background color of this panel. When the user selects the new color and dismisses the dialog box, the background color for the panel is set to this new choice.

The MouseClickListener is actually defined from inside the EachSquare class and is called an "inner class." The line addMouseListener( new MouseClickListener()) is what associates the MouseClickListener with this particular panel. When a panel is clicked on, its MouseClickListener responds. The only other changes to the file are to add the appropriate imports.

Summary

Although this wasn't really an introduction to Java, I hope it helps you see how easy it is to create simple applications for Mac OS X using the language. You may find that you work better with an IDE that does most of this visual work for you. With most of them, you can drag these visual components onto a workspace and quickly customize the properties to get the look and action that you want. For an application of this scope, it wasn't that hard to implement it from scratch using simple tools.

As a challenge, take a look at this month's issue of Mac Tech. Its challenge is to get you to write a version of Dots. The puzzling thing, however, is that Mac Tech has specified the IDE you must use and a choice of languages. Java is not among your choices. For kicks, let's take a shot at Dots in Java. Feel free to use the discussion forums below to enlist the help of others.

Daniel H. Steinberg is the editor for the new series of Mac Developer titles for the Pragmatic Programmers. He writes feature articles for Apple's ADC web site and is a regular contributor to Mac Devcenter. He has presented at Apple's Worldwide Developer Conference, MacWorld, MacHack and other Mac developer conferences.


Read more Java Programming on the Mac columns.

Return to the Mac DevCenter.

Copyright © 2009 O'Reilly Media, Inc.