Mis à jour le 05/10/2017
  • 4 heures
  • Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Les noeuds graphiques

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Nous avons vu, dans le chapitre sur La scène graphique, comment insérer des objets graphiques dans la fenêtre de notre application. Nous avons inséré un objet de type Cercle et un autre de type Rectangle. Nous allons apprendre dans ce chapitre à créer nos propres nœuds graphiques, c'est-à-dire à créer nos propres classes graphiques. Ces classes n'ont rien de très différent des classes "normales", il y a juste quelques aspects particuliers à connaître que nous allons découvrir :) .

Nous allons créer la classe de notre objet Clavier, et donc la classe Touche qui va avec. Si vous êtes à l'aise avec la programmation orientée objet, ce chapitre ne vous posera aucun problème.

Création de notre première classe graphique

Pour créer nos propres types de nœuds graphiques, nous devons définir les classes associées, et ces classes doivent hériter de la classe Parent :

class Ma_classe extends Parent{

	Ma_classe(){
		//instructions du constructeur
	}
}

La classe que vous définirez ainsi se comportera comme un groupe d'objets graphiques auquel vous pourrez ajouter tous les objets que vous souhaitez : rectangle, cercle, image, texte... et vous pourrez appliquer à ce groupe tous les effets et toutes les transformations que vous souhaiterez : translation, rotation, effet d'éclairage...

Par exemple créons notre classe Clavier. Pour ça commencez par ajouter une nouvelle feuille de code à notre projet Melordi : click droit sur le package melordi situé dans le panel de gauche >> New >> Java Class... Entrez comme nom de classe "Clavier" puis cliquez sur Finish.

La feuille Clavier.java pré-remplie apparaît dans le panel central. Nous pouvons commencer par faire hériter cette classe de la classe Parent et lui ajouter son constructeur :

public class Clavier extends Parent{
    
    public Clavier(){
        
    }
}

Très bien, vous venez de créer votre premier nœud graphique. Nous pouvons dès maintenant créer un objet mon_clavier de type Clavier dans la fonction start() de notre classe Melordi (feuille Melordi.java) :

@Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Melordi");
        Group root = new Group();
        Scene scene = new Scene(root, 500, 500, Color.WHITE);
        
        Instru mon_instru = new Instru();
        
        Clavier mon_clavier = new Clavier();//on créé un objet clavier
        root.getChildren().add(mon_clavier);//on l'ajoute à notre groupe root
        
        primaryStage.setScene(scene);
        primaryStage.setVisible(true);
    }

Mais on n'a rien mis dans notre classe Clavier... le constructeur est vide !

Absolument, d'ailleurs si vous compilez vous verrez que notre scène sera toujours aussi vide. Un nœud graphique est un groupe d'objets graphiques, il peut très bien être vide... Bon, je suis d'accord, un groupe vide ne sert à rien, ajoutons lui les objets qui constitueront notre clavier :) .

Bon, retournons à notre feuille Clavier.java. Voici la forme que nous avions prévue de donner à notre clavier :

Image utilisateur

On peut commencer par créer le rectangle noir de fond du clavier. Ses dimensions seraient 400x200 et ses coins seraient arrondis. On a déjà vu comment faire :

public Clavier(){
        
        Rectangle fond_clavier = new Rectangle();
        fond_clavier.setWidth(400);
        fond_clavier.setHeight(200);
        fond_clavier.setArcWidth(30);
        fond_clavier.setArcHeight(30);
        fond_clavier.setFill(Color.BLACK);
        
        this.getChildren().add(fond_clavier);//on ajoute le rectangle au groupe
    }

Vous pouvez compiler pour voir notre beau rectangle :) .

Image utilisateur

Non seulement il n'est pas si beau que ça, et en plus il est mal placé...

C'est vrai, commençons par le positionner en bas et au centre de notre fenêtre :

public Clavier(){
        
        Rectangle fond_clavier = new Rectangle();
        fond_clavier.setWidth(400);
        fond_clavier.setHeight(200);
        fond_clavier.setArcWidth(30);
        fond_clavier.setArcHeight(30);
        fond_clavier.setFill(Color.BLACK);
        
        this.setTranslateX(50);//on positionne le groupe plutôt que le rectangle
        this.setTranslateY(250);
        this.getChildren().add(fond_clavier);
    }

