JavaUsing the Full-Screen Exclusive Mode API in Java

Using the Full-Screen Exclusive Mode API in Java

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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.

General Background Information

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.

Preview

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 1024×768 pixels to
470×353 pixels for publication purposes.

The output from the program named FullScreen01

(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

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.

Summary

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.

References

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

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories