Using the Full-Screen Exclusive Mode API in Java
May 30, 2006
Java Programming Notes # 1496
Preface
Oh for the good old days
Do you have a secret longing for the "good old days" when you could
write a program that would take over the entire screen and wouldn't be bothered
by a lot of pesky other programs competing for screen space. If so, those
days are back. In this lesson, I will show you how to use the Full-Screen Exclusive Mode API to
write programs that take over
the entire screen, as shown by the output from the animation program in
Figure 3.
Once your program has entered the Full-Screen Exclusive Mode, I will show
you how to use active rendering as opposed to passive rendering to
eliminate the overhead associated with passive rendering.
Viewing tip
You may find it useful to open another copy of this lesson in a
separate browser window. That will make it easier for you to
scroll back
and forth among the different listings and figures while you are
reading
about them.
Supplementary material
I recommend that you also study the other lessons in my extensive
collection of online Java tutorials. You will find those lessons
published
at Gamelan.com.
However, as of the date of this writing, Gamelan doesn't maintain a
consolidated index of my Java tutorial lessons, and sometimes they are
difficult to locate there. You will find a consolidated index at www.DickBaldwin.com.
I also recommend that you pay particular attention to the lessons listed in
the References section of this document.
Display many programs concurrently
Recent history would indicate that in most cases, it is probably best to
have a multitude of programs running concurrently and sharing screen space, as
indicated by the typical screen shot of my computer in
Figure 1.
Dedicate display capacity to a single program
However, sometimes it might be best to dedicate the computational and display capacity of your computer screen to a single
program as shown in Figure 3. Dedicating the
display capacity to a single program can be achieved by writing the program so
that uses the Full-Screen Exclusive Mode API.
Active versus passive rendering
In addition to providing dedicated display space, the use of Full-Screen
Exclusive Mode makes it possible to employ active rendering as opposed to passive rendering of
the material on the screen. This eliminates some of the rendering
overhead, and should make the system more responsive when running simulation, game, and animation programs
where a responsive screen display is important.
A screen shot (showing the entire screen) of my computer screen during a typical working session is
shown in Figure 1.

Figure 1
|
As you can see, there were several different applications competing for space
on my screen when I captured the screen shot.
A sense of scale
I'm sure that you have no interest in what my computer
screen happened to look like while I was writing this lesson.
Figure 1 is
provided to give you a sense of scale (The purpose is similar to that of photographing
a small object
with a coin laying beside it so that the size of the object will be apparent.) Because of the narrow publication
format for this lesson, it was not possible for me to show screen shots in their full size.
Therefore, the size of the image in
Figure 1 was reduced from 1024x768 pixels to
470x353 pixels for publication purposes.
The output from the program named FullScreen01
Figure 2 shows a screen shot of my computer screen
while running the
program named FullScreen01. (Once again,
Figure 2 shows my entire screen.)

Figure 2
|
A JFrame object in Full-Screen Exclusive Mode
As was the case in Figure 1, the screen-shot image in Figure 2 was also reduced to 470x353 pixels for
inclusion in this lesson. Briefly, Figure 2 shows a JFrame object
displayed by a program using the Full-Screen Exclusive Mode API.
(In the remainder of this lesson, I will often refer to the
Full-Screen Exclusive Mode simply
as the full-screen mode.)
One button and four labels
For illustration purposes, the JFrame object shown in Figure 2 has a
JButton object placed in the NORTH location, and has JLabel
objects with colored backgrounds placed in the other four locations managed by
the BorderLayout manager.
I will present and explain the code for the program named Layout01
later in this lesson.
A more substantive program
I will also present and explain a much more substantive program named
FullScreen02 in this lesson. A screen shot (showing the entire
screen) of the output from the program named
FullScreen02 is shown
in Figure 3.

