Download Ssjava.java source file
// S.S. Java 1 - an animation demo adapted from Sun's Java Tutorial. // This version uses a swing timer to send action events at regular // intervals, which are used to move and repaint the animation. // The space ship S.S. Java is moved across a star field, and the // user can click the mouse to start/stop the animation. // // The animation uses a "frame number" to advance the image // a certain number of pixels per repaint update (5). This number // is used modulo the width of the applet (which is the width of // the background image). // // Notice how when used as an appet a MediaTracker is used to // preload the image, and how the Applet start/stop methods are // used in conjunction with the boolean "frozen" to control the // animation. Also a main has been included so this program can // be run as a stand-alone as well. Notice no media tracker is // needed in this case, but certain window event handler methods // are used to control the animation, just like the applet start // and stop. // // Notice too the new style of showing multiple closing braces // on a single line. I feel this is just as readable, see if you do. // // Written 3/2001 by Wayne Pollock, Tampa Florida USA. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Ssjava1 extends JApplet implements ActionListener { int frameNumber = -1; static final int pelsPerUpdate = 2; // a "pel" is a "pixel" boolean frozen = false; Timer timer; AnimationPane animationPane; // Should probably use PARAM tags (and cmd line args) to set these: static String fgFile = "rocketship.gif"; static String bgFile = "starfield.gif"; //Invoked only when run as an applet, don't invoke from main: public void init() { // Get and load the images: Image bgImage = getImage( getCodeBase(), bgFile ); Image fgImage = getImage( getCodeBase(), fgFile ); MediaTracker mt = new MediaTracker( this ); mt.addImage( bgImage, 0 ); mt.addImage( fgImage, 1 ); try { mt.waitForAll(); } catch ( InterruptedException ignored ) {} buildUI( getContentPane(), bgImage, fgImage ); } void buildUI ( Container container, Image bgImage, Image fgImage ) { int fps = 25; // Frames per Second, should be >28 to appear smooth. // How many milliseconds between frames? // 1000 / (frames per second) = milliseconds delay per frame. int delay = ( fps > 0 ) ? ( 1000 / fps ) : 40; // Set up a swing timer that calls this object's action handler: timer = new Timer( delay, this ); timer.setInitialDelay( 0 ); // Start immediately. timer.setCoalesce( true ); // ok to skip updates if falling behind. animationPane = new AnimationPane( bgImage, fgImage ); container.add( animationPane, BorderLayout.CENTER ); animationPane.addMouseListener( new MouseAdapter() { public void mousePressed( MouseEvent e ) { if ( frozen ) { frozen = false; startAnimation(); } else { frozen = true; stopAnimation(); } } } ); } // Invoked by a browser only: public void start () { startAnimation(); } // Invoked by a browser only: public void stop () { stopAnimation(); } // Can be invoked from any thread, for instance if a user clicks then // immediately clicks "Back" then this might be called simultaneously // from main and the AWT Event Dispatch Thread. // Since "frozen" and "timer" are shared resources make method synchronized: public synchronized void startAnimation () { if ( ! frozen ) { // Start animating only if not frozen and not already running: if ( ! timer.isRunning() ) { timer.start(); } } } // Can be invoked from any thread like startAnimation: public synchronized void stopAnimation () { // Stop the animating thread: if ( timer.isRunning() ) { timer.stop(); } } public void actionPerformed ( ActionEvent e ) { // Advance animation frame counter and repaint: frameNumber++; animationPane.repaint(); } // An inner class, this is all that actually gets repainted: class AnimationPane extends JPanel { Image background, foreground; public AnimationPane ( Image background, Image foreground ) { this.background = background; this.foreground = foreground; } // Draw the current frame of animation: public void paintComponent ( Graphics g ) { super.paintComponent( g ); //paint any space not covered //by the background image int compWidth = getWidth(); int compHeight = getHeight(); // Safty check: Only draw if we have a valid image: int imageWidth = background.getWidth( this ); int imageHeight = background.getHeight( this ); if ( (imageWidth > 0) && (imageHeight > 0) ) { g.drawImage( background, (compWidth - imageWidth)/2, (compHeight - imageHeight)/2, this ); } imageWidth = foreground.getWidth( this ); imageHeight = foreground.getHeight( this ); if ( (imageWidth > 0) && (imageHeight > 0) ) { int pos = ( (frameNumber * pelsPerUpdate) % (imageWidth + compWidth) ) - imageWidth; g.drawImage( foreground, pos, (compHeight - imageHeight)/2, this ); } } } // End of AnimationPane class. // Invoked only when run as an application: public static void main ( String [] args ) { Toolkit tk = Toolkit.getDefaultToolkit(); Image bgImage = tk.getImage( bgFile ); Image fgImage = tk.getImage( fgFile ); JFrame f = new JFrame( "S. S. Java 1" ); // Anonymous inner classes can only access final variables: final Ssjava1 controller = new Ssjava1(); controller.buildUI( f.getContentPane(), bgImage, fgImage ); f.addWindowListener( new WindowAdapter() { public void windowIconified ( WindowEvent e ) { controller.stopAnimation(); } public void windowDeiconified ( WindowEvent e ) { controller.startAnimation(); } public void windowClosing ( WindowEvent e ) { System.exit(0); } } ); f.setSize( new Dimension( 500, 100 ) ); f.setVisible( true ); controller.startAnimation(); } }