import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A panel that shows randomly generated "art". When the user * clicks a "Start" button, a new random artwork is generated every * two seconds. *
This program demonstrates using a thread for a very simple * animation. (In fact, it would be more appropriate to use a * timer.) */ public class RandomArtWithThreads extends JPanel { /** * This main routine just shows a panel of type RandomArtWithThreads. */ public static void main(String[] args) { JFrame window = new JFrame("Demo: Animation with a Thread"); RandomArtWithThreads content = new RandomArtWithThreads(); window.setContentPane(content); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.pack(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); window.setLocation( (screenSize.width - window.getWidth()) / 2, (screenSize.height - window.getHeight()) / 2 ); window.setVisible(true); } private Display display; // A panel where the random "art" is drawn private JButton startButton; // Button for starting/stopping the animation private Runner runner; // The thread that drives the animation. private volatile boolean running; // Set to false to stop the thread. /** * This class defines the threads that drive the animation. */ private class Runner extends Thread { public void run() { while (running) { display.repaint(); try { Thread.sleep(2000); // Wait two seconds between repaints. } catch (InterruptedException e) { } } } } /** * A subpanel of type Display that draws the random "art". (The * paintComponent method is taken from the class RandomArt.) * If the signal variable, running, is false, only a gray background * is drawn. Art is only displayed when running is true. */ private class Display extends JPanel { Display() { setPreferredSize(new Dimension(500,400)); setBorder(BorderFactory.createLineBorder(Color.BLACK, 2)); } protected void paintComponent(Graphics g) { // Note: Since the next three lines fill the entire panel with // gray, there is no need to call super.paintComponent(g), since // any drawing that it does will only be covered up anyway. Color randomGray = Color.getHSBColor( 1.0F, 0.0F, (float)Math.random() ); g.setColor(randomGray); g.fillRect( 0, 0, getWidth(), getHeight() ); if (!running) { return; // don't draw art when not running. } int artType = (int)(4*Math.random()); switch (artType) { case 0: for (int i = 0; i < 500; i++) { int x1 = (int)(getWidth() * Math.random()); int y1 = (int)(getHeight() * Math.random()); int x2 = (int)(getWidth() * Math.random()); int y2 = (int)(getHeight() * Math.random()); Color randomHue = Color.getHSBColor( (float)Math.random(), 1.0F, 1.0F); g.setColor(randomHue); g.drawLine(x1,y1,x2,y2); } break; case 1: for (int i = 0; i < 200; i++) { int centerX = (int)(getWidth() * Math.random()); int centerY = (int)(getHeight() * Math.random()); Color randomHue = Color.getHSBColor( (float)Math.random(), 1.0F, 1.0F); g.setColor(randomHue); g.drawOval(centerX - 50, centerY - 50, 100, 100); } break; default: for (int i = 0; i < 25; i++) { int centerX = (int)(getWidth() * Math.random()); int centerY = (int)(getHeight() * Math.random()); int size = 30 + (int)(170*Math.random()); Color randomColor = new Color( (int)(256*Math.random()), (int)(256*Math.random()), (int)(256*Math.random()) ); g.setColor(randomColor); g.fill3DRect(centerX - size/2, centerY - size/2, size, size, true); } break; } } } /** * The constructor sets up the panel, containing the Display and the * Start button below it. */ public RandomArtWithThreads() { setLayout(new BorderLayout()); display = new Display(); add(display, BorderLayout.CENTER); startButton = new JButton("Start"); JPanel bottom = new JPanel(); bottom.add(startButton); bottom.setBackground(Color.WHITE); add(bottom,BorderLayout.SOUTH); startButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (running) stop(); else start(); } }); } /** * This method is called when the user clicks the Start button, * while no thread is running. It starts a new thread and * sets the signaling variable, running, to true; Also changes * the text on the Start button to "Finish". */ private void start() { startButton.setText("Finish"); runner = new Runner(); running = true; // Set the signal before starting the thread! runner.start(); } /** * This method is called when the user clicks the button while * a thread is running. A signal is sent to the thread to terminate, * by setting the value of the signaling variable, running, to false. * Also sets the text on the Start button back to "Start." */ private void stop() { startButton.setEnabled(false); // Disable until thread exits. /* Set the value of the signaling variable to false as a signal * to the thread to terminate. */ running = false; display.repaint(); // Repaint display, which will show only gray since running = false /* Wake the thread, in case it is sleeping, to get a more * immediate reaction to the signal. */ runner.interrupt(); /* Wait for the thread to stop before setting runner = null. * One second should be plenty of time for this to happen, but * in case something goes wrong, it's better not to wait forever. */ try { runner.join(1000); // Wait for thread to stop. One second should be plenty of time. } catch (InterruptedException e) { } runner = null; startButton.setText("Start"); startButton.setEnabled(true); } } // end RandomArtWithThreads