C. Recanati
InHM Java
Master M1, 2012

TP 11

Exercice 1:
Finir d’abord le TP10. Mais pour ceux qui seraient débordés et penseraient ne pas pouvoir terminer ce TP (application Sketcher), vous pouvez chercher à la place l’exercice simplifié qui dessine interactivement un rectangle qui suit la souris dans le panneau d’un JFrame (solution à la fin de cette feuille).

Exercice 2: (AnimatorApplicationTimer.java)
On va créer une application animant un label en utilisant un timer,
comme dans le squelette vu en cours. La classe de l’application
étendra JFrame et implémentera ActionListener. L’animation
consistera simplement à afficher un JLabel indiquant
le nombre d’animations (appels d’action du timer) ayant eu lieu.
Le nombre d’animations devant se produire par seconde (aps) sera
passé en argument au programme et utilisé pour initialiser le
timer. Le constructeur du JFrame de l’application prendra en
argument ce nombre et une chaîne de titre pour la fenêtre.
On spécifiera en outre un écouteur pour les événements sur la
fenêtre (utiliser WindowAdapter) qui permettra d’arrêter
l’animation quand la fenêtre sera iconifiée, et de la relancer quand la
fenêtre réapparaîtra.
On quittera l’application si le système ferme la fenêtre. De
plus, l’utilisateur pourra stopper ou redémarrer l’animation en
cliquant sur le label (utiliser un MouseAdapter).
Pour gérer les déclenchements et arrêts de l’animation, on utilisera
un booléen indiquant si l’animation est gelée ou non et deux
procédures stopAnimation() et startAnimation() contrôlant le
timer.

Exercice 3: (MovingImageTimer.java)
On va créer une applet (ou une application) faisant avancer une
image d’avant-plan représentant une fusée (à récupérer dans
"~cathy/images/rocketship.gif") sur une image de fond
représentant un ciel étoilé ("~cathy/images/starfield.gif"). Le
principe du programme est le même que celui de l’exercice
précédent : utilisation d’un timer, arrêt et reprise de l’animation sur
un clic de souris ou sur iconification de la fenêtre.
On simplifie le programme précédent en fixant à 10 le nombre
d’animations par seconde.
1) Version applet. La classe de l’application étend JApplet et
implémente ActionListener. On déclarera le nombre
d’animations, un booléen pour savoir si l’animation est gelée, le
timer et un panneau d’animation. Plus éventuellement des variables
pour le nom des fichiers.
1a) La procédure init() chargera les images d’arrière plan et
d’avant-plan avec getImage :
Image bgImage =
getImage(getCodeBase(),"images/file.gif"));
puis appellera une procédure buildUI
buildUI(Container container, Image bgImage, Image
fgImage) pour construire l’interface utilisateur. buildUI
initialisera le timer et construira le panneau d’animation (une
extension de JPanel ayant pour membres les deux images et une
méthode paintComponent permettant d’afficher les deux images.
On affichera d’abord l’image de fond au centre du panneau, puis
l’image d’avant-plan en variant l’abscisse d’affichage selon le
nombre d’animations courant).
Le panneau créé sera ajouté au conteneur et gèrera les clics de
souris.
1b) Les procédures start() et stop() de l’applet appelleront
comme dans l’exemple du poly des procédures startAnimation()
et stopAnimation() qui gèreront le timer.

2) Version application (ou version mixte Applet/Application). On
garde l’architecture précédente (la classe d’application
MyAppletClass étend JApplet) mais on rajoute un main qui
initialise les images avec
Image bgImage =
Toolkit.getDefaultToolkit().getImage("file.gif");
On crée un JFrame et on initialise son contentPane en utilisant la
méthode buildUI de notre classe d’application, par exemple :
JFrame f = new JFrame("MovingImageTimer");
final MyAppletClass controller = new
MyAppletClass();
controller.buildUI(f.getContentPane(), bgImage,
fgImage);
On enregistre ensuite un écouteur sur le JFrame pour les
événements sur les fenêtres, on affiche le JFrame puis on appelle
controller.startAnimation().

// exercice 1 : un rectangle qui suit la souris
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.event.*;