Maintenant qu'il est bien placé je propose de le relooker, c'est vrai qu'il n'est pas très beau :( . Pour ça, colorions le avec un dégradé plutôt qu'une couleur unie et appliquons lui un effet de réflexion pour donner l'illusion qu'il est posé sur une surface lisse :

public Clavier(){
        
        Rectangle fond_clavier = new Rectangle();
        fond_clavier.setWidth(400);
        fond_clavier.setHeight(200);
        fond_clavier.setArcWidth(30);
        fond_clavier.setArcHeight(30);
        fond_clavier.setFill( //on remplie notre rectangle avec un dégradé
            new LinearGradient(0f, 0f, 0f, 1f, true, CycleMethod.NO_CYCLE,
                new Stop[] {
                    new Stop(0, Color.web("#333333")),
                    new Stop(1, Color.web("#000000"))
                }
            )
        );
        Reflection r = new Reflection();//on applique un effet de réflection
        r.setFraction(0.25);
        r.setBottomOpacity(0);
        r.setTopOpacity(0.5);
        fond_clavier.setEffect(r);
        
        this.setTranslateX(50);
        this.setTranslateY(250);
        this.getChildren().add(fond_clavier);
    }

Si vous compilez, le résultat devrait avoir un peu plus de classe ;) :

Image utilisateur

Pas mal non !!

Passons maintenant à la création des touches de notre clavier.

Création des touches

Nous avons dit dans notre cahier des charges que nous créerions une classe Touche. Notre clavier de touches sera donc un tableau de huit objets de type Touche.

Commençons par créer notre classe Touche (click droit sur le package melordi >> New >> Java Class... nommez la feuille "Touche" >> Finish). Puis comme pour la classe Clavier :

  • 1. Faites hériter la classe Touche de la classe Parent.

  • 2. Et ajouter le constructeur Touche().

Voici ce que devrait contenir la feuille Touche.java pour le moment :

package melordi;
import javafx.scene.Parent;

public class Touche extends Parent {
    
    public Touche(){
        
    }
}

Compilez pour être sûr que tout va bien.
Maintenant voyons ce que nous allons mettre dans cette classe. Un objet de type Touche contiendra deux éléments graphiques :

  • 1.Un objet de type Rectangle de 75 pixels de hauteur et de 75 de largeur :

    Rectangle fond_touche = new Rectangle(75,75,Color.WHITE);
    
  • 2.Un objet de type Text correspondant à la lettre de la touche : "U", "I", "O"... :

    Text lettre_touche = new Text(lettre);
    

Et chaque touche sera définie par quatre variables :

  • 1.La variable lettre de type String qui contiendra la lettre de la touche :

    private String lettre;
    
  • 2.La variable positionX de type int qui contiendra son abscisse X :

    private int positionX;
    
  • 3.La variable positionY de type int qui contiendra son ordonnée Y :

    private int positionY;
    
  • 4.La variable note de type int qui contiendra le numéro MIDI de la note qui doit être jouée quand l'utilisateur appuie sur la touche, c'est ce numéro que l'on enverra en paramètre de la fonction note_on() de l'objet mon_instru. Par exemple le numéro MIDI de la note Do est 60 :

    private int note;
    

Les valeurs de chacune de ces quatre variables seraient passées en paramètre du constructeur :

public Touche(String l, int posX, int posY, int n){
	//instructions
}

On obtiendrait ainsi la classe Touche suivante :

public class Touche extends Parent {
    
    public String lettre;//lettre de la touche, c'est une variable public pour qu'elle puisse être lue depuis les autres classes
    private int positionX = 0;//abscisse
    private int positionY = 0;//ordonnée de la touche
    private int note = 0;//note correspond au numéro MIDI de la note qui doit être jouée quand on appuie sur la touche
    
    Rectangle fond_touche;
    Text lettre_touche;
    
    public Touche(String l, int posX, int posY, int n){
        lettre =  new String(l);
        positionX = posX;
        positionY = posY;
        note = n;
        
        fond_touche = new Rectangle(75,75,Color.WHITE);
        fond_touche.setArcHeight(10);
        fond_touche.setArcWidth(10);
        this.getChildren().add(fond_touche);//ajout du rectangle de fond de la touche
        
        lettre_touche = new Text(lettre);
        lettre_touche.setFont(new Font(25));
        lettre_touche.setFill(Color.GREY);
        lettre_touche.setX(25);
        lettre_touche.setY(45);
        this.getChildren().add(lettre_touche);//ajout de la lettre de la touche
        
        this.setTranslateX(positionX);//positionnement de la touche sur le clavier
        this.setTranslateY(positionY);
    }
}

