HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

With the continual onslaught of the Internet and the World Wide Web, surfers are now getting bored with just flat, lifeless pages. Over the past year, many tools have emerged for the Web developer to experiment with. For example, Netscape's support of the Gif89a graphic standard allowed sites to take advantage of a relatively simple flicker book style animation. Although simple, control of such animation is very limited. Animation is just one of the many things Java can be applied to. This article will show the reader some techniques used in producing animation's with Java, and will present a complete coded example.

Techniques
Before we get into the intricacies of coded examples, we need to know how to solve the problem before we start implementing. The simplest and easiest technique is the old fashioned flicker book animation, as illustrated in Figure 1a. This is where a series of pictures, each with a slightly different image, is quickly drawn over the top of the previous and gives the illusion of movement. Another straightforward method is to move a small image or sprite over the top of a background image, for example, a man walking his dog as in figure 1b. This article will show the implementation for the flicker book style of animation.

Stage 1 - Design !
For this animation we will assume we have already created a series of images, and saved them in either GIF or JPG format. The technique tells us that we have to quickly display these images one after another to create the illusion of movement. So, problem one is to bring all the images into memory. We could of course load them as and when required, but this method is fraught with danger. And, what would happen if the user was experiencing a particularly slow Internet connection. Your animation would look jerky and very unprofessional. The next problem is to find some way to display the images in a timed, orderly manner. Fortunately, these are not problems for Java, and many aids have been provided to make life much, much easier.

Stage 2- Implementation
Loading the images into memory will require a little more thought than simply issuing a load command. The complication is the Internet. Remember the files are not coming from a local hard disk but from a remote host, which may be on the other side of the planet. Java designers have foreseen such issues,and provided a class to aid the developer: MediaTracker. This class monitors the loading process and informs the user when the operation is successfully completed or not, allowing the program to get on with some other task. The code in Listing 1 shows the use of MediaTracker.

Listing 1: Media Tracker
Applet Parent; //- Reference to the parent of class
MediaTracker TrackLoading; //- Declare a reference to the class
Image Pictures[]; //- Somewhere to place the loaded images
int NoPictures = 10; //- Lets assume we have 10 images named
//- Img1.gif, Img2.gif, ... Img10.gif

public void vLoadImages()
{
for ( int x=1; x <= NoPictures; x++ )
{
//- Initiate the loading process
Parent.showStatus( "Loading the file: Img" + x + ".gif");
Pictures[ x-1 ] = Parent.getImage( Parent.getDocumentBase(), "Img" + x + ".gif" );

//- Start the tracking of this image
TrackLoading.addImage( Pictures[ x-1 ], 0 );

try
{
TrackLoading.waitForAll(); //- Wait for the loading to be completed
}
catch ( InterruptedException e )
{
Parent.showStatus( "Img" + x + ".gif failed to load");
}
}
}

Figure 1

The method getImage(URL, graphic) will load the graphic file from the host specified in the URL. Due to the security restrictions of an applet, the only host we are allowed to load from is the applet's host, and the method getDocumentBase() returns this host information. We now need to inform the MediaTracker class which file wants tracking, using the addImage( Image, ID ) method. We pass in the image we wish to monitor along with an ID to allow us to identify it in the future. Now we wait, and the method waitForAll() waits with us. In the unlikely event the loading will fail, for whatever reason, we ensure safe execution by placing an exception handler around it.

Now that we have the images successfully loaded we now need to look at how we display them. Listing 2, shows the paint routine. The method drawImage(Image, x, y, ImageObserver) displays the loaded image onto the screen at the co-ordinates (x,y). The variable CurrentImage keeps track of which image is to be displayed. public void paint( Graphics g )

Listing 2: The Paint Routine
{
//- Draw the Image
g.drawImage( Pictures[ CurrentImage ], 10, 10, this );
}

Listing 3: Shows the Main Components of the Applet
Thread Running = null; //- Declare a handle to the current thread of execution
int SleepTime = 200; //- Time between image switches

public void run()
{
Thread.currentThread().setPriority( Thread.NORM_PROIRITY - 1 );

while ( true )
{
//- Increment the Image Index
CurrentImage = CurrentImage + 1;
if ( CurrentImage == NoPictures )
CurrentImage = 0;

//- Draw the image
repaint();

//- Sleep for a while
try
{
Thread.sleep( SleepTime );
}
catch ( InterruptedException e )
{
break;
}
}
}

public void start()
{
if ( Running == null )
{
Running = new Thread( this );
Running.start();
}
}

public void stop()
{
if ( Running != null )
{
Running.stop();
Running = null;
}
}

The main method, run, continually loops round, doing a repaint every SleepTime milliseconds. The repaint method forces the paint routine to be called. The stop, start methods control the threads execution and ensures only one thread ever gets started. As one could imagine, multiple threads running could cause some interesting results.

The complete listing can be found at the end, and includes an enhancement to manually control the frame speed. The overall application is built on two panels, one for the animation, and the other for the two buttons and one label to control the speed of the animation.

As can be seen, the overhead and effort involved for an animation based applet is minimal, but the results could make the difference between an average site and a site that would make the Top 100 cool sites. However, before starting, it is important that the animation is designed properly, and the number of pictures is kept to a minimum, there is no sense in making the surfer wait and get frustrated with your site. Java is just the tool to co-ordinate the display of images.

