Différences
Ci-dessous, les différences entre deux révisions de la page.
| Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
| restricted:mco-2:tp3 [2016/03/05 19:32] – fbrucker | restricted:mco-2:tp3 [2016/03/10 15:52] (Version actuelle) – [Mais que fait la police ?] new Font() => Font.font() adurand-gass | ||
|---|---|---|---|
| Ligne 1: | Ligne 1: | ||
| + | ====== TP3 ====== | ||
| + | **Bases de Java FX 8 : Dessinez c'est gagné.** | ||
| + | |||
| + | Ceci est une nouveauté de Java 8. Il est donc impératif que vous ayez un JDK 1.8 d' | ||
| + | |||
| + | Ce TP et les suivants sont basés sur plusieurs sources. Si vous êtes curieux, lisez les : | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | * [[http:// | ||
| + | |||
| + | ===== Hello World ===== | ||
| + | |||
| + | Pour tous les exercices de ce TP, on vous demandera de créer un projet Java simple (comme pour les 2 premiers TP). Nous ne créerons **pas** de projets directement //Java FX // ici. | ||
| + | |||
| + | Créez donc : | ||
| + | * un projet Java simple | ||
| + | * le package de base sera comme toujours '' | ||
| + | * Choisissez de créer un programme principal | ||
| + | |||
| + | Copiez/ | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.application.Application; | ||
| + | import javafx.stage.Stage; | ||
| + | |||
| + | public class Main extends Application { | ||
| + | public static void main(String[] args) { | ||
| + | launch(args); | ||
| + | } | ||
| + | |||
| + | public void start(Stage theStage) { | ||
| + | theStage.setTitle(" | ||
| + | theStage.show(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Exécutez le code. Vous devriez voir apparaître une fenêtre avec "Hello World!" | ||
| + | |||
| + | |||
| + | Particularité du code : | ||
| + | * la classe '' | ||
| + | * La classe '' | ||
| + | * Une fois l' | ||
| + | |||
| + | Cette forme d' | ||
| + | |||
| + | |||
| + | ===== Ajoutons du contenu ===== | ||
| + | |||
| + | Le //Stage// contient un //scene graph// contenant tout ce qui est représenté. Ce //scene graph// est un arbre dont : | ||
| + | * La racine est de classe [[https:// | ||
| + | * les feuilles sont de la classe [[https:// | ||
| + | * On peut ajouter un étage en ajoutant un noeud de classe [[https:// | ||
| + | |||
| + | {{ restricted: | ||
| + | |||
| + | |||
| + | ==== Utilisation du Scene Graph ==== | ||
| + | |||
| + | Ici nous allons : | ||
| + | * créer le scene graph, | ||
| + | * lui adjoindre son nœud root | ||
| + | * ajouter à cette racine une feuille qui sera du texte. | ||
| + | * paramétrer un peu notre texte. | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.application.Application; | ||
| + | import javafx.scene.Group; | ||
| + | import javafx.scene.Scene; | ||
| + | import javafx.scene.paint.Color; | ||
| + | import javafx.scene.text.Font; | ||
| + | import javafx.scene.text.Text; | ||
| + | import javafx.stage.Stage; | ||
| + | |||
| + | public class Main extends Application { | ||
| + | public static void main(String[] args) { | ||
| + | |||
| + | launch(args); | ||
| + | |||
| + | } | ||
| + | |||
| + | public void start(Stage theStage) { | ||
| + | theStage.setTitle(" | ||
| + | |||
| + | //mise en place du Stage : création du scene graph et du noeud racine. | ||
| + | |||
| + | Group root = new Group(); //ce ne sera pas une feuille, donc de classe Group. | ||
| + | Scene theScene = new Scene(root); | ||
| + | theStage.setScene(theScene); | ||
| + | |||
| + | //Création d'un text | ||
| + | Text text = new Text(); | ||
| + | text.setFont(Font.getDefault()); | ||
| + | text.setFill(Color.RED); | ||
| + | |||
| + | text.setText(" | ||
| + | text.setX(10); | ||
| + | text.setY(50); | ||
| + | |||
| + | //on l' | ||
| + | root.getChildren().add(text); | ||
| + | |||
| + | theStage.show(); | ||
| + | |||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | |||
| + | Spécificité du code : | ||
| + | * comment ajoute-t-on des fils au scene graph ? | ||
| + | * comment est placé le repère ? | ||
| + | * Pour afficher du Texte il faut : | ||
| + | * un objet de la classe [[https:// | ||
| + | * une [[https:// | ||
| + | |||
| + | ==== A vous ==== | ||
| + | |||
| + | Essayez d' | ||
| + | |||
| + | ===== Jouons avec le texte ===== | ||
| + | |||
| + | ==== Mais que fait la police ? ==== | ||
| + | |||
| + | Les polices de caractères installées changent d'un ordinateur à l' | ||
| + | |||
| + | <code java> | ||
| + | for (String fontName: Font.getFamilies()){ | ||
| + | | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Pour créer une nouvelle police on peut alors créer une nouvelle police ('' | ||
| + | |||
| + | <code java> | ||
| + | Font comic = Font.font(" | ||
| + | </ | ||
| + | |||
| + | Puis l' | ||
| + | |||
| + | <code java> | ||
| + | text.setFont(comic); | ||
| + | </ | ||
| + | |||
| + | ==== A vous ==== | ||
| + | |||
| + | Choisissez une police installée sur votre ordinateur et essayez là avec des tailles différentes. | ||
| + | |||
| + | ===== Des dessins ===== | ||
| + | |||
| + | Tout comme le [[https:// | ||
| + | |||
| + | ==== Carré blanc sur fond blanc ==== | ||
| + | |||
| + | Prenons nous pour [[https:// | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.application.Application; | ||
| + | import javafx.scene.Group; | ||
| + | import javafx.scene.Scene; | ||
| + | import javafx.scene.canvas.Canvas; | ||
| + | import javafx.scene.canvas.GraphicsContext; | ||
| + | import javafx.scene.paint.Color; | ||
| + | import javafx.stage.Stage; | ||
| + | |||
| + | public class Main extends Application { | ||
| + | public static void main(String[] args) { | ||
| + | launch(args); | ||
| + | |||
| + | } | ||
| + | |||
| + | |||
| + | public void start(Stage theStage) { | ||
| + | theStage.setTitle(" | ||
| + | |||
| + | //mise en place du Stage : création du scene graph et du noeud racine. | ||
| + | Group root = new Group(); //ce ne sera pas une feuille, donc de classe Group. | ||
| + | Scene theScene = new Scene(root); | ||
| + | theStage.setScene(theScene); | ||
| + | |||
| + | //Création d'un canvas de taille 512x512 pixels | ||
| + | Canvas canvas = new Canvas(512, 512); | ||
| + | GraphicsContext gc = canvas.getGraphicsContext2D(); | ||
| + | |||
| + | //un carré blanc sur fond blanc | ||
| + | gc.setFill(Color.WHITE); | ||
| + | gc.fillRect(0, | ||
| + | |||
| + | gc.setFill(Color.GHOSTWHITE); | ||
| + | gc.fillRect(100, | ||
| + | |||
| + | |||
| + | //on l' | ||
| + | root.getChildren().add(canvas); | ||
| + | |||
| + | theStage.show(); | ||
| + | |||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Dans un canvas on change des pixels de couleurs via un [[https:// | ||
| + | |||
| + | ==== A vous ==== | ||
| + | |||
| + | Dessinez quatre ronds comme [[https:// | ||
| + | Pour cela : | ||
| + | * vous pourrez utiliser une couleur semi-transparente en modifiant le chanel //alpha// qui va de 0.0 (transparent) à 1.0 (opaque). | ||
| + | * la méthode [[https:// | ||
| + | |||
| + | |||
| + | ===== Le Temps ===== | ||
| + | |||
| + | |||
| + | Il y a plusieurs manières de gérer le temps en Java (voir [[http:// | ||
| + | |||
| + | Pour cela on utilise une classe '' | ||
| + | |||
| + | La classe AnimationTimer définit une méthode start et stop pour démarrer et arrêter le timer. Nous n' | ||
| + | |||
| + | ==== Nanosecondes écoulées ==== | ||
| + | |||
| + | Le code suivant Crée un objet de classe Loop qui hérite de AnimationTimer. De là, après l' | ||
| + | |||
| + | == Main.java == | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.application.Application; | ||
| + | import javafx.stage.Stage; | ||
| + | |||
| + | public class Main extends Application { | ||
| + | |||
| + | public static void main(String[] args) { | ||
| + | launch(args); | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public void start(Stage primaryStage) { | ||
| + | primaryStage.setTitle(" | ||
| + | |||
| + | Loop loop = new Loop(); | ||
| + | |||
| + | loop.start(); | ||
| + | primaryStage.show(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Loop.java == | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.animation.AnimationTimer; | ||
| + | |||
| + | public class Loop extends AnimationTimer { | ||
| + | @Override | ||
| + | public void handle(long now) { | ||
| + | System.out.println(now); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </ | ||
| + | |||
| + | ==== A vous ==== | ||
| + | |||
| + | - Plutôt que d' | ||
| + | - Plutôt que d' | ||
| + | * on pourra passer un texte en paramètre de la création de l' | ||
| + | * on écrira son texte à chaque exécution de la méthode handle. | ||
| + | * on créera la scène avec une taille par défaut en utilisant [[https:// | ||
| + | |||
| + | |||
| + | ==== Carré ==== | ||
| + | |||
| + | Le delta de temps (mesuré ici en nanosecondes) entre deux exécution de handle nous permet par exemple de faire bouger de façon réaliste un objet. | ||
| + | |||
| + | Le code suivant fait bouger un carré de gauche à droite. | ||
| + | |||
| + | Attention à l' | ||
| + | |||
| + | == Main.java == | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.application.Application; | ||
| + | import javafx.scene.Group; | ||
| + | import javafx.scene.Scene; | ||
| + | import javafx.scene.canvas.Canvas; | ||
| + | import javafx.stage.Stage; | ||
| + | |||
| + | public class Main extends Application { | ||
| + | |||
| + | public static void main(String[] args) { | ||
| + | launch(args); | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public void start(Stage primaryStage) { | ||
| + | primaryStage.setTitle(" | ||
| + | |||
| + | Group root = new Group(); | ||
| + | Scene theScene = new Scene(root); | ||
| + | primaryStage.setScene(theScene); | ||
| + | |||
| + | Canvas canvas = new Canvas(512, 512); | ||
| + | root.getChildren().add(canvas); | ||
| + | |||
| + | Loop loop = new Loop(canvas); | ||
| + | |||
| + | loop.start(); | ||
| + | primaryStage.show(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Loop.java == | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.animation.AnimationTimer; | ||
| + | import javafx.scene.canvas.Canvas; | ||
| + | import javafx.scene.canvas.GraphicsContext; | ||
| + | import javafx.scene.paint.Color; | ||
| + | |||
| + | public class Loop extends AnimationTimer { | ||
| + | |||
| + | private Canvas canvas; | ||
| + | |||
| + | private long previousTime; | ||
| + | private double x, y; | ||
| + | |||
| + | public Loop(Canvas canvas) { | ||
| + | this.canvas = canvas; | ||
| + | this.previousTime = System.nanoTime(); | ||
| + | |||
| + | x = 0; | ||
| + | y = canvas.getHeight() / 2 - 50; | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public void handle(long now) { | ||
| + | long delta = now - previousTime; | ||
| + | previousTime = now; | ||
| + | |||
| + | x += (100 * 1E-9) * delta; //avance de 100px toute les secondes | ||
| + | |||
| + | if (x > canvas.getWidth() - 100) { //se cogne à droite de l' | ||
| + | x = canvas.getWidth() - 100 - 5; | ||
| + | } | ||
| + | |||
| + | GraphicsContext gc = canvas.getGraphicsContext2D(); | ||
| + | gc.setFill(Color.WHEAT); | ||
| + | gc.fillRect(0, | ||
| + | |||
| + | gc.setFill(Color.MAROON); | ||
| + | gc.fillRect(x, | ||
| + | |||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | === A vous === | ||
| + | |||
| + | Faites osciller le carré de gauche à droite et de droite à gauche. | ||
| + | |||
| + | ==== Une Horloge ==== | ||
| + | |||
| + | Nous avons ici fait un exemple complet en utilisant tout ce que vous avez appris jusque là. On a dans la mesure du possible utilisé des méthodes de code courantes. | ||
| + | |||
| + | |||
| + | Essayez de comprendre le code : | ||
| + | * Les constantes sont décrites comme attributs ce qui permet de les modifier aisément (NO MAGIC NUMBERS) | ||
| + | * Chaque méthode est décomposée en autant de " | ||
| + | * on utilise au maximum les attributs de chaque objet pour minimiser le nombre de paramètres de chaque méthode : un objet doit être une unité fonctionnelle simple. | ||
| + | |||
| + | == Main.java == | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.application.Application; | ||
| + | import javafx.scene.Group; | ||
| + | import javafx.scene.Scene; | ||
| + | import javafx.scene.canvas.Canvas; | ||
| + | import javafx.stage.Stage; | ||
| + | |||
| + | public class Main extends Application { | ||
| + | public static void main(String[] args) { | ||
| + | launch(args); | ||
| + | } | ||
| + | | ||
| + | public void start(Stage theStage) { | ||
| + | theStage.setTitle(" | ||
| + | |||
| + | Group root = new Group(); | ||
| + | Scene theScene = new Scene(root); | ||
| + | theStage.setScene(theScene); | ||
| + | |||
| + | Canvas canvas = new Canvas(512, 512); | ||
| + | root.getChildren().add(canvas); | ||
| + | |||
| + | Loop mainLoop = new Loop(canvas); | ||
| + | mainLoop.start(); | ||
| + | |||
| + | theStage.show(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | == Loop.java == | ||
| + | |||
| + | <code java> | ||
| + | package com.mco; | ||
| + | |||
| + | import javafx.animation.AnimationTimer; | ||
| + | import javafx.scene.canvas.Canvas; | ||
| + | import javafx.scene.canvas.GraphicsContext; | ||
| + | import javafx.scene.paint.Color; | ||
| + | |||
| + | |||
| + | public class Loop extends AnimationTimer { | ||
| + | private Canvas canvas; | ||
| + | |||
| + | final double ROTATION | ||
| + | final Color BACKGROUND_COLOR = Color.ALICEBLUE; | ||
| + | |||
| + | final Color WATCH_HAND_COLOR | ||
| + | final int WATCH_HAND_LINE_WIDTH = 10; | ||
| + | final double WATCH_HAND_LENGTH = 200; | ||
| + | |||
| + | private double angle; | ||
| + | private long previousTime; | ||
| + | |||
| + | public Loop(Canvas canvas) { | ||
| + | currentTime = System.nanoTime(); | ||
| + | this.canvas = canvas; | ||
| + | } | ||
| + | |||
| + | @Override | ||
| + | public void handle(long now) { | ||
| + | handleTime(now); | ||
| + | drawStuff(); | ||
| + | } | ||
| + | |||
| + | private void handleTime(long now) { | ||
| + | changeAngle(now - previousTime); | ||
| + | previousTime = now; | ||
| + | } | ||
| + | |||
| + | private void changeAngle(long delta) { | ||
| + | angle += ROTATION * delta; | ||
| + | angle %= 2 * Math.PI; | ||
| + | } | ||
| + | |||
| + | private void drawStuff() { | ||
| + | clearCanvas(); | ||
| + | drawWatchHand(); | ||
| + | } | ||
| + | |||
| + | private void clearCanvas() { | ||
| + | GraphicsContext gc = canvas.getGraphicsContext2D(); | ||
| + | |||
| + | gc.setFill(BACKGROUND_COLOR); | ||
| + | gc.fillRect(0, | ||
| + | } | ||
| + | | ||
| + | private void drawWatchHand() { | ||
| + | GraphicsContext gc = canvas.getGraphicsContext2D(); | ||
| + | |||
| + | double middleX = canvas.getWidth() / 2; | ||
| + | double middleY = canvas.getHeight() / 2; | ||
| + | |||
| + | gc.beginPath(); | ||
| + | gc.setLineWidth(WATCH_HAND_LINE_WIDTH); | ||
| + | gc.setStroke(WATCH_HAND_COLOR); | ||
| + | |||
| + | gc.moveTo(middleX, | ||
| + | gc.lineTo(middleX + WATCH_HAND_LENGTH * Math.cos(angle), | ||
| + | middleY + WATCH_HAND_LENGTH * Math.sin(angle)); | ||
| + | |||
| + | gc.stroke(); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ==== A vous ==== | ||
| + | |||
| + | Ajouter la petite aiguille à la montre. Elle doit tourner 12 fois moins vite que la grande aiguille et être 3 fois plus petite. | ||
| + | |||
| + | Normalement, | ||