ProblemYou need to update a graphical display while other parts of the program are running. SolutionUse a background thread to drive the animation. DiscussionOne common use of threads is an animator, a class that displays a moving image. This "animator" program does just that. It draws a graphical image (see Recipe 13.8) at locations around the screen; the location is updated and redrawn from a Thread for each such image. This version is an applet, so we see it here in the AppletViewer (Figure 24-1). Figure 24-1. AnimatorThe code for the animator program consists of two classes, Sprite (Example 24-4) and Bounce[3] (Example 24-5). A Sprite is one image that moves around; Bounce is the main program.
Example 24-4. Sprite.java (part of animator applet)import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; /** A Sprite is one Image that moves around the screen on its own */ class Sprite extends Component implements Runnable { protected static int spriteNumber = 0; protected Thread t; protected int x, y; protected Bounce parent; protected Image img; protected boolean done = false; /** Construct a Sprite with a Bounce parent: construct * and start a Thread to drive this Sprite. */ Sprite(Bounce parent, Image img) { super( ); this.parent = parent; this.img = img; setSize(img.getWidth(this), img.getHeight(this)); t = new Thread(this); t.setName("Sprite #" + ++spriteNumber); t.start( ); } /** Stop this Sprite's thread. */ void stop( ) { System.out.println("Stopping " + t.getName( )); done = true; } /** * Run one Sprite around the screen. * This version is very stupid, and just moves them around * at some 45-degree angle. */ public void run( ) { int width = parent.getSize( ).width; int height = parent.getSize( ).height; // Random location x = (int)(Math.random( ) * width); y = (int)(Math.random( ) * height); // Flip coin for x & y directions int xincr = Math.random( )>0.5?1:-1; int yincr = Math.random( )>0.5?1:-1; while (!done) { width = parent.getSize( ).width; height = parent.getSize( ).height; if ((x+=xincr) >= width) x=0; if ((y+=yincr) >= height) y=0; if (x<0) x = width; if (y<0) y = height; // System.out.println("Move " + t.getName( ) + " from " + // getLocation( ) + " to " + x + "," + y); setLocation(x, y); repaint( ); try { Thread.sleep(250); } catch (InterruptedException e) { return; } } } /** paint -- just draw our image at its current location */ public void paint(Graphics g) { g.drawImage(img, 0, 0, this); } } } Example 24-5. Bounce.java (part of animator applet)import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; /** This is the Bounce class; create and start Sprites, using Threads. */ public class Bounce extends Applet implements ActionListener { /** The main Panel */ protected Panel p; /** The image, shared by all the Sprite objects */ protected Image img; /** A Vector of Sprite objects. */ protected Vector v; public void init( ) { Button b = new Button("Start"); b.addActionListener(this); setLayout(new BorderLayout( )); add(b, BorderLayout.NORTH); add(p = new Panel( ), BorderLayout.CENTER); p.setLayout(null); String imgName = getParameter("imagefile"); if (imgName == null) imgName = "duke.gif"; img = getImage(getCodeBase( ), imgName); MediaTracker mt = new MediaTracker(this); mt.addImage(img, 0); try { mt.waitForID(0); } catch(InterruptedException e) { throw new IllegalArgumentException( "InterruptedException while loading image " + imgName); } if (mt.isErrorID(0)) { throw new IllegalArgumentException( "Couldn't load image " + imgName); } v = new Vector( ); } public void actionPerformed(ActionEvent e) { System.out.println("Creat-ing another one!"); Sprite s = new Sprite(this, img); s.start( ); p.add(s); v.addElement(s); } public void stop( ) { for (int i=0; i<v.size( ); i++) { ((Sprite)(v.get(i))).stop( ); } v.clear( ); } } |