|
Last time, we promised we'd show you how to create a stand-alone
graphics (or SAG) application from scratch. In this section,
we're going to live up to that promise.
Overall, the process is much like the one we used to create
the console app back in tutorial #2. However, there are a couple of new concepts and
bits of code that will require some explaining.
Let's start off by creating a new, blank project in BlueJ. As
usual, we're going to walk through the button-pushing and
file-creating pretty quickly, then explain what we've been doing
after we're finished.
Open BlueJ and select "New Project" from the
"Project" menu. Enter something like "BattleshipSAG"
when prompted for a project name.

Figure 1--Creating an SAG App from Scratch.
When the blank project browser appears, press the
"New Class" button and enter "BattleshipApp"
for the Class Name.

Figure 2--Creating a Blank BattleshipApp Class.
Your new class will appear in the Project browser as a tan box with gray
hash marks. Double click this
box and look at its code. Delete the extraneous bits until the
file looks like this:
/**
* Write a description of class BattleshipApp here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class BattleshipApp
{
// instance variables
/**
* Constructor for objects of class BattleshipApp
*/
public
BattleshipApp()
{
}
}
Just like we did with the console app, we need to
add a static main method. This will act as the starting
point for Java when we tell it to run the program. The SAG main
looks exactly like it did in the console app:
/**
* Entry point for code execution.
*/
public static void
main(String[] args) {
}
You'll probably want to change "(your
name)" to your name, and "(a version number or
date)" to a meaningful version number (like '0.1') or date.
Also, you should write a description of the class where it says,
"Write a description of class BattleshippApp here".
Hit the 'compile' button and make sure everything
works OK up to this point. Once you've got the file compiling, we
can start adding code to turn this into a valid SAG app.
But first, some theory.
Window to the World
Stand-alone graphics applications are windows
programs--they live on the desktop, respond to mouse clicks, have
close buttons and resize controls, etc. Because they have this
extra functionality, we're going to have give Java more
information about our program than we did with the Console app.
Roughly, here's a list of new data we'll have to
supply:
-
First, we have to tell Java that this is a
windowed program.
-
Second, we need to tell Java how big to make
the window in which this program will run.
-
Third, we need to add commands to pop our
window up on the desktop.
When we've added these three elements to the
BattleshipApp, we should be able to run it and have it appear on
the screen.
Doing Windows
Satisfying the first requirement is pretty
easy--we just have to say, "Hey, Java--put this program
inside a window." To do that, just change the line
public class BattleshipApp
to
public class BattleshipApp
extends JFrame
That's easy enough to write,
but what does it mean?
To answer that, we need to
touch upon a cornerstone of object oriented programming: inheritance.
Just as 'inheritance' implies a relationship between parents and
children in the real world, it suggests relationship between
parent objects and child objects in an object-oriented programming
language.
For example, in "real
life", we might say "that girl inherited her father's
eyes," meaning that her eyes look much like those of her
father. In Java, we might say "BattleshipApp inherits its
window behavior from the JFrame class," meaning that the
BattleshipApp acts like a JFrame as far as its existence on the
desktop is concerned.
Great. So what's a JFrame?
Simply put, it's a
predefined class that comes with Java and has basic window
functionality--it has a title bar, a close button, resize
controls, the works.
So, when we say that
BattleshipApp inherits from JFrame, what we really mean is
that our game will have a title bar, close button, resize
controls, etc.
We get all of these nifty
features just by adding two little words to our class definition: extends
JFrame. If it helps, you can mentally
read 'extends' as 'inherits from'. Then our class definition would
read:
public class
BattleshipApp inherits from JFrame
and it couldn't be much
clearer than that.
Make the change and
recompile BattleshipApp. You should get an error. Don't worry,
it's a small one.