Animate it, and they will come

About the Author
Alan Williamson currently runs his own Software Consultancy company (N-ARY Limited) based in the United Kingdom. Having previously gained his honours degree at the University of Paisley, Scotland, he went on to work for major corporate organizations including BT and BP Exploration. N-ARY's is involved in the Internet and Web publishing, Alan, along with one of his colleagues, is currently co-authoring a book on Java and databases which is scheduled for release soon by a major publisher.

	

Complete Listing

//-------------------------------------
//- Animate.java
//- Copyright 1996, All rights reserved
//- N-ARY Limited
//- Version: 1.0
//- Author: AR Williamson ( [email protected] )
//- Created: 2 June 1996
//- Web: http://www.u-net.com/~n-ary
//-------------------------------------

import	java.awt.*;
import	java.applet.*;
import	java.lang.*;

//-------

public class Animate extends Applet
{
int SleepTime	= 200;

public void init()
	{
setLayout( new BorderLayout() );

FlickBook	Fb	= new FlickBook( this, SleepTime );
TimeControl	Tc	= new TimeControl( Fb, SleepTime );

Fb.init();
Fb.start();

add( "Center", Fb );
add( "South", Tc );
	}

//----

public static void main( String args[] )
	{
Frame	fram	= new Frame( "Animate" );
Animate	am	= new Animate();

am.init();
am.start();

fram.add( "Center", am );
fram.resize( 200, 200 );
fram.show();
	}
}

//-------

class	FlickBook extends Panel implements Runnable
{
Thread	Running = null;	
//- Declare a handle to the current thread of execution
int	SleepTime;			
//- Time between image switches
Applet		Parent;		
//- Reference to the parent of class
MediaTracker	TrackLoading;	
//- Declare a reference to the class
Image		Pictures[];	
//- Somewhere to place the loaded images
int		NoPictures = 10;	
//- Lets assume we have 10 images named
//- Img1.gif, Img2.gif, ... Img10.gif

//----

public FlickBook( Applet _Parent, int _SleepTime )
	{
Parent		= _Parent;
SleepTime	= _SleepTime;
	}

//----

public void init()
	{
Pictures= new Image[ NoPictures ]; 
//- Declare an instance of the pictures
TrackLoading= new MediaTracker( Parent ); 
//- Declare an instance of the mediatracker
vLoadImages();
repaint();
	}

//----

public void	vLoadImages()
	{
		for ( int x=1; x <= NoPictures; x++ )
		{
//- Initiate the loading process
Parent.showStatus( "Loading the file: Img" + x + ".gif");
Pictures[ x-1 ]	= Parent.getImage( Parent.getDocumentBase(), "Img" + x + ".gif" );

//- Start the tracking of this image
	TrackLoading.addImage( Pictures[ x-1 ], 0 );

	try
	{
	TrackLoading.waitForAll();	//- Wait for the loading to be completed
	}
	catch ( InterruptedException e )
	{
	Parent.showStatus( "Img" + x + ".gif failed to load");
	}
	}
	}

//----

public void paint( Graphics g )
	{
//- Draw the Image
	g.drawImage( Pictures[ CurrentImage ], 10, 10, this );
	}

//----

public void run()
	{
Thread.currentThread().setPriority( Thread.NORM_PROIRITY - 1 );

while ( true )
		{
//- Increment the Image Index
	CurrentImage	= CurrentImage + 1;
	if ( CurrentImage == NoPictures )
	CurrentImage	= 0;

//- Draw the image
repaint();

//- Sleep for a while
	try
			{
Thread.sleep( SleepTime );
			}
catch ( InterruptedException e )
			{
break;
			}
		}
	}

//----

public void start()
	{
if ( Running == null )
		{
Running	= new Thread( this );
	Running.start();
		}
	}

//----

public void stop()
	{
if ( Running != null )
		{
Running.stop();
	Running	= null;
		}
	}

//----

public int IncSpeed()
	{
//- Use to control the speed of the animation
SleepTime	= SleepTime - 50;
if ( SleepTime < 0 )
		{
SleepTime = 20;
		}

return SleepTime;
	}

//----

public int DecSpeed()
	{
//- Use to control the speed of the animation
	SleepTime	= SleepTime + 50;

return SleepTime;
	}
}

//-------

class TimeControl extends Panel
{
Button		Slower;		
//- Buttons for the faster and slower controls
Button		Faster;
Label			SpeedLabel;
int			Speed;
FlickBook	FBook;

//----

public	TimeControl( FlickBook _FBook, int _Speed )
	{
FBook			= _FBook;
Speed			= _Speed;

setLayout( new FlowLayout() );

Slower		= new Button( "<" );
Faster		= new Button( ">" );
SpeedLabel	= new Label( Speed );

add( Slower );
add( Faster );
add( SpeedLabel );
	}

//----

public boolean action( Event _e, Object _arg )
	{
//- This method handles the button presses
if ( _e.target == Slower )
		{
Speed	= FBook.DecSpeed();
		}
else if ( _e.target == Faster )
		{
Speed	= FBook.IncSpeed();
		}

SpeedLabel.setText( Speed );

return true;
	}

}

//-------------
//- End of file
//------------


 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.