Figure 3
|
Figure 3 shows the result of running an animation program in full-screen
mode.
Based on an earlier animation lesson
The animation program named FullScreen02 is largely based on an
animation program that I presented and explained in an earlier lesson entitled
Fun with Java:
Animated Sea Worms. However, the version of the program that I will
present and explain this
lesson makes use of full-screen mode, and also includes some other modernization
features relative to the earlier version.
Begin with FullScreen01
I will begin by explaining the program named FullScreen01. The
purpose of this program is to teach you how to implement full-screen mode while
avoiding the additional complexity of a substantive application.
Then I will explain the more
substantive program named FullScreen02, to teach you how to make
productive use of full-screen mode once you know how to implement it.
Discussion
and Sample Code
FullScreen01 is a skeleton program that implements the Full-Screen Exclusive Mode
API.
An undecorated non-resizable JFrame object
The program places an undecorated non-resizable JFrame object on the
screen in the Full-Screen Exclusive Mode. The JFrame object
is managed by a BorderLayout manager. A JButton object appears in the
NORTH location of the JFrame as shown in Figure 2. Clicking the button causes the
program to exit the full-screen mode, restore the original graphics mode, and
terminate.
The program places red, green, and white JLabel objects in the EAST,
WEST, SOUTH, and CENTER locations of the JFrame object, as shown in
Figure 2. This is done to demonstrate that the graphics object that occupies the
entire screen is an undecorated non-resizable JFrame object.
A list of screen devices
The program displays a list of the available screen devices solely for
information purposes in the command-line window. (There is only one
available screen device on my laptop computer, but there may be more than one on your
computer.) However, the command line window is hidden behind the
full-screen version of the JFrame object and isn't visible until the
full-screen mode is terminated.
Use only the first screen device in the list
Even though the program displays a list of all of the available screen
devices, it operates only on the first screen device in the list.
Program testing
The program was tested using J2SE5.0 under WinXP. J2SE 1.4 or later is
required to support the setFullScreenWindow method of the
GraphicsDevice object.
Will discuss in fragments
As is my custom, I will discuss the program in fragments. You can view
a complete listing of the program in Listing 26 near the end of the lesson.
The class definition begins in Listing 1.
public class FullScreen01 extends JFrame
implements ActionListener{
private GraphicsDevice graphicsDevice;
private DisplayMode origDisplayMode;
private JButton exitButton = new JButton(
"Exit Full-Screen Mode");
Listing 1
|
A container and an action listener
The class extends JFrame and implements ActionListener.
Thus an object of the class is a top-level container that can be placed directly
on the computer screen as a GUI. An object of the class is also suitable
for being registered as an action listener on objects that fire action events.
The code in Listing 1 declares a couple of instance variables that will be
used later. It also declares, instantiates, and initializes a reference to the JButton
object shown at the top of Figure 2.
The main method
The main method begins in Listing 2.
public static void main(String[] args){
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices =
graphicsEnvironment.getScreenDevices();
for(int cnt = 0;cnt < 1;cnt++){
System.out.println(devices[cnt]);
}//end for loop
Listing 2
|
List all available screen devices
The main method begins by getting and displaying a list of all the
available screen devices on the computer. As you can see in
Listing 2,
getting the list of screen devices is a two-step process:
- Get a GraphicsEnvironment object that describes your computer.
- Invoke the getScreenDevices method on that object to get a list
of available screen devices, each as a reference to an object of type
GraphicsDevice.
The GraphicsEnvironment class and GraphicsDevice
objects
Here is part of what Sun has to say about the GraphicsEnvironment
class and GraphicsDevice objects:
"The GraphicsEnvironment class describes the collection of
GraphicsDevice objects and Font objects available to a Java(tm)
application on a particular platform. The resources in this
GraphicsEnvironment might be local or on a remote machine.
GraphicsDevice objects can be screens, printers or image buffers and are
the destination of Graphics2D drawing methods. Each GraphicsDevice
has a number of GraphicsConfiguration objects associated with it.
These objects specify the different configurations in which the
GraphicsDevice can be used."
This seems straightforward enough, and shouldn't require further explanation.
The getScreenDevices method
As you have probably also guessed, the Sun documentation for the
getScreenDevices method of the GraphicsEnvironment class states that the method "Returns an array of all
of the screen GraphicsDevice objects." (The class provides
several other interesting instance methods that you might want to investigate as
well.)
Display the list of screen devices
After getting the list of available screen devices, the code in
Listing 2
uses a for loop to display the items in the list in the command-line
window. On my laptop computer, the single-item list of screen devices reads as follows:
Win32GraphicsDevice[screen=0]
The remainder of the main method is shown in Listing 3.
new FullScreen01(devices[0]);
}//end main
Listing 3
|
The constructor for FullScreen01
The statement in Listing 3 instantiates an object of the FullScreen01
class, causing the constructor for the class, which begins in
Listing 4, to be
executed.
public FullScreen01(GraphicsDevice graphicsDevice){
this.graphicsDevice = graphicsDevice;
Listing 4
|
Note that the code in Listing 3 passes the first reference in the list of
screen devices to the constructor that begins in Listing 4. The code in
Listing 4 saves this reference in one if the instance variables that was
declared in Listing 1. This reference will be used later when time comes
to exit the full-screen mode.
The JFrame title
The code in Listing 5 sets the title property on the JFrame object.
setTitle("This title will be hidden (undecorated)");
Listing 5
|
I included this code to remind me to mention that when the JFrame object is
displayed as an undecorated object the title
doesn't show. (The JFrame object will be displayed as an
undecorated JFrame in this lesson.)
Save the original display mode
Listing 6 gets and saves a reference to the original display mode in one of
the instance variables declared in Listing 1.
origDisplayMode = graphicsDevice.getDisplayMode();
Listing 6
|
This reference will be used later by the program to restore the original
display mode before terminating.
The JButton object
Listing 7 registers this FullScreen01 object as an ActionListener on the JButton object that was
instantiated in Listing 1.
exitButton.addActionListener(this);
getContentPane().add(exitButton, BorderLayout.NORTH);
Listing 7
|
I will discuss the behavior of the ActionListener object a little later in
conjunction with the discussion of the actionPerformed method.
Then Listing 7 places the JButton object at the top of the JFrame
object as shown in Figure 2.
Add some colored JLabel objects
Listing 8 places four colored (red, green, and white) JLabel objects in the remaining
BorderLayout positions in the JFrame object to demonstrate that the
screen is filled with an undecorated JFrame object as shown in Figure 2.
//Place four labels in the JFrame solely for the
// purpose of showing that it is a full-screen
// undecorated JFrame.
JLabel eastLabel = new JLabel(" East ");
eastLabel.setOpaque(true);
eastLabel.setBackground(Color.RED);
getContentPane().add(eastLabel,BorderLayout.EAST);
JLabel southLabel =
new JLabel("South",SwingConstants.CENTER);
southLabel.setOpaque(true);
southLabel.setBackground(Color.GREEN);
getContentPane().add(southLabel,BorderLayout.SOUTH);
JLabel westLabel = new JLabel(" West ");
westLabel.setOpaque(true);
westLabel.setBackground(Color.RED);
getContentPane().add(westLabel,BorderLayout.WEST);
JLabel centerLabel =
new JLabel("Center",SwingConstants.CENTER);
centerLabel.setOpaque(true);
centerLabel.setBackground(Color.WHITE);
getContentPane().add(centerLabel,BorderLayout.CENTER);
Listing 8
|
Switch to Full-Screen Exclusive Mode
Listing 9 invokes the isFullScreenSupported method to confirm that the
device is capable of operating in Full-Screen Exclusive Mode, and if so,
invokes the setFullScreenWindow method to switch the display to the
Full-Screen Exclusive Mode.
if (graphicsDevice.isFullScreenSupported()){
// Enter full-screen mode with an undecorated,
// non-resizable JFrame object.
setUndecorated(true);
setResizable(false);
//Make it happen!
graphicsDevice.setFullScreenWindow(this);
validate();
}else{
System.out.println("Full-screen mode not supported");
}//end else
}//end constructor
Listing 9
|
The isFullScreenSupported method
As you probably guessed from the syntax, the isFullScreenSupported
method returns true if the GraphicsDevice object supports
Full-Screen Exclusive Mode, and returns false otherwise.
The setFullScreenWindow method
Invoking the setFullScreenWindow method has a number of interesting
ramifications in addition to switching the screen to Full-Screen Exclusive
Mode. I recommend that you go to the Sun documentation and read
about those ramifications prior to using this mode in production programs.
An undecorated non-resizable JFrame object
Although it isn't a requirement to do so, Listing 9 sets the undecorated
property of the JFrame object to true and the resizable
property to false.
Setting the undecorated property to true eliminates the title
bar at the top and the borders on the sides of the JFrame object as shown
in Figure 2. Although not required, this probably should be done, particularly if active
rendering is to be used. Otherwise, the JFrame object can be
moved or iconified by the user to expose other windows behind it in the Z-order.
(Having eliminated the title bar and the borders, I suppose that
setting the resizable property to false may be superfluous, because there is
nothing to grab hold of with the mouse to change the size. I don't
know if it is possible to change the size of a JFrame object without
the use of a mouse by using keystrokes alone. If so, this is not
superfluous and the resizable property should be set to false.)
The full-screen object should appear
Listing 9 signals the end of the constructor. Once the constructor has
executed, the JFrame object should fill the screen as shown in Figure 2.
Figure 2 shows the results on my laptop computer with a screen size of
1024x768 pixels. Your results may be somewhat different depending on your
overall screen size in pixels, and the sizes of your fonts. The font size
determines the height of the JButton object at the top and the JLabel
object at the bottom, as well as the widths of the two JLabel objects on
the sides.
The ActionEvent handler
That leaves us with one more item to discuss for this program.
Listing
10 shows the actionPerformed method, which is invoked when the button at
the top of Figure 2 is clicked.
public void actionPerformed(ActionEvent evt){
//Restore the original display mode
graphicsDevice.setDisplayMode(origDisplayMode);
//Terminate the program
System.exit(0);
}//end actionPerformed
}//end class
Listing 10
|
The setDisplayMode method
Listing 10 invokes the setDisplayMode method of the GraphicsDevice
class, passing a reference to the DisplayMode object saved earlier in
order to restore the display mode to the original display mode. Then
Listing 10 terminates the program.
Sun has this to say about the setDisplayMode method:
"Sets the display mode of this graphics device. This may only be
allowed in full-screen, exclusive mode."
Sun has this to say about the DisplayMode class:
"The DisplayMode class encapsulates the bit depth, height,
width, and refresh rate of a GraphicsDevice. Display modes are
hardware-dependent and may not always be available."
May be superfluous
Setting the display mode in this manner at this point may be superfluous.
For this program on my computer, it doesn't seem to make any difference relative
to the appearance of the display after the program terminates whether or not I
invoke this method prior to termination.
The end of the program
Listing 10 signals the end of the class and the end of the program.
You should be able to use this skeleton to write programs that use the
Full-Screen Exclusive Mode API. As you will see in the next program
named FullScreen02, this skeleton can be wrapped around a much more
substantive application to control the display mode used by that application.
Shrink the Exit button
If you use the full-screen mode with an undecorated Frame or
JFrame object, you should always provide an Exit button to terminate.
(The buttons that are typically in the upper-right corner of the frame will
be unavailable.) However, the Exit button needn't be as large
as the one shown in Figure 2. Rather, you should be able to use a
different layout manager and place a small Exit button unobtrusively in
one of the corners of the screen.
The purpose of this program is to demonstrate the use of the Full-Screen
Exclusive Mode API for simulation and animation.
Update to an earlier program
This program animates colored sea worms on a background image in Full-Screen
Exclusive Mode. A screen shot of the program in operation is shown in
Figure 3. The program is an update to the program named Animate03,
which I explained in the earlier lesson entitled
Fun with Java:
Animated Sea Worms.
This version of the animation code has been modernized to make use of
the Swing Timer class as an animation timer, and also to use Active
Rendering as opposed to Passive Rendering in Full-Screen Exclusive
Mode. The program has also been modernized to use
generics.
Active rendering
You will find a good discussion of Active Rendering
versus Passive Rendering in
The Java Tutorial.
Because the program uses Active Rendering, it does not call the repaint
method to ask the operating system to make a callback to the update method.
Rather, the program invokes a method named activeRenderer, which causes
the animated sprites to be drawn on the screen without concern for sharing the screen with
other applications. (There are no other applications on the screen in
Full-Screen Exclusive Mode.)
An undecorated, non-resizable JFrame object
As shown in Figure 3, the program places an undecorated non-resizable JFrame object on the
screen in Full-Screen Exclusive Mode. A JButton is placed in
the NORTH location in the JFrame. Clicking the button causes the
program to exit the full- screen mode, restore the original graphics mode, and
terminate.
A JPanel in the JFrame
A JPanel object is placed in the center of the JFrame object.
The colored worms are animated as sprites on the JPanel object.
A
background image is drawn on the JPanel object at the beginning of each
animation cycle. This image erases the sprites that were previously there
producing a clean slate for drawing the sprites in their new locations.
Importance of the background image
The drawing of the background image can be eliminated by deleting one
statement from the program. If the background image is eliminated, the worms turn into long tubes because
the old sprite images are never erased.
The background image that I used fits a
1024 x 768 screen in Full-Screen Exclusive Mode. If your screen is a
different size, a different size background image will be required.
The program was tested using J2SE5.0 under
WinXP. J2SE 1.4 or later is required to support the setFullScreenWindow
method of a GraphicsDevice object. However, J2SE 5.0 or later is required to support
generics.
Will discuss in fragments
As usual, I will discuss this program in fragments. You can view the
program in its entirety in Listing 27 near the end of the lesson.
Won't discuss all of the code
Because this program is an update of the program named Animate03,
which I explained in the lesson entitled
Fun with Java:
Animated Sea Worms, I will assume that you already understand that code, as
well as the code in the other lessons listed in the
References section of this document. I won't repeat the explanation of
that code in this lesson.
Also, this animation program is wrapped in the skeleton that I explained
earlier in this lesson for the program named FullScreen01. I will also assume that
you understand that code and won't repeat the explanation of that code here.
Rather, I will explain only the code that is new and different in this
program.
An abbreviated listing of the beginning of the class and the main
method is shown in Listing 11.
class FullScreen02 extends JFrame
implements ActionListener{
//Code deleted for brevity.
public static void main(String[] args){
//Code deleted for brevity.
}//end main
Listing 11
|
The code in this part of the program is essentially the same as in the
program named FullScreen01. Therefore, I deleted that code from
Listing 11 for brevity. Once again, you can view the program named
FullScreen02 in its entirety in Listing 27 near the end of the lesson.
The constructor for FullScreen02
The constructor begins in Listing 12. Much of the code in this
constructor is the same as in the earlier program. Therefore, I deleted
the repeated code for brevity.
public FullScreen02(GraphicsDevice graphicsDevice){
//Code deleted for brevity.
getContentPane().add(new AnimationPanel());
//Code deleted for brevity.
}//end constructor
Listing 12
|
An animation panel
The only significant difference between this constructor and the constructor
that I explained earlier occurs in the single statement shown in
Listing 12.
This statement instantiates an object of the class AnimationPanel and
places it in the center of the JFrame object. This is the mechanism
by which the skeleton program wraps the Full-Screen Exclusive Mode around the
animation program.
The actionPerformed method
Listing 13 shows the actionPerformed method, which is the same as
before.
public void actionPerformed(ActionEvent evt){
//Code deleted for brevity.
}//end actionPerformed
}//end class FullScreen02
Listing 13
|
Listing 13 also signals the end of the class definition for the class named
FullScreen02.
The BackgroundImage class
Listing 14 shows an abbreviated version of a class named
Background Image.
class BackgroundImage{
//Code deleted for brevity.
}//end class BackgroundImage
Listing 14
|
This is a convenience class, from the earlier program named Animate03,
which makes it a little easier to deal with the background image. Note, however,
that this version is much simpler than the earlier version. Much of the
impetus for this class went away when the animation program named Animate03
was converted to full-screen mode. Several convenience methods having to
do with the size of the background image were eliminated in conjunction with
that conversion.
The SpriteManager class
Listing 15 shows an abbreviated and slightly modified version of a class
named SpriteManager from the earlier program named Animate03.
class SpriteManager{
private ArrayList <Sprite> theSprites =
new ArrayList<Sprite>();
//Code deleted for brevity.
}//end class SpriteManager
Listing 15
|
An object of this class manages a collection of animated sprites stored in an
ArrayList object. (A Vector object was used in the
earlier version named Animate03.) Note that this class also
underwent some modernization with respect to the use of
generics
as shown by the boldface code near the beginning of Listing 15.
The Sprite class
Listing 16 shows an abbreviated and slightly modified version of the
Sprite class from the earlier program named Animate03.
class Sprite{
//Code deleted for brevity.
private ArrayList <History> tailData =
new ArrayList<History>();
//Code deleted for brevity.
public void drawSpriteImage(Graphics g){
//Code deleted for brevity.
Iterator <History> iterator = tailData.iterator();
//Code deleted for brevity.
}//end drawSpriteImage()
//Code deleted for brevity.
}//end Sprite class
Listing 16
|
As suggested by the name, this is the class from which the sprites are
instantiated.
This class was also updated for
generics
as indicated by the boldface code in Listing 16.
The History class
Listing 17 shows an abbreviated version of the History class from the
earlier program named Animate03.
class History{
//Code deleted for brevity.
}//end class History
Listing 17
|
An object of this class is used to contain state information for each segment of each worm.
The AnimationPanel class
The animation is played out by drawing images on an object of the
AnimationPanel class, which begins in Listing 18. An object of this class
is placed in the CENTER of an undecorated non-resizable JFrame object
that is displayed in Full-Screen Exclusive Mode.
Most of the code in this class was applied directly to the JFrame object in the
earlier program named Animate03, because in that program, the animation
images were drawn directly on the JFrame object.
class AnimationPanel extends JPanel implements Runnable{
//Code deleted for brevity.
private int animationDelay = 83;
private javax.swing.Timer animationTimer;
Listing 18
|
Uses a Swing Timer object
The first substantive difference between this code and the code in the
earlier program named Animate03 is shown by the boldface code in
Listing 18. This program uses an object of the javax.swing.Timer class to
control the animation timing, whereas the earlier program used a timer built around the sleep method of the Thread class.
The animationDelay value of 83 milliseconds shown in
Listing 18 will result in an animation frame rate of
approximately twelve frames per second.
Listing 18 declares an instance variable named animationTimer, which will
be populated later with a reference to a Timer object.
The constructor for the AnimationPanel class
The constructor begins in Listing 19.
public AnimationPanel(){//constructor
// Load and track the images
mediaTracker = new MediaTracker(this);
//Get and track the background image.
backGroundImage = Toolkit.getDefaultToolkit().
getImage("background03.gif");
Listing 19
|
When you run this program, you will need to have an image file named
background03.gif with a size that matches the size of your computer screen.
In my case, that is 1024x768 pixels, but your screen may be a different size.
That image is loaded by the code in Listing 19.
The Timer object
The next substantive difference is shown by the code shown in
Listing 20.
animationTimer = new javax.swing.Timer(animationDelay,
new ActionListener(){
public void actionPerformed(ActionEvent e){
//Update the sprites
spriteManager.updateSprites();
//Update the animation panel on the screen
activeRenderer(getGraphics());
}//end actionPerformed
}//end new ActionListener
);//end new javax.swing.Timer
}//end constructor
Listing 20
|
The code in Listing 20 instantiates a Timer object and registers an
anonymous ActionListener on it using the peculiar listener registration
process for a Timer object.
(An ActionListener object is registered on a Timer
object by passing the listener object's reference as the second parameter to
the constructor for the Timer object. Then the listener object
is notified of an ActionEvent each time the Timer object
ticks. In this program, I set the Timer object to tick once
every 83 milliseconds in Listing 18. This later results in
approximately twelve animation frames per second being drawn on the
animation panel.)
The Timer object must be started before it will begin ticking.
It will be started in the run method of the animation thread after
everything is set up and is ready to run.
The ActionEvent handler
Each time the Timer object ticks, the actionPerformed method
shown in Listing 20 is executed. The code in this method invokes the
updateSprites method on the SpriteManager object. This causes
the state of each sprite to be updated in terms of location, color, direction of
motion, etc.
Then the code in the actionPerformed method invokes the
activeRenderer method to cause the background image and the updated sprites
to be drawn on the animation panel. Note that control does not flow
through the operating system in a callback sense as is typically the case
with passive rendering. Instead, the activeRenderer method
causes the material to be drawn directly on the screen without regard for
sharing the screen with other programs.
The activeRenderer method
The activeRenderer method is shown in its entirety in
Listing 21.
private void activeRenderer(Graphics g){
//Create the offscreen graphics context
if(offScreenGraphicsCtx == null){
offScreenImage =
createImage(getSize().width,getSize().height);
offScreenGraphicsCtx = offScreenImage.getGraphics();
}//end if
// Draw the sprites offscreen
spriteManager.drawScene(offScreenGraphicsCtx);
// Draw the scene onto the screen
if(offScreenImage != null){
g.drawImage(offScreenImage,0,0,this);
}//end if
}//end activeRenderer method
Listing 21
|
The activeRenderer method is used to draw directly on the screen using
active rendering without invoking the repaint method as is the case for
passive rendering.
Same as earlier code
The code in the activeRenderer method is essentially the same as the
code that I explained in the overridden update(Graphics g) method in the
earlier program named Animate03. Therefore, I won't repeat the
explanation of that code here.
The big difference is the flow of control through which the two methods are
called. The activeRenderer method is called directly in this
program. The overridden update method in the earlier program is
called in a callback sense after the program invokes the repaint method
to send a message to the operating system requesting the callback. The
active rendering approach eliminates the overhead associated with that
callback system, and therefore should be more responsive than passive
rendering.
The makeSprite method
Listing 22 shows an abbreviated version of the makeSprite method.
private Sprite makeSprite(Point position,int imageIndex){
//Code deleted for brevity.
}//end makeSprite()
Listing 22
|
This method is essentially the same as the version in the earlier program
named Animate03, so I won't discuss it further.
The animation thread
The run method for the animation thread begins in Listing 23.
public void run(){
while(getWidth() == 0){
System.out.print(".");
}//end while loop
while(getHeight() == 0){
System.out.print(".");
}//end while loop
Listing 23
|
Delay until size is stable
The run method begins by creating and adding sprites to the
SpriteManager object. First, however, the code in
Listing 23 implements a loop to delay the
process until the AnimationPanel is fully constructed and the width and height of the AnimationPanel
has become stable in
Full-Screen Exclusive Mode.
There is probably a better way to do this, but I don't know what it is right
off the top of my head. In any event, this happens only once while the
program is getting started and is switching from normal mode to Full-Screen
Exclusive Mode. Therefore, I didn't worry too much about it. If
this were something that happens often during the running of the program, I
would have given more thought towards a better solution to the problem.
Create and add 200 worms
Listing 24 creates 200 sprites (200 worms in
Figure 3) and hands them
off to the SpriteManager object.
spriteManager = new SpriteManager(
new BackgroundImage(this,backGroundImage),
getWidth(),
getHeight());
//Create 200 sprites from 6 gif files.
for(int cnt = 0;cnt < 200;cnt++){
Point position =
spriteManager.getEmptyPosition(
new Dimension(gifImages[0].getWidth(this),
gifImages[0].getHeight(this)));
spriteManager.addSprite(
makeSprite(position,cnt % 6));
}//end for loop
Listing 24
|
Note that the published version of the earlier program named Animate03
only had fifteen worms swimming around in the fish tank. However, that
program operated in a small JFrame object where fifteen worms represented
a crowd. This program has the entire screen for the worms to swim in, so
there is plenty of room for all 200 worms as shown in Figure 3.
Smooth animation up to 800 worms
As an interesting side note, I increased the number of sprites in this
full-screen program to 800 worms with no apparent loss in smooth animation at
twelve animation frames per second. As the number of worms increased
beyond 800, however, the quality of the animation began to deteriorate.
Let the show begin
Listing 25 causes the animation timer to start running and generating
approximately twelve ticks per second. That in turn causes the worms to
start swimming.
animationTimer.start();//Start the animation timer
}//end run method
}//end AnimationPanel class
Listing 25
|
Once started, the worms will continue to swim until the user clicks the
button at the top of Figure 3 to terminate the program.
Listing 25 also signals the end of the AnimationPanel class and the
end of the program.
Run the Program
I encourage you to copy the code from Listing 26
and Listing 27 into your text
editor, compile it, and execute it. Experiment with it, making
changes, and observing the results of your changes.
You will need some images
Figure 4 contains links to the image files that you
will need to run the program named FullScreen02.
Figure 4. GIF image files that you will need.
You should be able to capture the images by right-clicking on them
individually, and then saving them into files on your local disk. Having done
that, you will need to rename the files to match the names that are hard-coded
into the program.
Resize the background image
In addition, you will need to resize the background image to cause it to
match the size of your computer screen. You will need an image processing
program such as LView to resize the image.
In case you don't already have one, a trial version is available for
downloading.
Image resizing within the program
As you will see in the comments in the program, it is supposed to be possible
to cause the image file to be resized when it is loaded by the program.
I was unable to get that to work properly, but I didn't spend much time
troubleshooting the problem.
In this lesson, I showed you how to use the Full-Screen Exclusive Mode API to
write programs that take over
the entire screen, as shown by the output from the animation program in
Figure 3.
I also showed you how to use active rendering as opposed to passive rendering to
eliminate the overhead associated with passive rendering.
The program named FullScreen02 is an update to a program named
Animate03, first published in the last lesson in the following list.
-
1450 Fun with Java: Sprite Animation, Part 1
-
1452 Fun with Java: Sprite Animation, Part 2
-
1454 Fun with Java: Sprite Animation, Part 3
-
1456 Fun with Java: Sprite Animation, Part 4
-
1458 Fun with Java: Sprite Animation, Part 5
-
1460 Fun with Java: Sprite Animation, Part 6
-
1462 Fun with Java: Sprite Animation, Part 7
-
1464 Fun with Java: Frame Animation
-
1466 Fun with Java: Animated Sea Worms
The program named Animate03 uses material published in the earlier
lessons in the above list.
Complete Program Listings
Complete listings of the programs discussed in this lesson are shown in
Listing 26 and Listing 27 below.
/*FullScreen01.java
This is a skeleton program that implements the
Full-Screen Exclusive Mode
The program places an undecorated non-resizable JFrame
object on the screen in the Full-Screen Exclusive Mode.
A JButton appears in the North location of the JFrame.
Clicking the button causes the program to exit the full-
screen mode, restore the original graphics mode, and
terminate.
The program places red, green, and white JLabels in the
East, West, South, and Center locations solely to
demonstrate that the graphics object is an undecorated
non-resizable JFrame object.
The program displays a list of the available graphics
devices solely for information purposes in the command-
line window. However, that window is hidden behind the
full-screen version of the JFrame object and isn't visible
until the full-screen mode is terminated.
Even though the program displays a list of all of the
graphics devices, it operates only on the first graphics
device in the list.
Tested using J2SE5.0 under WinXP. J2SE 1.4 or later is
required for the setFullScreenWindow method of the
GraphicsDevice object.
**********************************************************/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class FullScreen01 extends JFrame
implements ActionListener{
private GraphicsDevice graphicsDevice;
private DisplayMode origDisplayMode;
private JButton exitButton = new JButton(
"Exit Full-Screen Mode");
public static void main(String[] args){
//Get and display a list of graphics devices solely for
// information purposes.
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices =
graphicsEnvironment.getScreenDevices();
for(int cnt = 0;cnt < 1;cnt++){
System.out.println(devices[cnt]);
}//end for loop
//Construct a full-screen object using
// graphicsDevice 0.
new FullScreen01(devices[0]);
}//end main
//Constructor
public FullScreen01(GraphicsDevice graphicsDevice){
//Save a reference to the graphics device as an
// instance variable so that it can be used later to
// exit the full-screen mode.
this.graphicsDevice = graphicsDevice;
setTitle("This title will be hidden (undecorated)");
//Get and save a reference to the original display
// mode as an instance variable so that it can be
// restored later.
origDisplayMode = graphicsDevice.getDisplayMode();
//Register an action listener on the exitButton.
exitButton.addActionListener(this);
//Place the exitButton in the JFrame
getContentPane().add(exitButton, BorderLayout.NORTH);
//Place four labels in the JFrame solely for the
// purpose of showing that it is a full-screen
// undecorated JFrame.
JLabel eastLabel = new JLabel(" East ");
eastLabel.setOpaque(true);
eastLabel.setBackground(Color.RED);
getContentPane().add(eastLabel,BorderLayout.EAST);
JLabel southLabel =
new JLabel("South",SwingConstants.CENTER);
southLabel.setOpaque(true);
southLabel.setBackground(Color.GREEN);
getContentPane().add(southLabel,BorderLayout.SOUTH);
JLabel westLabel = new JLabel(" West ");
westLabel.setOpaque(true);
westLabel.setBackground(Color.RED);
getContentPane().add(westLabel,BorderLayout.WEST);
JLabel centerLabel =
new JLabel("Center",SwingConstants.CENTER);
centerLabel.setOpaque(true);
centerLabel.setBackground(Color.WHITE);
getContentPane().add(centerLabel,BorderLayout.CENTER);
if (graphicsDevice.isFullScreenSupported()){
// Enter full-screen mode witn an undecorated,
// non-resizable JFrame object.
setUndecorated(true);
setResizable(false);
//Make it happen!
graphicsDevice.setFullScreenWindow(this);
validate();
}else{
System.out.println("Full-screen mode not supported");
}//end else
}//end constructor
//The following method is invoked when the used clicks
// the exitButton
public void actionPerformed(ActionEvent evt){
//Restore the original display mode
graphicsDevice.setDisplayMode(origDisplayMode);
//Terminate the program
System.exit(0);
}//end actionPerformed
}//end class
Listing 26
|
Listing 27
/*FullScreen02.java
Copyright 2006, R.G.Baldwin
The purpose of this program is to demonstrate the use of
the Full-Screen Exclusive Mode API for simulation and
animation.
This program is an update to the earlier program named
Animate03 as described in the earlier lesson number
Java1466. This program has been modernized to
make use of the Swing Timer class as an animation timer,
and also to use Active Rendering (as opposed to Passive
Rendering) in Full-Screen Exclusive Mode. It has also been
modernized to use generics.
The program animates colored worms on a background image
in Full-Screen Exclusive mode. See the discussion of
Active Rendering versus Passive Rendering in The Java
Tutorial at:
http://java.sun.com/docs/books/tutorial/extra/fullscreen/
index.html
Using Active Rendering, the program does not call the
repaint method to ask the operating system to call the
update method. Rather, the program invokes a method named
activeRenderer, which draws the animated sprites on the
screen without concern for sharing the screen with other
applications. (There are no other applications on the
screen in full-screen mode.)
The program places an undecorated non-resizable JFrame
object on the screen in Full-Screen Exclusive Mode.
A JButton appears in the North location of the JFrame.
Clicking the button causes the program to exit the full-
screen mode, restore the original graphics mode, and
terminate.
A JPanel appears in the center of the JFrame object.
The colored worms are animated as sprites on the JPanel.
A background image is drawn on the JPanel at the beginning
of each animation cycle. This image erases the sprites
that were previously there creating a clean slate for
drawing the sprites in their new positions. If the
background image is eliminated, the worms turn into long
tubes because the old sprite images are never erased.
The background image that is used fits a 1024 x 768
screen in Full-Screen Exclusive mode. If the screen is
a different size, a different size background image is
needed.
Tested using J2SE5.0 under WinXP. J2SE 1.4 or later is
required for the setFullScreenWindow method of the
GraphicsDevice object. J2SE 5.0 or later is required
to support generics.
**********************************************************/
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
class FullScreen02 extends JFrame
implements ActionListener{
private GraphicsDevice graphicsDevice;
private DisplayMode origDisplayMode;
private JButton exitButton = new JButton(
"Exit Full-Screen Mode Copyright 2006, R.G.Baldwin");
public static void main(String[] args){
//Get a list of available graphics devices.
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] devices =
graphicsEnvironment.getScreenDevices();
//Construct a full-screen object using the first
// graphics device in the array. Ignore the others, if
// there are any.
new FullScreen02(devices[0]);
}//end main
//-----------------------------------------------------//
//Constructor
public FullScreen02(GraphicsDevice graphicsDevice){
//Save a reference to the graphics device as an
// instance variable so that it can be used later to
// exit the full-screen mode.
this.graphicsDevice = graphicsDevice;
setTitle("This title will be hidden (undecorated)");
//Get and save a reference to the original display
// mode as an instance variable so that it can be
// restored later.
origDisplayMode = graphicsDevice.getDisplayMode();
//Register an action listener on the exitButton.
exitButton.addActionListener(this);
//Place the exitButton at the top of the JFrame
getContentPane().add(exitButton,BorderLayout.NORTH);
//Create an animation panel and place it in the center
// of the JFrame.
getContentPane().add(new AnimationPanel());
if(graphicsDevice.isFullScreenSupported()){
// Enter full-screen mode witn an undecorated,
// non-resizable JFrame object.
setUndecorated(true);
setResizable(false);
graphicsDevice.setFullScreenWindow(this);
validate();
}else{
System.out.println("Full-screen mode not supported");
}//end else
}//end constructor
//-----------------------------------------------------//
//The following method is invoked when the used clicks
// the exitButton
public void actionPerformed(ActionEvent evt){
//Restore the original display mode
graphicsDevice.setDisplayMode(origDisplayMode);
//Terminate the program
System.exit(0);
}//end actionPerformed
//-----------------------------------------------------//
}//end class FullScreen02
//=======================================================//
//This is a convenience class that makes it a little
// easier to deal with the background image. Note that
// much of the impetus for this class went away when the
// animation program named Animate 03 was converted to
// full-screen mode. Several convenience methods having to
// do with the size of the background image were eliminated
// at that time.
class BackgroundImage{
private Image image;
private Component component;
public BackgroundImage(Component component,Image image){
this.component = component;
this.image = image;
}//end construtor
public void drawBackgroundImage(Graphics g){
//Note that component is an ImageObserver in the
// following statement.
g.drawImage(image,0,0,component);
}//end drawBackgroundImage()
}//end class BackgroundImage
//=======================================================//
//An object of this class manages a collection of animated
// sprites stored in an ArrayList object. (A Vector
// object was used in the earlier version named Animate03.)
// Note that this class also underwent some modernization
// with respect to the use of generics as released in
// J2SE 5.0.
class SpriteManager{
private ArrayList <Sprite> theSprites =
new ArrayList<Sprite>();
private BackgroundImage backgroundImage;
private int panelWidth;
private int panelHeight;
//-----------------------------------------------------//
public SpriteManager(BackgroundImage backgroundImage,
int panelWidth,
int panelHeight){
this.backgroundImage = backgroundImage;
this.panelWidth = panelWidth;
this.panelHeight = panelHeight;
}//end constructor
//-----------------------------------------------------//
public Point getEmptyPosition(Dimension spriteSize){
Rectangle trialSpaceOccupied = new Rectangle(
0,0,spriteSize.width,spriteSize.height);
Random rand = new Random(System.currentTimeMillis());
boolean empty = false;
int numTries = 0;
//Search for an empty position. If an empty position
// can't be found after 100 tries, give up and allow
// two sprites to occupy the same initial location.
while(!empty && numTries++ < 100){
// Get a trial position
trialSpaceOccupied.x = Math.abs(
rand.nextInt() % panelWidth);
trialSpaceOccupied.y = Math.abs(
rand.nextInt() % panelHeight);
// Iterate through existing sprites, checking if
// position is empty
boolean collision = false;
for(int cnt = 0;cnt < theSprites.size();cnt++){
Rectangle testSpaceOccupied =
theSprites.get(cnt).getSpaceOccupied();
if(trialSpaceOccupied.intersects(
testSpaceOccupied)){
collision = true;
}//end if
}//end for loop
empty = !collision;
}//end while loop
return new Point(trialSpaceOccupied.x,
trialSpaceOccupied.y);
}//end getEmptyPosition()
//-----------------------------------------------------//
//This method updates the state information for each of
// the sprites.
public void updateSprites(){
Sprite sprite;
//Iterate through sprite list
for(int cnt = 0;cnt < theSprites.size();cnt++){
sprite = theSprites.get(cnt);
//Update a sprite's position
sprite.updatePosition();
//Test for collision. Positive
// result indicates a collision
int hitIndex = testForCollision(sprite);
if(hitIndex >= 0){
//A collision has occurred. Make the two sprites
// involved in the collision bounce off the other
// sprite.
bounceOffSprite(cnt,hitIndex);
}//end if
}//end for loop
}//end updateSprites
//-----------------------------------------------------//
//This method tests for a collision between two sprites.
private int testForCollision(Sprite testSprite){
//Check for collision with other sprites
Sprite sprite;
for(int cnt = 0;cnt < theSprites.size();cnt++){
sprite = theSprites.get(cnt);
if(sprite == testSprite)
//Don't check self
continue;
//Invoke testCollision method of Sprite class to
// perform the actual test.
if(testSprite.testCollision(sprite))
//Return index of colliding sprite
return cnt;
}//end for loop
return -1;//No collision detected
}//end testForCollision()
//-----------------------------------------------------//
private void bounceOffSprite(
int spriteAindex,int spriteBindex){
//Swap motion vectors for bounce algorithm
Sprite spriteA = theSprites.get(spriteAindex);
Sprite spriteB = theSprites.get(spriteBindex);
Point swap = spriteA.getMotionVector();
spriteA.setMotionVector(spriteB.getMotionVector());
spriteB.setMotionVector(swap);
}//end bounceOffSprite()
//-----------------------------------------------------//
//This method draws the sprites in their new locations
// on the animation panel.
public void drawScene(Graphics g){
//Draw the background and erase sprites from animation
// panel.
//Disable the following statement for an interesting
// effect. This will turn the short worms into tube
// worms that increase in length forever.
backgroundImage.drawBackgroundImage(g);
//Iterate through sprites, drawing each sprite
for(int cnt = 0;cnt < theSprites.size();cnt++)
theSprites.get(cnt).drawSpriteImage(g);
}//end drawScene()
//-----------------------------------------------------//
public void addSprite(Sprite sprite){
//Add a new sprite to the ArrayList object.
theSprites.add(sprite);
}//end addSprite()
//-----------------------------------------------------//
}//end class SpriteManager
//=======================================================//
//The class from which Sprite objects are instantiated.
class Sprite{
private Component component;
private Image[] image;
private Rectangle spaceOccupied;
private Point motionVector;
private Rectangle bounds;
private Random rand;
private int frameNumber;
//The following collection contains a reference to a
// History object containing state information about
// each spherical segment that makes up a worm.
private ArrayList <History> tailData =
new ArrayList<History>();
private int wormLength;
//-----------------------------------------------------//
//Constructor
public Sprite(Component component,
Image[] image,
Point position,
Point motionVector){
//Seed a random number generator for this sprite with
// the sprite position.
rand = new Random(position.x);
wormLength = Math.abs(rand.nextInt() % 20);
this.component = component;
this.image = image;
this.spaceOccupied = new Rectangle(
position.x,
position.y,
image[0].getWidth(component),
image[0].getHeight(component));
this.motionVector = motionVector;
//Compute edges of usable graphics area. Because we
// are drawing on a JPanel, we don't need to worry
// about insets for borders, banner, etc. This is much
// simpler than in the earlier program named Animate03.
bounds = new Rectangle(0,0,component.getSize().width,
component.getSize().height);
}//end constructor
//-----------------------------------------------------//
public Rectangle getSpaceOccupied(){
return spaceOccupied;
}//end getSpaceOccupied()
//-----------------------------------------------------//
private void setSpaceOccupied(Point position){
spaceOccupied.setLocation(position.x,position.y);
}//setSpaceOccupied()
//-----------------------------------------------------//
public Point getMotionVector(){
return motionVector;
}//end getMotionVector()
//-----------------------------------------------------//
public void setMotionVector(Point motionVector){
this.motionVector = motionVector;
}//end setMotionVector()
//-----------------------------------------------------//
private void setBounds(Rectangle bounds){
this.bounds = bounds;
}//end setBounds()
//-----------------------------------------------------//
public void updatePosition(){
Point position = new Point(
spaceOccupied.x,spaceOccupied.y);
//Insert random behavior. During each update, a sprite
// has about one chance in 10 of making a small random
// change to its motionVector. When a change occurs,
// the motionVector coordinate values are forced to
// fall between -7 and 7.
if(rand.nextInt() % 10 == 0){
Point randomOffset =
new Point(rand.nextInt() % 3,rand.nextInt() % 3);
motionVector.x += randomOffset.x;
if(motionVector.x >= 7)
motionVector.x -= 7;
if(motionVector.x <= -7)
motionVector.x += 7;
motionVector.y += randomOffset.y;
if(motionVector.y >= 7)
motionVector.y -= 7;
if(motionVector.y <= -7)
motionVector.y += 7;
}//end if
//Make the move
position.translate(motionVector.x,motionVector.y);
//Bounce off the walls
boolean bounceRequired = false;
Point tempMotionVector =
new Point(motionVector.x,motionVector.y);
//Handle walls in x-dimension
if(position.x < bounds.x){
bounceRequired = true;
position.x = bounds.x;
//reverse direction in x
tempMotionVector.x = -tempMotionVector.x;
}else if((position.x + spaceOccupied.width) >
(bounds.x + bounds.width)){
bounceRequired = true;
position.x =
bounds.x + bounds.width - spaceOccupied.width;
//Reverse direction
tempMotionVector.x = -tempMotionVector.x;
}//end else if
//Handle walls in y-dimension
if(position.y < bounds.y){
bounceRequired = true;
position.y = bounds.y;
tempMotionVector.y = -tempMotionVector.y;
}else if((position.y + spaceOccupied.height) >
(bounds.y + bounds.height)){
bounceRequired = true;
position.y =
bounds.y + bounds.height - spaceOccupied.height;
tempMotionVector.y = -tempMotionVector.y;
}//end else if
//Save new motionVector
if(bounceRequired)
setMotionVector(tempMotionVector);
//Update spaceOccupied
setSpaceOccupied(position);
}//end updatePosition()
//-----------------------------------------------------//
//This method draws each worm, one segment at a time.
// A new segment is added for the head each time this
// method is called. When the worm has reached its
// specified length, the oldest segment is discarded
// each time this method is called.
public void drawSpriteImage(Graphics g){
frameNumber = Math.abs(rand.nextInt() % 6);
//Add a new head to the worm.
tailData.add(new History(
image[frameNumber],spaceOccupied.x,spaceOccupied.y));
Iterator <History> iterator = tailData.iterator();
int cnt = 0;
while(iterator.hasNext()){
//Draw each segment of the worm.
History history = iterator.next();
//Discard the first segment in the list if the worm
// has met its length requirement. This will keep
// the length of the worm constant.
if(tailData.size() > wormLength && cnt == 0){
iterator.remove();
}//end if
g.drawImage(history.image,
history.x,
history.y,
component);
cnt++;
}//end while
}//end drawSpriteImage()
//-----------------------------------------------------//
public boolean testCollision(Sprite testSprite){
// Check for collision with another sprite
if(testSprite != this){
return spaceOccupied.intersects(
testSprite.getSpaceOccupied());
}//end if
return false;
}//end testCollision
}//end Sprite class
//=======================================================//
//An object of this class is used to contain state
// information for each segment of each worm.
class History{
Image image;
int x;
int y;
public History(Image image,int x,int y){
this.image = image;
this.x = x;
this.y = y;
}//end constructor
}//end class History
//=======================================================//
//The animation is played out by drawing images on an
// object of this class. The object is placed in the
// center of an undecorated non-resizable JFrame object
// that is displayed in Full-Screen Exclusive Mode.
//Most of the code in this class was applied to the JFrame
// object in the earlier program named Animate03, because
// in that case, the images were drawn directly on the
// JFrame object.
class AnimationPanel extends JPanel implements Runnable{
private Image offScreenImage;
private Image backGroundImage;
private Image[] gifImages = new Image[6];
private Graphics offScreenGraphicsCtx;
private Thread animationThread;
private MediaTracker mediaTracker;
private SpriteManager spriteManager;
//Animation display rate, 12fps
private int animationDelay = 83;
private Random rand =
new Random(System.currentTimeMillis());
private javax.swing.Timer animationTimer;
//-----------------------------------------------------//
public AnimationPanel(){//constructor
// Load and track the images
mediaTracker = new MediaTracker(this);
//Get and track the background image. Note that this
// background image is appropriate only for a screen
// size of 1024x768. For a different screen size, you
// will need to use a different background image file.
backGroundImage = Toolkit.getDefaultToolkit().
getImage("background03.gif");
mediaTracker.addImage(backGroundImage,0);
//Note that it should be possible to use the following
// code to load a smaller version of the image and
// have it scaled to the required size as it is loaded,
// but this doesn't seem to work properly. (I probably
// just don't understand how it is supposed to work.)
// backGroundImage = Toolkit.getDefaultToolkit().
// getImage("background02.gif");
// mediaTracker.addImage(backGroundImage,0,1024,768);
//Get and track 6 images to use for sprites
gifImages[0] = Toolkit.getDefaultToolkit().
getImage("redball.gif");
mediaTracker.addImage(gifImages[0],0);
gifImages[1] = Toolkit.getDefaultToolkit().
getImage("greenball.gif");
mediaTracker.addImage(gifImages[1],0);
gifImages[2] = Toolkit.getDefaultToolkit().
getImage("blueball.gif");
mediaTracker.addImage(gifImages[2],0);
gifImages[3] = Toolkit.getDefaultToolkit().
getImage("yellowball.gif");
mediaTracker.addImage(gifImages[3],0);
gifImages[4] = Toolkit.getDefaultToolkit().
getImage("purpleball.gif");
mediaTracker.addImage(gifImages[4],0);
gifImages[5] = Toolkit.getDefaultToolkit().
getImage("orangeball.gif");
mediaTracker.addImage(gifImages[5],0);
//Block and wait for all images to be loaded
try{
mediaTracker.waitForID(0);
}catch(InterruptedException e){
System.out.println(e);
}//end catch
//Instantiate an animation thread and start it running.
animationThread = new Thread(this);
animationThread.start();
//Instantiate a Timer object and register an anonymous
// ActionListener on it using the peculiar listener
// registration process for a Timer object. The
// Timer will be started in the run method of the
// animationThread.
animationTimer = new javax.swing.Timer(animationDelay,
new ActionListener(){
public void actionPerformed(ActionEvent e){
//Update the sprites
spriteManager.updateSprites();
//Update the animation panel on the screen
activeRenderer(getGraphics());
}//end actionPerformed
}//end new ActionListener
);//end new javax.swing.Timer
}//end constructor
//-----------------------------------------------------//
//This method is used to draw directly on the screen
// using Active Rendering, without invoking the repaint
// method.
private void activeRenderer(Graphics g){
//Create the offscreen graphics context
if(offScreenGraphicsCtx == null){
offScreenImage =
createImage(getSize().width,getSize().height);
offScreenGraphicsCtx = offScreenImage.getGraphics();
}//end if
// Draw the sprites offscreen
spriteManager.drawScene(offScreenGraphicsCtx);
// Draw the scene onto the screen
if(offScreenImage != null){
g.drawImage(offScreenImage,0,0,this);
}//end if
}//end activeRenderer method
//-----------------------------------------------------//
public void run(){
//Create and add sprites to the sprite manager
//Delay until the width and the height of the
// AnimationPanel stabilize in Full-Screen mode. There
// is probably a better way to do this, but I don't
// know what it is. In any event, this happens only
// once while the program is getting started and
// switching to Full-Screen Exclusive Mode.
while(getWidth() == 0){
System.out.print(".");
}//end while loop
while(getHeight() == 0){
System.out.print(".");
}//end while loop
spriteManager = new SpriteManager(
new BackgroundImage(this,backGroundImage),
getWidth(),
getHeight());
//Create 200 sprites from 6 gif files.
for(int cnt = 0;cnt < 200;cnt++){
Point position =
spriteManager.getEmptyPosition(
new Dimension(gifImages[0].getWidth(this),
gifImages[0].getHeight(this)));
spriteManager.addSprite(
makeSprite(position,cnt % 6));
}//end for loop
animationTimer.start();//Start the animation timer
}//end run method
//-----------------------------------------------------//
private Sprite makeSprite(Point position,int imageIndex){
return new Sprite(this,
gifImages,
position,
new Point(rand.nextInt() % 5,
rand.nextInt() % 5));
}//end makeSprite()
//-----------------------------------------------------//
}//end AnimationPanel
Listing 27
|
Copyright 2006, Richard G. Baldwin. Reproduction in whole or in part in any
form or medium without express written permission from Richard Baldwin is
prohibited.
About the author
Richard Baldwin is a
college professor (at Austin Community College in Austin, TX) and private
consultant whose primary focus is a combination of Java, C#, and XML. In
addition to the many platform and/or language independent benefits of Java and
C# applications, he believes that a combination of Java, C#, and XML will become
the primary driving force in the delivery of structured information on the Web.
Richard has participated in numerous consulting projects and he
frequently provides onsite training at the high-tech companies located in and
around Austin, Texas. He is the author of Baldwin's Programming
Tutorials, which have gained a
worldwide following among experienced and aspiring programmers. He has also
published articles in JavaPro magazine.
In addition to his programming expertise, Richard has many years of
practical experience in Digital Signal Processing (DSP). His first job after he
earned his Bachelor's degree was doing DSP in the Seismic Research Department of
Texas Instruments. (TI is still a world leader in DSP.) In the following
years, he applied his programming and DSP expertise to other interesting areas
including sonar and underwater acoustics.
Richard holds an MSEE degree from Southern Methodist University and has
many years of experience in the application of computer technology to real-world
problems.
Baldwin@DickBaldwin.com
|