package javacodebook.thread.animation; import java.util.Random; import java.awt.*; /** * Der Thread für die eigentliche Animation. Er dient dazu, alle 200 ms einen * Kreis einer zufälligen Farbe an eine zufällige Stelle im Zeichenbereich * zu zeichnen. */ public class AnimatorThread extends Thread { private static Random random = new Random(System.currentTimeMillis()); // Die Grösse der Kreise. private static int SIZE = 80; private Canvas animationArea; private Color color; private int xMax; private int yMax; private boolean stop = false; /** * Ein neuer AnimatorThread zum zeichnen von Kreisen. * @param color Die Farbe, in der die Kreise gezeichnet werden sollen * @param animationArea Hier soll reingezeichnet werden */ public AnimatorThread(Color color, Canvas animationArea) { this.animationArea = animationArea; this.color = color; // Die Grösse des Zeichenbereiches. Da sie sich nicht ändert, kann man // sie einmal am Anfang abfragen und dann speichern. xMax = (int)animationArea.getSize().getWidth(); yMax = (int)animationArea.getSize().getHeight(); } public void run() { int x,y; // Der Thread läuft solange, bis er ein Stop-Signal empfangen hat. while(!stop) { // Wo soll der Kreis hin? // Aufpassen, dass kein Kreis über den Rand gezeichnet wird! x = random.nextInt(xMax - SIZE); y = random.nextInt(yMax - SIZE); // Das Zeichnen erfolgt in einem synchronisierten Block. Damit wird // sichergestellt, dass während des Zeichnens eines Kreises kein // anderer Thread in den Bereich zeichnet. synchronized(animationArea) { Graphics g = animationArea.getGraphics(); g.setColor(color); g.fillArc(x, y, SIZE, SIZE, 0, 360); } // Nach getaner Arbeit legt sich der Thread für 200 ms schlafen. try { sleep(200); } catch (Exception ignore) {} } } /** * Mit dieser Methode kann der Thread gestoppt werden. */ public void stopExecution() { stop = true; } } --- Neue Klasse --- package javacodebook.thread.animation; import java.util.Random; import java.awt.*; /** * Dieser Thread dient dazu, den Anzeigebereich der Kreise in einem zeitlichen * Abstand von 5 Sekunden zu löschen (bzw. durch ein weißes Rechteck zu * überschreiben. */ class CleanerThread extends Thread { private Canvas animationArea; private int xMax; private int yMax; public CleanerThread(Canvas animationArea) { // Dies ist der Zeichenbereich, der gelöscht werden soll. this.animationArea = animationArea; // Der gesamte Bereich soll gelöscht werden. Dafür muß zunächst einmal // die Größe des Bereiches festgestellt werden. Da sich die Größe nicht // ändert, wird die Grösse hier einmal abgefragt. xMax = (int)animationArea.getSize().getWidth(); yMax = (int)animationArea.getSize().getHeight(); } public void run() { while(true) { // Das Löschen erfolgt in einem synchronisierten Block. Als // Lock-Object dient der Zeichenbereich, welchen auch die anderen // Threads als Lock-Object benutzen. Damit wird sichergestellt, // dass während des Löschen des Bereichs kein anderer Thread in // den Bereich zeichnet. synchronized(animationArea) { // Der Bereich wird jetzt durch ein weisses Rechteck // überschrieben. Graphics g = animationArea.getGraphics(); g.setColor(Color.white); g.fillRect(0, 0, xMax, yMax); } // Das Überschreiben erfolgt alle 5 Sekunden. try { sleep(5000); } catch (Exception ignore) {} } } } --- Neue Klasse --- package javacodebook.thread.animation; import java.awt.*; import java.awt.event.*; import java.util.Random; import java.util.Vector; /** * Der Demo-Frame für die Animation */ public class AnimationFrame extends Frame implements ActionListener { // Der Zeichenbereich für die Thread. Quasi die Spielwiese private Canvas animationArea; // Eine Liste aller zur Zeit laufender AnimatorThreads. private Vector animatorVector; private Button more; private Button less; private Button close; private Label label; private static Random random = new Random(System.currentTimeMillis()); public AnimationFrame() { super("Animation mittels Thread"); initComponents(); animatorVector = new Vector(); // Ein Thread zum Säubern des Zeichenbereiches wird gestartet. CleanerThread cleaner = new CleanerThread(animationArea); cleaner.start(); } private void initComponents() { // Buttons zum Starten und Stoppen von AnimatorThreads sowie zum // Beenden der gesamten Anwendung. more = new Button("Mehr"); less = new Button("Weniger"); close = new Button("Schliessen"); more.addActionListener(this); less.addActionListener(this); close.addActionListener(this); more.setActionCommand("more"); less.setActionCommand("less"); close.setActionCommand("close"); // In diesem Label wird angezeigt, wie viele Threads zur Zeit laufen. // Sind am Anfang 0 label = new Label("Anzahl Animator-Threads: 0"); // Anordnung der Buttons und des Labels in einer Reihe Panel buttonPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); buttonPanel.add(more); buttonPanel.add(less); buttonPanel.add(close); buttonPanel.add(label); // Hier nun endlich der Zeichenbereich, in den die AnimatorThread // ihre Kreise zeichnen dürfen. animationArea = new Canvas(); animationArea.setSize(new Dimension(600, 400)); // Als letztes werden die Buttons und der Canvas in den Frame gesetzt. setLayout(new BorderLayout()); add("South", buttonPanel); add("Center", animationArea); // Da zu Beginn kein Thread läuft, kann man auch den Button "weniger" // nicht nutzen. Daher wird er erst einmal deaktiviert. less.setEnabled(false); pack(); } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); // Der Benutzer möchte die Anwendung verlassen. if (command.equals("close")) { System.exit(0); } // Es soll ein weitere Thread gestartet werden. else if (command.equals("more")) { // Zunächst eine zufällige Farbe für die Kreise wählen. int red = random.nextInt(255); int green = random.nextInt(255); int blue = random.nextInt(255); Color color = new Color(red, green, blue); // Einen neuen AnimatorThread erzeugen und starten. AnimatorThread newThread = new AnimatorThread(color, animationArea); newThread.start(); // Der Thread kommt in den Vector, damit er später wieder gestoppt // werden kann. animatorVector.add(newThread); // Da nun mind. ein Thread läuft, kann man auch den Button "weniger" // nutzen. less.setEnabled(true); // Die Anzeige für die Anzahl der laufenden Thread aktualisieren int threadCount = animatorVector.size(); label.setText("Anzahl Animator-Threads: " + threadCount); } else if (command.equals("less")) { // Der Thread, der zuerst gestartet wurde, wird gestoppt und aus // der Liste der laufenden Threads entfernt. AnimatorThread thread = (AnimatorThread)animatorVector.remove(0); thread.stopExecution(); // Wenn kein Thread mehr läuft muß der Button "weniger" deaktiviert // werden int threadCount = animatorVector.size(); if (threadCount == 0) less.setEnabled(false); // Die Anzeige für die Anzahl der laufenden Thread aktualisieren label.setText("Anzahl Animator-Threads: " + threadCount); } } } --- Neue Klasse --- package javacodebook.thread.animation; /** * Startet das Beispiel */ public class Starter { public static void main(String args[]) { // Der Animationsframe wird erzeugt und mit // show() angezeigt. AnimationFrame frame = new AnimationFrame(); frame.show(); } }