Lesson 12.1 GUI Applications
Applications, you recall, are different from applets in several ways. Applications don't run inside a Web browser, like applets do. Instead, applications run on your local machine, just like a program written in Pascal or C. In this lesson, you'll learn how to write GUI [Graphical User Interface] Java applications to go along with the Console Mode applications you mastered back in Lesson 6.5.
![Applets vs Applications. [Click image to enlarge].](apt_sm.gif)
If you recall, a Java application contains a static method named main(), and you run your application by using the Java interpreter, java. The Java interpreter implicitly calls your main() method when started.
|
Running on the Mac
|
| To run an application on the Macintosh [at least using pre OSX], you use the JBindery program. JBindery is kind of like the jar tool and the appletviewer for applications, rolled into one.
With JBindery, you can put all your classes in a single file and then run them as a regular double-clickable Mac application. Much easier than in the Windows/Unix world. |
Graphical Java applications, as opposed to the console-mode applications, also have one other requirement: you have to give them a "place" to live. Here are some of the possible habitats for a GUI app:
As you can see from this fragment of the AWT class hierarchy, the Container class--the subclass that can hold other components--has two main subclass branches: Panel and Window.
The Panel class, and its subclass Applet, must live inside a "top-level" window, such as that provided by your Web browser. The Window and Frame classes have no such restrictions; they are top-level windows.
When you create a graphical application, you'll have to create a Frame object to house it. Let's see how that works.
Meet the Frame
A Frame is a GUI window that holds a Java application. You don't need to use Frames when you write applets because your Web browser provides the Frame for your applet.
Creating a Frame is easy. There are two constructors:
Frame myFrame = new Frame();
Frame myFrame = new Frame(“Title”); |
The first constructor creates a Frame with no title, while the second allows you to specify a String to appear in the Frame's title bar. Normally, each of your graphical Java applications will extend the Frame class. Let's take a look at this basic framework you'll use for all "Graphic Apps". We'll do that by developing a few framework classes that you can later build upon for your specific applications.
Your Basic Graphic App
The first step is to create a new application, [let's call it GuiApp]:
- Extend the Frame class.
- Next, import the AWT classes, [unlike your console apps], but don't add the java.applet package. Make sure you remember the java.awt.event package as well.
Your class should look like this:
// A GUI Application Framework
import java.awt.*;
import java.awt.event.*:
public class GuiApp extends Frame
{
}
|
Attributes and Methods
Add two constructors and a main() method to your class. Here's what you should do:
- Add a main() method, and inside the method make a new GuiApp object like this:
| GuiApp app = new GuiApp(); |
The Frame class has two constructors, a default constructor with no arguments, and a working constructor that takes a String for the title. To allow the GuiApp class to take the place of any Frame, it must also supply both constructors.
For the working constructor, simply pass the title supplied to your constructor on to the superclass constructor like this:
public GuiApp(String title)
{
super(title);
} |
For the default constructor let's use "GuiApp" for the title. You can use the this() method to call your working constructor from your default constructor, like this:
public GuiApp()
{
this("GuiApp");
} |
Your finished class should look like this:
// A GUI Application Framework
import java.awt.*; import java.awt.event.*:
public class GuiApp extends Frame>
{
// Constructors ----------------------------
public GuiApp(String title)
{
super(title);
}
public GuiApp()
{
this("GuiApp");
}
public static void main(String[]args)
{
GuiApp app = new GuiApp();
}
}
|
Now compile it using javac and run it using java like this:
C:\> javac GuiApp.java
C:\> java GuiApp |
What happens? On my system, everything compiles OK, but when I run the program, I get this:
Nothing appears AT ALL! What is going on?
Working With Frames
The program just ends immediately without even an error message. Why doesn’t the program display? What's wrong?
Remember that every GuiApp object IS A Frame object. That creates two problems:
- The default size for a Frame object is 0x0 pixels large. This is too small for most of us to see, even wearing our bifocals. To fix this, send the setSize() message in the constructor. For right now, make your app 300 pixels wide and 250 pixels high.
- The second problem is that Frame objects are not visible by default, like Button and Label objects. To fix this, use the show() method, but call show inside the main() method, instead of inside the constructor.
Here is the change you have to make to your constructor:
public GuiApp(String title)
{
super(title);
setSize(300, 250);
} |
Here is the change you have to make to your main() method:
public static void main(String[]args)
{
GuiApp app = new GuiApp();
app.show();
} |
Now, if you compile and run with java, you'll see something like this:
Much, much better. If you play with GuiApp for a moment, you'll see that your Java application acts just like a "real" window.
- You can move it around the screen.
- You can resize it.
- You can minimize it, [and maximize it as well].
- You can close...Uh Oh! Looks like something's wrong here. The window doesn't go away. Why?
The problem is that Frames do not “listen” for WindowEvents, such as the "close" button being pressed. Instead, you have to "subscribe” to the "closing" event to receive it. You'll see how to do that in the next section.
|
Closing the Frame
|
| Even though the GuiApp Frame doesn't respond to WindowEvents, you don't have to shut down your computer to kill the GuiApp window. Instead you just need to kill the GuiApp!
In Windows and Unix, you do this by selecting the shell window [the DOS window] where the application was launched, and then pressing CTRL+C. [Hold down the CTRL key and press C].
This doesn't close the Frame, but it does stop the GuiApp application from running, and when the application stops running, it takes its Frame with it.
Java 2 Note
In Java 2, you'll see that the Frame does close when you click its Close button. Just like with Java 1.1, however, the application is still running. Even with Java2 you'll need to take the steps shown here for Java 1.1 whenever you want to stop a graphical application. |
Closing the Window
An application's main window acts differently than the other windows in your program. Most of us expect the MS Word application to close down whenever we close the main MS Word window. We certainly don't want Word shutting down whenever we close a particular document window, however.
Let's extend that behavior to Java by adding window-closing behavior to GUIApp Here are the steps to follow:
Step 1: Implement WindowListener
The GuiApp class is already a subclass of Frame, but it also needs to listen for window events. Do that by implementing the WindowListener interface, just like you did for ActionListener and AdjustmentListener.
Your new class should look like this:
public class GuiApp extends Frame
implements WindowListener
{
// Rest of class here
} |
Step 2: Stopping the App
To "close" your GuiApp, you can send it the dispose() message. The problem is, your program is still running, even with your main window in the "bit bucket."
To actually quit your application--that is, to stop the program from running--you use the method: System.exit(). The exit() method stops your program and also returns a value to the operating system. The normal practice is to return 0 if no error has occurred, and return an error code otherwise. In some operating sytems, [notably Unix, but also DOS], the O/S can make use of the returned code. In the Unix world, however, there is no standardized set of error codes. In the cross-platform Java world, you should probably just return 0 no matter what. Now that you know what to do [call System.exit()], the next question is where you should add the code. Simply add a method to your application called quitApp(), that looks like this:
public void quitApp()
{
dispose();
System.exit(0);
} |
Step 3: Hooking up your code
Now that you have the code to stop your application, you have to hook it up to the WindowEvent that occurs when the user clicks on your window's "close" box. That event is called the windowClosing event.
To intercept this event, you implement the WindowListener interface. To implement the WindowListener interface you have to add seven different methods to your class:
|
Method
|
Purpose
|
| windowOpened() |
Called after window is first opened. |
| windowClosed() |
Called after window closed, provided application is still running. |
| windowClosing() |
Called when window starts closing. Put your code to stop the application here. |
| windowIconified() |
Called when your window is minimized. [No way to tell if window is maximized.] |
| windowDeiconified() |
Called when your window returns from being an icon. |
| windowActivated() |
Called when window becomes the active window. [Like gaining focus] |
| windowDeactivated() |
Called when another window becomes active. [Like loosing focus.] |
Each of these is a public void method that takes a WindowEvent as its argument. Add each method to your GuiApp class, leaving the body of each method empty, except for the windowClosing() method where you should call your quitApp() method like this:
public void windowOpened(WindowEvent we) {}
public void windowClosed(WindowEvent we) {}
public void windowIconified(WindowEvent we) {}
public void windowDeiconified(WindowEvent we) {}
public void windowActivated(WindowEvent we) {}
public void windowDeactivated(WindowEvent we) {}
public void windowClosing(WindowEvent we)
{
quitApp();
} |
The last step is to add this line:
to your working constructor. This makes sure that every GuiApp you create shuts down the application when its main window is closed.
|
Implementing an Interface
|
| What, exactly is an interface? What's the difference between extending a class and implementing an interface?
An interface is like a class with all empty methods [methods with no body] that is defined like this:
public interface MyInterface
{
public void doThis(int n);
public void doThat(byte b);
}
Notice that instead of the keyword class, the keyword interface is used instead. Also, note that each method consists of a regular header, followed by a semicolon instead of a body. To use an interface, you implement it, unlike a class, which you extend. Implementing an interface means that you write a method body for every method declared in the interface, like this:
public class MyClass
implements MyInterface
{
public void doThis(int n)
{
/* code */
}
public void doThat(byte b)
{
/* code */
}
}
As you know, each class can extend only a single class, but a class may implement several different interfaces. |
Finishing Up
Now that MainFrame can close itself, let's add one more change; let's install a FlowLayout layout manager, just like the one used for an applet. The default layout manager for the Frame class is BorderLayout, but by changing to FlowLayout, you don't have to remember whether you're writing an application or an applet before adding components to your program.
Here is the finished GuiApp class:
|
GuiApp.java
|
// A GUI Application Framework
import java.awt.*;
import java.awt.event.*;
public class GuiApp extends Frame
implements WindowListener
{
// 1. Working constructor --------------------
public GuiApp(String title)
{
super(title);
addWindowListener(this); // Window events
setLayout(new FlowLayout(FlowLayout.CENTER));
setSize(300, 250); // Default size
}
// 2. Default constructor ---------------------
public GuiApp()
{
this("GuiApp");
}
// 3. Quit the application
public void quitApp()
{
dispose();
System.exit(0);
}
// 4. WindowListener methods -------------------
public void windowOpened(WindowEvent we) {}
public void windowClosed(WindowEvent we) {}
public void windowIconified(WindowEvent we) {}
public void windowDeiconified(WindowEvent we) {}
public void windowActivated(WindowEvent we) {}
public void windowDeactivated(WindowEvent we) {}
public void windowClosing(WindowEvent we)
{
quitApp();
}
// 5. The main() method -------------------------
public static void main(String[] args)
{
GuiApp app = new GuiApp();
app.show();
}
}
|
Using GuiApp With Inheritance
Now that you've finished the GuiApp class, let's see how you'd use it to create a real-world application. The first way to do that is by using inheritance and extending GuiApp instead of extending Frame or Applet.
Here's an example app based on GuiApp, that uses contains a TextField, a TextArea, and a Button. When the user enters some data into the TextArea [phone numbers in this example] and then presses ENTER, the data is copied into the TextArea, and the text in the TextField is selected, waiting the next entry. When the user presses the Clear button, the text in the TextArea is erased.
Step 1: Create the FirstGuiApp Class
Create a new file named FirstGuiApp.java, and place it in the same directory as a copy of GuiApp.java [or GuiApp.class ]. Your new class should contain the traditional AWT import statements, extend GuiApp, and implement ActionListener like this:
// A first GuiApp using GuiApp.java
// The GuiApp framework using inheritance
import java.awt.*;
import java.awt.event.*;
public class FirstGuiApp extends GuiApp
implements ActionListener
{
// Additional data and methods
}
|
Step 2: Define New Attributes
The FirstGuiApp class will need two attributes:
- a TextArea that the data will be copied to, named ta.
- a TextField where users will enter data, named tf.
Make these private fields in the class as shown here.
// Attributes ------------------------------
private TextArea ta = new TextArea();
private TextField tf = new TextField(15); |
The other data elements can be local variables, because you don't need to interact with them as the application runs.
Step 3: Add a Constructor
The GuiApp class has two constructors, but you don't have to support both constructors in your subclass. Let's give FirstGuiApp a single constructor, one that takes a String. Here's what the constructor has to do:
- call the superclass constructor. You must do this explicitly because you don't want to use the default superclass constructor.
- set the layout to BorderLayout, overriding the FlowLayout that GuiApp uses.
- Create a local Button object and hookup the ActionListener for both the Button and the TextField object tf.
- Create a Panel object, arrange the Button, the TextField, and a descriptive Label to the Panel, and add the Panel to the top of the app.
- Add the TextArea to the center of the app.
Here's the completed code:
// Constructors ----------------------------
public FirstGuiApp(String title)
{
super(title);
setLayout(new BorderLayout(2,2));
Button btn = new Button(" Clear ");
btn.addActionListener(this);
tf.addActionListener(this);
Panel p = new Panel(new FlowLayout(FlowLayout.LEFT));
p.add(new Label("Enter names :", Label.RIGHT));
p.add(tf);
p.add(btn);
add(p, BorderLayout.NORTH);
add(ta, BorderLayout.CENTER);
}
|
Step 4: Respond to Events
Your application implements ActionListener, so you have to add an actionPerformed() method. Since both the TextField and the Button object call the same method, use the instanceof operator to determine whether the Clear button was pressed, or whether the user pressed the ENTER key after adding some new data, and respond accordingly.
Here's what your code should look like:
// Copy the text or clear if button pressed ---------
public void actionPerformed(ActionEvent ae)
{
if (ae.getSource() instanceof Button)
ta.setText("");
else
ta.append(tf.getText() + "\n");
tf.selectAll();
} |
Step 5: Add a main() Method
Your new application needs a new main() method; you can't reuse the main() method from the GuiApp class. Since FirstGuiApp has only a single constructor, your new method should:
- See if a command-line argument was passed If so, then construct a new FirstGuiApp using args[0] If not, construct a new FirstGuiApp using a String literal
- Resize and display the application
Your code should look something like this:
// main() creates app using command-line ------------
public static void main(String[] args)
{
FirstGuiApp app = null;
if (args.length > 0)
app = new FirstGuiApp(args[0]);
else
app = new FirstGuiApp("Untitled");
app.setSize(350, 300);
app.show();
} |
Using GuiApp as a Template
Instead of using GuiApp via inheritance, you might prefer to use the class as a template. To do this for FirstGuiApp.java, for instance:
- make a copy of GuiApp.java and save it as FirstGuiApp.java. Use your editor's search-and-replace feature to change GuiApp to FirstGuiApp
- add your changes directly to the new FirstGuiApp.java file itself.
The template method is especially powerful when your IDE explicitly supports it as JCreator does. Here's how to turn GuiApp.java into a JCreator template.
Step 1: Copy an Existing Template
When you first install JCreator, it comes with three existing templates. Your first step is to copy one of the existing templates. Here's how:
- Locate the JCreator Templates directory and make a copy of the folder Template_1.
- Rename the folder to Template_4 like this:
Step 2: Modify Project_Name.java
Open the file named Project_Name.java located in the Template_4 directory. You can use JCreator itself to modify the files. Make these initial changes:
- Remove the comments from the header, and, if you like, add your name, etc.
- Remove the myprojects part of the package statement.
- Add implements WindowListener to the header.
- Remove the code inside the braces of the body of the class.
At this point, your file should look like this:
Next, add the code from GuiApp.java in between the braces. Then, use search and replace to change all instances of GuiApp to <PROJECT_NAME> as shown here:
The result should look something like this:
Save the file and then you're ready for Step 3.
Step 3: Modify setup.tst
After you've saved Project_Name.java, open the file named setup.tst in the Template_4 folder. You'll need to change the type of file to All Files(*.*) before you can see the file:
Change the project description following [LABEL] to "GuiApp-based Java Application", as shown here, and then save the file, and close the current workspace and all open files.
Step 4: Use the Template
Once all projects are closed, choose New from the File menu, or click the New Document toolbar icon to open the New Projects dialog tab as shown here:
Select the GuiApp-based Java Application [which you just created] and type in a name in the Project name text field. Note that the JCreator automatically creates a directory inside your home directory to hold the project files. Press OK, when your screen looks like the image above.
When the template first runs, it will create a Java class file containing a package of the same name as your project, translated to lowercase. Inside the file JCreator replaces every instance of <PROJECT_NAME> with the name of your project. To open this file in the JCreator editor, double-click the file name, here FirstGuiApp.java, in the Workspace window on the left:
Make your changes to FirstGuiApp.java, and then use the Compile Project button, shown below, to compile the project. You can also press F7 or choose Project | Compile Project from the main menu. [Note that the Compile Project button is different than Compile File.]
Once the project is compiled, you can run it by pressing the Run Project button appearing to the right of the Compile Project button.
In the next lesson, you'll take a closer look at the Frame class, so you know a little more about the messages that GuiApp responds to.
Please continue to the next section of this lesson.
|