Figure 4--Problems Compiling BattleshipApp.
The highlighted line tells us where the error
occurred, and the status window at the bottom tells us what went
wrong. Apparently, the compiler doesn't know what to make of the
word JFrame.
This isn't a big problem. As we mentioned above,
the JFrame object is a part of Java. More precisely, many classes
came pre-packaged with the JDK version 2 that we installed in
tutorial #1. All we have to do is tell the compiler in which
package it will find the JFrame.
That's easy enough to do. The package is called
"Swing". Swing contains a bunch of different objects
that make creating Graphics User Interfaces (GUIs) very easy. To
make all of these objects available to our program, we just have
to include the following line at the top of the file (above the
first curly brace):
import javax.swing.*;
Note that the '*' tells Java to import all the
objects in the "Swing" package. We really only need the
JFrame, so we could've written
import javax.swing.JFrame;
instead. However, we're
going to need more Swing elements later, so it's easier just to
include them all in one go.
Unfortunately, we can't stop
there. Swing depends on objects found in another package: AWT. AWT
stands for "abstract window toolkit", and forms the
basis for most of Java's graphics and user input code.
To include the objects in
the AWT package, we need to add the line:
import
java.awt.*;
to the top of our class
file.
After you add these lines,
your file should look something like this:
/**
* The basic framework for a battleship game.
*
* @author Mark Kreitler
* @version
0.1
*/
import
java.awt.*;
import
javax.swing.*;
public class BattleshipApp extends
JFrame
{
// instance variables
/**
* Entry point for code execution.
*/
public static
void main(String[]
args) {
}
/**
* Constructor for objects of class BattleshipApp
*/
public
BattleshipApp()
{
}
}
Now try compiling again.
This time, everything should work out fine.
Size Matters
Now that we've told Java
that we're a windowed program, we need to tell it how big we want
to be when we show up on the desktop. In principle, this is easy
to do--we just need to put the line
this.setSize(600,
500);
somewhere inside our object.
Doing so will make our window 600 pixels wide and 500 pixels tall.
No problem.
Except...where do we
put this line?
Well, we want it to execute
when Java creates our program window. Where does that happen?
That's a trick question. The
answer is, "right now, that doesn't happen." In
fact, we have to explicitly tell Java to create a new
BattleshipApp object. If we don't do that, there will never be a
window to receive the 'setSize' command.
Don't believe me? Try
running the program (right click on the 'BattleshipApp' box in the
project browser and select 'main(args[])' in the drop-down
box). No window appears, because we never created one in code.
So, first things first, how
do we create a new instance of BattleshipApp, and where do we put
the code?
Recall that Java begins
executing our code with the main method. You might guess
that we'd want to create our window there. As usual, you'd be
right.
The required code is very
simple. Just add this line to the main method:
new
BattleshipApp();
That tells Java to create an
instance of class BattleshipApp.
Now all we have to do is set
its size. You might be tempted to do it immediately after the 'new
Battleship()' command. That's logical--but there's a better
place. It's called the constructor,
and we're going to spend a few minutes talking about this most
special and important method.
Under Construction
You've probably noticed
that, when BlueJ generates a class skeleton for us, it always
includes a method whose name matches the class name. For instance,
in BattleshipApp, there's a public method called BattleshipApp().
Coincidence? Nope.
Java recognizes methods
whose names match the class name as constructors.
These methods have a very special property--whenever you create an
object using the new
command, Java calls the constructor immediately after creating the
object.
This is very handy. You can
place code in the constructor that sets up the object for use (we
usually call this initializing the object).
As an example, consider a
simple 'Circle' class:
public
class
Circle {
public float
radius;
public float
circumference;
// Circle constructor.
public
Circle(float
r) {
radius =
r;
circumference =
2 *
3.14159 *
r;
}
}
As you can see, the Circle
has two instance variables--radius and circumference. We can
create an instance of Circle as follows:
Circle
c1 = new
Circle(3);
When Java reaches this line,
it immediately executes the code in the Circle's constructor,
passing in a value of 3 for 'r'.
public
Circle(float
r) {
radius =
r;
circumference =
2 *
3.14159 *
r;
}
So, if we checked the value
of c1.radius, it would be 3, and c1.circumference
would equal 6 times pi, or about 18.9.
Especially alert readers
probably noticed one other quirk of constructors--unlike other
methods, they have no return type. In other words, most method
definitions must follow the form
type
methodName(type arg1, type arg2, ...) {
// Method body goes here.
}
where type tells Java
what kind of data the method produces (int, float, etc).
Constructors have the form
className(type
arg1, type arg2, ...) {
// Method body goes here.
}
This isn't a problem for
Java, because it knows that a constructor always produces a
new object of the type associated with the class definition.
On the Road Again
Back to our problem. If we
put the setSize command in BattleshipApp's constructor,
Java will execute it when we create the new instance of
BattleshipApp. That's the behavior we want, so let's give it a
try:
/**
* Constructor for objects of class BattleshipApp
*/
public BattleshipApp()
{
this.setSize(600, 500);
}
Here, the keyword this
is optional. We use it to show that we're calling setSize
on 'this' instance of BattleshipApp. You can leave it out if you
like.
Compile and run the code. If
you get compiler errors and you absolutely can't fix them, check
your code against this
working copy.
When you run this version, still
nothing happens, even though we created an instance of
BattleshipApp. The problem is that making the object doesn't let
us see it. We have to explicitly tell Java to show us the
window.
Show Me the Window
Fortunately, that's very
easy. Just add the following line to BattleshipApp's constructor:
this.show();
(immediately following the setSize
line).
Compile and run this
code.
Finally, a window!
Now that we've got a basic framework in place, we
can start laying out the rest of our project.
This is
where the fun begins...
|