public class DessinRect extends JFrame {	
	public DessinRect(String title) {
		super(title);
		Toolkit tlk = getToolkit();
		Dimension dim = tlk.getScreenSize();
		setBounds(dim.width/4, dim.height/4, 
					dim.width/2, dim.height/2);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		Panneau panel = new Panneau();
		getContentPane().add(panel, BorderLayout.CENTER);	
	}
	public static void main(String args[]) {
		DessinRect fenetre = 
				new DessinRect("Dessiner un rectangle");
		fenetre.setVisible(true);	
	}
}

class Panneau extends JPanel {
	public Panneau() {
			super();
			setBackground(Color.WHITE);
			MouseHandler mh;
			addMouseListener(mh = new MouseHandler() );
			addMouseMotionListener(mh);
	}
/* une classe interne pour gérer les événements souris */	
  class MouseHandler extends MouseInputAdapter {

	private Point debut, fin;
	private Rectangle2D.Double tempRect;
	private Graphics2D g2D;
					
	public void mousePressed(MouseEvent e) {
		debut = e.getPoint(); // on mémorise l'origine
	
		// on initialise g2D
        g2D = (Graphics2D) getGraphics();
        g2D.setStroke((Stroke) new BasicStroke(2));
        g2D.setPaint(Color.black);
        g2D.setXORMode(Color.white);
	}
			
      public void mouseDragged (MouseEvent e) {
        // deplacement de souris
        fin = e.getPoint(); // dernier point
        if (tempRect == null) {
        		// creer un premier rectangle tempRect 
			// à partir de debut
            tempRect = new 
			Rectangle2D.Double(Math.min(debut.x,fin.x), 
            		Math.min(debut.y, fin.y),
				Math.abs(debut.x - fin.x), 
            		Math.abs(debut.y - fin.y) );
        }
        else { 
            g2D.draw(tempRect);	// effacer le precedent rectangle
            // modifier le rectangle tempRect avant de l'afficher
		  tempRect.x = Math.min(debut.x, fin.x);
		  tempRect.y = Math.min(debut.y, fin.y);
		  tempRect.width = Math.abs(debut.x - fin.x);
		  tempRect.height = Math.abs(debut.y - fin.y);
        }
        g2D.draw(tempRect); // dessiner le nouveau rectangle
      }
            
      public void mouseReleased (MouseEvent e) {
         // relachement de la souris
        if (tempRect != null)
            tempRect = null;
        if (g2D != null) {
            g2D.dispose();
            g2D = null;
        }
        debut = fin = null;	
      }		
   }
}

// exercice 2 : AnimatorApplicationTimer.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnimatorApplicationTimer extends JFrame implements
ActionListener {
	int AnimationNumber = -1;
	Timer timer;
	boolean frozen = false;
	JLabel label;

	AnimatorApplicationTimer(int aps, String windowTitle) {
		super(windowTitle);
		int delay = (aps > 0) ? (1000 / aps) : 100;
//Initialise un timer qui appelle l'action handler de cet objet.
		timer = new Timer(delay, this);
		timer.setInitialDelay(0);
		timer.setCoalesce(true);
		addWindowListener(new WindowAdapter() {
			public void windowIconified(WindowEvent e) {
				stopAnimation();
			}
			public void windowDeiconified(WindowEvent e) {
				startAnimation();
			}
			public void windowClosing(WindowEvent e) {
				System.exit(0);
			}
		});
		label = new JLabel("Animation ", JLabel.CENTER);
		label.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				if (frozen) {
					frozen = false;
					startAnimation();
				} else {
					frozen = true;
					stopAnimation();
				}
			}
		});
		getContentPane().add(label, BorderLayout.CENTER);
	}
//Peut etre invoque par n'importe quel thread (thread-safe)
	public void startAnimation() {
		if (frozen) {
			//Ne rien faire. L'utilisateur demande qu'on
			//arrete de changer l'image.
		} else {
			//Commencer l'animation
			if (!timer.isRunning()) {
			timer.start();
			}
		}
	}
//Peut etre invoque par n'importe quel thread
	public void stopAnimation() {
		//Arrete le thread d'animation.
		if (timer.isRunning()) {
			timer.stop();
		}
	}
