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:11] – 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, |