Vous pouvez compiler pour vérifier que tout se passe bien.
Nous pouvons maintenant retourner dans notre page Clavier.java pour créer notre tableau de huit objets de type Touche. Vous vous souvenez de la syntaxe pour déclarer un tableau :

Type[] mon_tableau = new Type[] = {…, …, …, …};

Dans notre cas ce sera un tableau de type Touche contenant huit éléments :

Touche[] touches = new Touche[] = {< 8 objets Touche >};

Avant de créer nos huit touches, collectons les valeurs de chacune des quatre variables qui caractérisent chaque touche de notre clavier Mélordi :

Image utilisateur

Le tableau suivant récapitule les valeurs de chaque variable de chacune des touches :

lettre

U

I

O

P

J

K

L

M

note

60

62

64

65

67

69

71

72

positionX

50

128

206

284

75

153

231

309

positionY

20

20

20

20

98

98

98

98

Nous pouvons maintenant créer et remplir notre tableau touches :

public class Clavier extends Parent{
    
    private Touche[] touches;
    
    public Clavier(){
        //…

        touches = new Touche[]{
            new Touche("U",50,20,60),
            new Touche("I",128,20,62),
            new Touche("O",206,20,64),
            new Touche("P",284,20,65),
            new Touche("J",75,98,67),
            new Touche("K",153,98,69),
            new Touche("L",231,98,71),
            new Touche("M",309,98,72)
        };
        
        //...
    }

Et pour terminer, maintenant que le tableau touches contient les huit touches de notre clavier, nous n'avons plus qu'à insérer son contenu dans le groupe de l'objet que nous sommes en train de créer :

for (Touche touche: touches){	//on insère chaque touche une par une.
            clavier.getChildren().add(touche);
        }

Voici à quoi devrait ressembler notre classe Clavier :

public class Clavier extends Parent{
    
    private Touche[] touches;
    
    public Clavier(){
        
        Rectangle fond_clavier = new Rectangle();
        fond_clavier.setWidth(400);
        fond_clavier.setHeight(200);
        fond_clavier.setArcWidth(30);
        fond_clavier.setArcHeight(30);
        fond_clavier.setFill( //on remplie notre rectangle avec un dégradé
            new LinearGradient(0f, 0f, 0f, 1f, true, CycleMethod.NO_CYCLE,
                new Stop[] {
                    new Stop(0, Color.web("#333333")),
                    new Stop(1, Color.web("#000000"))
                }
            )
        );
        Reflection r = new Reflection();//on applique un effet de réflection
        r.setFraction(0.25);
        r.setBottomOpacity(0);
        r.setTopOpacity(0.5);
        fond_clavier.setEffect(r);
        
        touches = new Touche[]{
            new Touche("U",50,20,60),
            new Touche("I",128,20,62),
            new Touche("O",206,20,64),
            new Touche("P",284,20,65),
            new Touche("J",75,98,67),
            new Touche("K",153,98,69),
            new Touche("L",231,98,71),
            new Touche("M",309,98,72)
        };
        
        this.setTranslateX(50);
        this.setTranslateY(250);
        this.getChildren().add(fond_clavier);
        for (Touche touche: touches){
            this.getChildren().add(touche);
        }
        
    }
}

Voilà le travail, vous pouvez enfin compiler, voici ce que vous êtes censés obtenir :

Image utilisateur

Un effet très intéressant que vous pourriez tester serait d'appliquer un effet d'éclairage aux touches pour leur donner un effet de relief (dans la classe Touche) :

Light.Distant light = new Light.Distant();
light.setAzimuth(-45.0);
Lighting li = new Lighting();
li.setLight(light);
fond_touche.setEffect(li);

Bon... ça commence à ressembler à un clavier :) . Mais ça n'y ressemble qu'en apparence, on ne peut toujours pas appuyer sur les touches pour jouer de la musique...

Dans le chapitre suivant, nous apprendrons à capturer les événements utilisateur et à donner vie à notre Mélordi :) .

Exemple de certificat de réussite
Exemple de certificat de réussite