// l'action declenchee par le timer
	public void actionPerformed(ActionEvent e) {
		//Incremente le nombre d'animations et l'affiche
		AnimationNumber++;
		label.setText("Animation " + AnimationNumber);
	}
	public static void main(String args[]) {
		AnimatorApplicationTimer animator = null;
		int aps = 10;
	//Récupère le nombre d'animation par seconde (aps)
	// passé sur la ligne de commande en argument
		if (args.length > 0) {
			try {
				aps = Integer.parseInt(args[0]);
			} catch (Exception e) {}
		}
		animator = new AnimatorApplicationTimer(aps,
						"Animation avec Timer");
		animator.setBounds(50,100,400,200);
		animator.setVisible(true);
	//OK pour commencer l'animation ici puisque
	//startAnimation peut etre invoquee dans n'importe quel thread.
		animator.startAnimation();
	}
}

// exercice 3 : MovingImageTimer.java version Applet/Application
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/*
* Bouge une image d'avant plan (foreground)
* devant une image de fond (background).
*/
public class MovingImageTimer extends JApplet implements
			ActionListener {
	int AnimationNumber = -1;
	boolean frozen = false;
	Timer timer;
	AnimationPane animationPane;
	static String fgFile = "images/rocketship.gif";
	static String bgFile = "images/starfield.gif";
	//Invoquée seulement quand on tourne en applet.
	public void init() {
	//Recupere les fichiers images dans le repertoire du fichier code.
		Image bgImage = getImage(getCodeBase(), bgFile);
		Image fgImage = getImage(getCodeBase(), fgFile);
		buildUI(getContentPane(), bgImage, fgImage);
	}
	void buildUI(Container container,
				Image bgImage, Image fgImage) {
		int aps = 10;
		//Combien de millisecondes entre les animations
		int delay = (aps > 0) ? (1000 / aps) : 100;
	//Configure un timer qui appelle l'action handler de cet objet.
		timer = new Timer(delay, this);
		timer.setInitialDelay(0);
		timer.setCoalesce(true);
		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();
				}
			}
		});
	}
//Invoquée seulement avec un browser.
	public void start() {
		startAnimation();
	}
//Invoquée seulement avec un browser.
	public void stop() {
		stopAnimation();
	}
//Peut etre invoquée par n'importe quel thread
	public synchronized void startAnimation() {
		if (frozen) {
			// Ne rien faire. L'utilisateur a demandé
			// d'arrêter de changer l'image.
		} else {
			// lancer l'animation
			if (!timer.isRunning()) {
			timer.start();
		}
	}
}
// Peut être invoquée par n'importe quel thread
	public synchronized void stopAnimation() {
		// Stoppe le thread d'animation.
		if (timer.isRunning()) {
			timer.stop();
		}
	}
	public void actionPerformed(ActionEvent e) {
		// Incrémente le nombre d'animations
		AnimationNumber++;
		// Réaffiche le pannneau d'animation
		animationPane.repaint();
	}

class AnimationPane extends JPanel {
	Image background, foreground;
	public AnimationPane(Image background,
				Image foreground) {
			this.background = background;
			this.foreground = foreground;
	}
	// Peint le cadre d'animation courant.
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		int compWidth = getWidth();
		int compHeight = getHeight();
		int imageWidth, imageHeight;
		// Si on a des largeur et hauteur valides pour
		// l'image de fond, la dessiner au centre.
		imageWidth = background.getWidth(this);
		imageHeight = background.getHeight(this);
		if ((imageWidth > 0) && (imageHeight > 0)) {
			g.drawImage(background,
					(compWidth - imageWidth)/2,
					(compHeight - imageHeight)/2, this);
		}
		// Si on a des largeur et hauteur valides pour
		// l'image d'avant-plan, la dessiner
		imageWidth = foreground.getWidth(this);
		imageHeight = foreground.getHeight(this);
		if ((imageWidth > 0) && (imageHeight > 0)) {
			g.drawImage(foreground, ((AnimationNumber*5)
% (imageWidth + compWidth)) - imageWidth,
			(compHeight - imageHeight)/2, this);
		}
	}
}

// Invoquée seulement si on tourne comme application
public static void main(String[] args) {
	Image bgImage = Toolkit.getDefaultToolkit().getImage(
					MovingImageTimer.bgFile);
	Image fgImage = Toolkit.getDefaultToolkit().getImage(
					MovingImageTimer.fgFile);
	JFrame f = new JFrame("Timer déplaçant une image");
	final MovingImageTimer controller = new
							MovingImageTimer();
	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, 125));
	f.setVisible(true);
	controller.startAnimation();
}
}