Aller au contenu

Programmation JavaFX/Version imprimable

Un livre de Wikilivres.

Ceci est la version imprimable de Programmation JavaFX.
  • Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
  • Cliquez sur Rafraîchir cette page pour obtenir la dernière version du wikilivre.
  • Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l'article Versions imprimables.


Programmation JavaFX

Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_JavaFX

Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la Licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans Texte de dernière page de couverture. Une copie de cette licence est incluse dans l'annexe nommée « Licence de documentation libre GNU ».

Introduction

JavaFX est un framework et une bibliothèque d'interface utilisateur issue du projet OpenJFX, qui permet aux développeurs Java de créer une interface graphique pour des applications de bureau, des applications internet riches et des applications smartphones et tablettes tactiles. Il permet également le rendu de scènes 3D.

JavaFX a été conçu initialement pour remplacer Swing et AWT à long terme.

Le framework JavaFX est intégré aux versions 7 à 10 du JDK et du JRE (librairie jfxrt.jar). Il n'y a donc aucun logiciel supplémentaire à installer.

<JDKDIR>/jre/lib/ext/jfxrt.jar

Des applications de démonstration sont fournies avec le JDK dans un sous-répertoire

<JDKDIR>/demo/javafx_samples

Lancez en particulier l'application Ensemble8.jar qui donne un large aperçu de toutes les possibilités de JavaFX. Le code source de chaque démonstration est composé souvent d'un seul fichier, disponible par simple clic sur le lien "View source" associé à la démo.

À partir de Java 11, JavaFX (version 11 également) est un module indépendant à installer séparément[1] depuis le site suivant :

https://openjfx.io/

Les packages de JavaFX

[modifier | modifier le wikicode]

Le framework JavaFX est composé de classes situées dans les packages commençant par javafx :

javafx.application
Les classes pour la gestion de l'application JavaFX.
javafx.stage
Les classes pour la gestion des fenêtres applicatives (Stage).
javafx.scene
Les classes pour la gestion des scènes qui définissent le contenu des fenêtres. Ce contenu est constitués de nœuds (classe Node).
javafx.scene.control
Composants d'interface utilisateur : boutons, bouton radio, case à cocher, champ de texte, ...
javafx.scene.layout
Gestion de la taille et du positionnement des nœuds composant un scène.
javafx.scene.shape
Formes de base en 2D et 3D : rectangle, cercle, courbe, chemin, polygone, sphère, boîte, cylindre, ...
javafx.scene.paint
Couleur et autres objets tels que les dégradés (radial, linéaire, ...) pour le remplissage et le traçage des formes et du texte.
javafx.scene.image
Classes pour le chargement et l'affichage des images.
javafx.scene.text
Classes pour l'affichage du texte, gestion des polices de caractères.
javafx.scene.transform
Transformation 2D et 3D des nœuds : translation, rotation, échelle, étirage. Ces transformations sont aussi accessibles par appel de méthode sur les nœuds.
javafx.scene.input
Gestion des interactions avec l'utilisateur par le clavier et la souris.
javafx.scene.effect
Effets graphiques par application de filtres.
javafx.scene.effect.light
javafx.scene.chart
Création et gestion de graphiques : courbe de points, graphique à barres, camembert, ...
javafx.scene.chart.data
Gestion des données pour les graphiques.
javafx.scene.chart.part
.
javafx.geometry
Les classes de gestion des dimensions et positions des nœuds (éléments d'une scène) en 2D et 3D.
javafx.ext.swing
Adaptateurs pour encapsuler des composants Swing dans une scène. Ces classes sont en général utilisées pour effectuer une transition des composants Swing vers leur équivalents JavaFX.

Utilisation avec Eclipse

[modifier | modifier le wikicode]

Après la création d'un nouveau projet Eclipse ou l'import d'un projet existant utilisant un package de JavaFX, il est possible que Eclipse affiche des messages d'erreurs de ce type :

Access restriction: The type 'Application' is not API (restriction on required library jfxrt.jar)

Dans ce cas, il faut ajouter une règle d'accès dans le "Build Path" du projet :

  1. Cliquer avec le bouton droit sur le projet, "Build Path" > "Configure Build Path...".
  2. Dans l'onglet "Libraries", déplier le nœud "JRE System Library".
  3. Cliquer "Access rules", puis le bouton "Edit..." situé à droite.
  4. Dans la nouvelle fenêtre, cliquer le bouton "Add..." situé à droite.
  5. Mettre "Accessible" comme valeur pour "Resolution".
  6. Entrer "javafx/**" comme valeur pour "Rule Pattern".
  7. Valider toutes les fenêtres de dialogues ouvertes.

Le chapitre suivant décrits les objets composant une application JavaFX.

  1. https://www.infoworld.com/article/3305073/removed-from-jdk-11-javafx-11-arrives-as-a-standalone-module.html


Composants d'une application

Ce chapitre est important car il présente les classes de bases communes aux applications JavaFX. Un exemple illustre ce chapitre, complété au fur et à mesure des notions introduites dans les sections du chapitre.

Ordre d'appel des méthodes

Application est le nom de la classe Abstraite à implémenter par la sous-classe représentant l'application JavaFX.

Cette sous-classe ne comporte pas obligatoirement une méthode statique main car la JVM peut lancer directement un JAR contenant une application JavaFX en utilisant certains attributs particuliers du fichier de manifeste.

Elle suit un cycle particulier :

  • La méthode statique launch(Class c, String[] args) est appelée soit par une méthode main(String[] args), soit directement par la JVM. Le premier argument est la classe implémentant la classe abstraite Application et peut être omis, auquel cas la méthode utilise la classe à partir de laquelle elle a été appelée. Le second argument est un tableau des arguments de la ligne de commande (voir section « Récupération des arguments de la ligne de commande » ci-dessous).
  • Cette méthode initialise l'environnement JavaFX et appelle ensuite la méthode init().
  • La méthode start(Stage stage) est ensuite appelée pour démarrer l'application : créer l'interface et les scènes à afficher dans la fenêtre (stage) donnée en argument. La sous-classe de Application doit implémenter cette méthode.
  • La méthode stop() est appelée pour arrêter l'application, généralement quand la fenêtre de l'application est fermée. L'implémentation par défaut ne fait rien.

Récupération des arguments de la ligne de commande

[modifier | modifier le wikicode]

Quel que soit le mode de lancement de l'application, la méthode getParameters() de la classe Application retourne un objet de classe Parameters :

Parameters params = getParameters();

Les méthodes de la classe Parameters permettent de récupérer les arguments de la ligne de commande :

List<String> getRaw()
Cette méthode retourne une liste contenant tous les arguments de la ligne de commande.
Map<String,String> getNamed()
Cette méthode retourne un dictionnaire de tous les arguments nommés de la forme suivante :
--name=value
List<String> getUnnamed()
Cette méthode retourne une liste contenant les autres arguments (non nommés) de la ligne de commande.
package org.wikibooks.fr.javafx;

import java.util.*;

import javafx.application.Application;
import javafx.stage.Stage;

public class FirstExample extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		Group root = new Group();
		Scene scene = new Scene(root, 400, 200); // Fenêtre 400x200 pixels

		stage.setScene(scene);
		stage.setTitle("Exemple de scene vide"); // Titre de la fenêtre
		stage.show();

		// Afficher tous les arguments passés à l'application
		// Exemple: test --limit=20 example --out=file.dat
		//   Paramètre limit = 20
		//   Paramètre out = file.dat
		//   Autre paramètre: test
		//   Autre paramètre: example
		Parameters params = getParameters();
		for(Map.Entry<String, String> named_parameter : params.getNamed().entrySet())
			System.out.println("  Paramètre "+named_parameter.getKey()+" = "+named_parameter.getValue());
		for(String p : params.getUnnamed())
			System.out.println("  Autre paramètre: "+p);
	}
}

Une instance de la classe Stage représente une fenêtre d'application JavaFX.

Les principales méthodes sont les suivantes:

setTitle(String title)
Définir le titre de la fenêtre.
setScene(Scene scene)
Définir la scène à afficher.
show()
Afficher la fenêtre.

Il est également possible d'ajouter une ou plusieurs icônes en modifiant la liste observable retournée par la méthode getIcons().

Liste observable

[modifier | modifier le wikicode]

Une liste observable est une liste générique dont les modifications sont notifiés aux instances des classes implémentant l'interface ListChangeListener enregistrées sur la liste par la méthode addListener. Les listes observables sont très utilisées dans l'API JavaFX.

Example de scene contenant des nœuds 2D

Une scène est composée de nœuds (Node). Une scène comporte des nœuds 2D (composants : boutons, labels...) ou 3D (formes : sphères, boîtes, cylindres...).

package org.wikibooks.fr.javafx;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.Scene;
import javafx.scene.shape.Box;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.Sphere;
import javafx.stage.Stage;

public class First3DScene extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		// Create a Box
		Box box = new Box(100, 100, 100);
		box.setTranslateX(150);
		box.setTranslateY(0);
		box.setTranslateZ(400);

		// Create a Sphere
		Sphere sphere = new Sphere(50);
		sphere.setTranslateX(300);
		sphere.setTranslateY(-5);
		sphere.setTranslateZ(400);

		// Create a Cylinder
		Cylinder cylinder = new Cylinder(40, 120);
		cylinder.setTranslateX(500);
		cylinder.setTranslateY(-25);
		cylinder.setTranslateZ(600);

		// Create a Light
		PointLight light = new PointLight();
		light.setTranslateX(350);
		light.setTranslateY(100);
		light.setTranslateZ(300);

		// Create a Camera to view the 3D Shapes
		PerspectiveCamera camera = new PerspectiveCamera(false);
		camera.setTranslateX(100);
		camera.setTranslateY(-50);
		camera.setTranslateZ(300);

		// Add the Shapes and the Light to the Group
		Group root = new Group(box, sphere, cylinder, light);

		// Create a Scene with depth buffer enabled
		Scene scene = new Scene(root, 400, 200, true);
		// Add the Camera to the Scene
		scene.setCamera(camera);

		// Add the Scene to the Stage
		stage.setScene(scene);
		// Set the Title of the Stage
		stage.setTitle("An Example with Predefined 3D Shapes");
		// Display the Stage
		stage.show();
	}
}

Classes similaires à AWT et Swing

[modifier | modifier le wikicode]

JavaFX a été conçu pour remplacer Swing et AWT à long terme. Le framework est donc indépendant et possède des classes spécifiques, nommées comme celles de Swing et AWT et jouant à peu près le même rôle mais sont différentes. Ce qui veut dire qu'il n'est pas possible d'utiliser directement les classes de JavaFX en Swing/AWT et vice-versa.

La spécificité des classes de JavaFX permet leur utilisation en 2D et en 3D.

Color
Représente une couleur, modélisée par les composantes RVB (Rouge, Vert, Bleu) dont les valeurs sont de type double et comprises entre 0.0 et 1.0.
Image
Représente une image.

Un nœud représenté un élément affichable dans une scène.

Certaines classes d'objet ne sont pas des nœuds ; leurs instances doivent alors être encapsulées dans une vue (View) gérant le type d'objet :

  • ImageView pour les objets de classe Image ;
  • MeshView pour les objets de classe Mesh (notamment la sous-classe TriangleMesh) ;
  • ...

Exemple : afficher une image

[modifier | modifier le wikicode]
package org.wikibooks.fr.javafx;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ShowImage extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage)
	{
		StackPane sp = new StackPane();
		Image im = new Image("image.jpg"); // Chemin local relatif
		// Image im = new Image("file://///E:/Images/image_8.jpg"); // ou chemin local absolu : URL
		// Image im = new Image(getClass().getResourceAsStream("logo.png")); // ou resource du package
		// Image im = new Image("http://www.monserveur.com/images/example.png"); // ou URL internet
		ImageView im_view = new ImageView(im);
		sp.getChildren().add(im_view);

		Scene scene = new Scene(sp);

		stage.setScene(scene);
		stage.setTitle("Afficher une image");
		stage.show();
	}
}


Classes 2D

Hiérarchie des classes 2D

[modifier | modifier le wikicode]

La hiérarchie de classes ci-dessous montrent les classes communes aux application 2D et 3D, vues précédemment et les classes spécifiques à la 2D.

Classes de base

[modifier | modifier le wikicode]

La classe de base des composants JavaFX est Node. Il s'agit de la classe utilisée pour l'ajout de nœuds dans les conteneurs (par exemple Pane).

Parmi les sous-classes, il y a la classe Parent servant de base à tous les composants pouvant contenir d'autres composants (nœuds enfants dans l'arbre). Il y a également la sous-classe MediaViewer héritant directement de la classe Node, ce qui signifie que ce composant ne peut pas contenir d'autres composants, et aussi qu'il peut être ajouté à n'importe quel composant Parent. Voir le chapitre Jouer une vidéo ou un son pour une utilisation concrète de la classe MediaViewer.

La classe Region est la classe de base des composants de type Pane et définit une composition similaire à celle du modèle de boîte en CSS[source 1] :

marge extérieure (Margin)

bordure (Border)

espace intérieur (Padding)

aire intérieure (ContentArea)

 

 

 

La similitude avec le modèle de boîte en CSS est due au fait que les composants JavaFX peuvent être stylisés avec des règles CSS (voir le livre Le langage CSS).

Styles CSS sur les composants JavaFX

[modifier | modifier le wikicode]

Les composants JavaFX dérivant de la classe Node peuvent être stylisés en CSS. Cependant, les styles CSS supportés sont limités[source 2], car basés sur W3C CSS version 2.1 avec quelques ajouts spécifiques à JavaFX. De plus les propriétés doivent être préfixées par -fx-.

Exemple de code :

package org.wikibooks.fr;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.stage.*;

public class CssTest extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	@Override
	public void start(Stage stage) throws Exception
	{
		StackPane root = new StackPane();
		root.setStyle("-fx-background-color: #00f;"); // Style CSS : fond bleu.

		// Création de la scène :
		Scene scene = new Scene(root, 500, 500, Color.BLACK);
		stage.setScene(scene);
		stage.setTitle("CSS test");
		stage.show();   
	}
}


  1. JavaFX Region - https://jenkov.com/tutorials/javafx/region.html
  2. JavaFX CSS Reference Guide - https://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html


Formes 3D

Une scène en 3D est composée de formes 3D, de source lumineuses et d'une caméra définissant le point de vue de la scène.

Coordonnées et orientation des axes 3D

[modifier | modifier le wikicode]

Les coordonnées d'un point ou d'une forme est un ensemble de 3 valeurs de type double, nommées X, Y et Z.

L'orientation par défaut des axes est le suivant :

  • L'axe X est orienté de la gauche vers la droite : une valeur plus élevée de X donne une position relative plus à droite.
  • L'axe Y est orienté du haut vers le bas.
  • L'axe Z est orienté du plus proche au plus loin.

Classe de base

[modifier | modifier le wikicode]

La classe Shape3D est la classe de base des formes 3D. Elle définit les méthodes communes à toutes les formes, permettant de les placer/déplacer et de les orienter.

La position initiale à la création d'une forme 3D est (0,0,0).

setTranslateX(double)
Incrémente la coordonnée X du nœud.
setTranslateY(double)
Incrémente la coordonnée Y du nœud.
setTranslateZ(double)
Incrémente la coordonnée Z du nœud.

La classe Shape3D a 4 implémentations concrètes :

  • La classe Sphere
  • La classe Cylinder
  • La classe Box représente une boîte ou parallélépipède rectangle ou pavé droit.
  • La classe MeshView permet de définir une forme particulière à partir d'une instance de la classe abstraite Mesh. La classe abstraite Mesh possède la sous-classe TriangleMesh comme implémentation concrète, définissant la forme à partir de triangles.

Les sous-classes présentées ci-dessous ont des caractéristiques communes :

  • Par défaut, les formes sont centrées sur le point d'origine (0,0,0).
  • Si les dimensions ne sont pas spécifiées (constructeur sans arguments), les dimensions par défaut sont définies par une boîte 2x2x2.

Sphère (classe Sphere)

[modifier | modifier le wikicode]

Les paramètres définissant une sphère sont les suivants (même ordre que les constructeurs de la classe) :

double radius
(Optionnel) Rayon de la sphère. La valeur par défaut est 1.0.
int divisions
(Optionnel) Nombre de divisions de la circonférence. La valeur par défaut est 64.
Sphere sphere = new Sphere(100);

Cylindre (classe Cylinder)

[modifier | modifier le wikicode]

Les paramètres définissant un cylindre sont les suivants (même ordre que les constructeurs de la classe) :

double radius
(Optionnel) Rayon du cylindre. La valeur par défaut est 1.0.
double height
(Optionnel) Hauteur du cylindre. La valeur par défaut est 2.0.
int divisions
(Optionnel) Nombre de divisions de la circonférence. La valeur par défaut est 64.

L'orientation initiale du cylindre suit l'axe Y ; les faces circulaires sont donc perpendiculaire à l'axe Y.

Cylinder cyl = new Cylinder(40, 220);
cyl.setTranslateY(220);

Boîte (classe Box)

[modifier | modifier le wikicode]

Les paramètres définissant une boîte sont les suivants (même ordre que les constructeurs de la classe) :

double width
(Optionnel) Largeur de la boîte (axe X). La valeur par défaut est 2.0.
double height
(Optionnel) Hauteur de la boîte (axe Y). La valeur par défaut est 2.0.
double depth
(Optionnel) Profondeur de la boîte (axe Z). La valeur par défaut est 2.0.
Box box = new Box(100, 200, 100);
box.setTranslateX(250);
box.setTranslateY(220);

Forme triangulée

[modifier | modifier le wikicode]

Une forme triangulée est définie avec un objet de class TriangleMesh. Le seul constructeur à appeler n'a aucun paramètres. Il faut ensuite remplir les listes de points, de faces triangulaires et les points de texture.

Méthodes de la classe TriangleMesh:

getTexCoords()
Retourne la liste modifiable (float) des points (U,V) pour les textures.
getPoints()
Retourne la liste modifiable (float) de tous les sommets.
getFaces()
Retourne la liste modifiable (int) des index des sommets de chaque face.
getFaceSmoothingGroups()
Retourne la liste modifiable (int) des identifiants de face. La même valeur signifiant qu'il s'agit de la même face. Ceci est utilisé pour le rendu continu des faces.
TriangleMesh pyramid_mesh = new TriangleMesh();

// Pas de texture dans cet exemple, mais un point est nécessaire => (0,0)
pyramid_mesh.getTexCoords().addAll(0, 0); // --> T[]

float h = 150;                    // Height
float s = 300;                    // Side

pyramid_mesh.getPoints().addAll( // --> P[]
//    X   Y    Z
      0,  0,   0,   // Point 0 - Top
      0,  h, -s/2,  // Point 1 - Front
    -s/2, h,   0,   // Point 2 - Left
      0,  h,  s/2,  // Point 3 - Back
     s/2, h,   0 ); // Point 4 - Right

pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
//  P,T   P,T   P,T
    0,2,  2,4,  1,3,          // Front left face
    0,2,  1,4,  4,3,          // Front right face
    0,2,  4,4,  3,3,          // Back right face
    0,2,  3,4,  2,3,          // Back left face
    3,2,  1,3,  2,0,          // Bottom rear face
    3,2,  4,1,  1,4   );      // Bottom front face

// Regrouper les deux derniers triangles constituant la base carrée de la pyramide
pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);

Il faut ensuite encapsuler la triangulation dans une forme :

MeshView pyramid = new MeshView(pyramid_mesh);
pyramid.setDrawMode(DrawMode.FILL);
pyramid.setTranslateY(-150-h);

Sources de lumière

[modifier | modifier le wikicode]

Par défaut, la scène n'a aucune source lumineuse. Le rendu serait donc de la couleur de fond de la scène.

Une source lumineuse a une couleur filtrant celle des objets, une intensité, et une position.

JavaFX a deux types de sources lumineuses :

Lumière ambiante (AmbientLight)
Une source lumineuse sans position, se diffusant sur toute la scène avec la même intensité.
Lumière ponctuelle (PointLight)
Une source lumineuse avec position, dont la luminosité décroit avec l'augmentation de la distance.

Il faut également définir pour chaque source lumineuse, la liste des objets éclairés par celle-ci.

Node[] allnodes = { sphere, cyl, pyramid, box };

Color light_color = Color.LIGHTGREY;

AmbientLight light = new AmbientLight(light_color);
light.setTranslateX(-180);
light.setTranslateY(-90);
light.setTranslateZ(-120);
light.getScope().addAll(allnodes);

PointLight light2 = new PointLight(light_color);
light2.setTranslateX(+180);
light2.setTranslateY(+190);
light2.setTranslateZ(+180);
light2.getScope().addAll(allnodes);

La caméra (classe Camera) représente le point de vue d'affichage de la scène : position et orientation. Pour une représentation en 3D, la sous-classe PerspectiveCamera est utilisée.

setNearClip(float)
Définit la distance minimale pour le rendu des objets.
setFarClip(float)
Définit la distance maximale pour le rendu des objets.
getTransforms()
Liste modifiable des transformations (translation, rotation).
// Rotation et relocalisation de la caméra
Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
Translate translate = new Translate(0, 0, -1000);

PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setNearClip(0.1);    // Afficher pour une distance entre 0.1
camera.setFarClip(10000.0); // ... et 10000
camera.getTransforms().addAll(rotate_x, rotate_y, translate);

Construction de la scène

[modifier | modifier le wikicode]

La scène est construite en spécifiant le nœud racine regroupant les éléments de la scène.

Il ne manque que la scène constituée des éléments vus précédemment.

Group root = new Group(allnodes);
root.getChildren().addAll(light, light2);
Scene scene = new Scene(root, 600, 600, true); // true to use depth buffer
scene.setFill(Color.BLACK); // Couleur de fond par défaut
scene.setCamera(camera);

// Affichage de la scène dans la fenêtre de l'application
stage.setTitle("3D JavaFX");
stage.setScene(scene);
stage.show();
package org.wikibooks.fr.javafx;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.*;

public class ExempleFormes3D extends Application
{
    // Transformations pour la caméra
    private final Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
    private final Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
    private final Translate translate = new Translate(0, 0, -1000);

    @Override
    public void start(Stage stage)
    {
        // Coordonnées de base
        int dx = 0, dy = 0, dz = 0;

        // Sphère
        Sphere sphere = new Sphere(100);
        sphere.setTranslateX(dx);
        sphere.setTranslateY(dy);
        sphere.setTranslateZ(dz);
        sphere.setCursor(Cursor.OPEN_HAND);

        // Boîte
        Box box = new Box(100, 200, 100);
        box.setTranslateX(dx + 250);
        box.setTranslateY(dy + 220);
        box.setTranslateZ(dz);
        box.setCursor(Cursor.OPEN_HAND);

        // Cylindre
        Cylinder cyl = new Cylinder(40, 220);
        cyl.setTranslateX(dx);
        cyl.setTranslateY(dy + 220);
        cyl.setTranslateZ(dz);
        cyl.setCursor(Cursor.OPEN_HAND);

        // Pyramide à base carrée : par triangulation
        TriangleMesh pyramid_mesh = new TriangleMesh(); // VertexFormat.POINT_TEXCOORD => P,T

        pyramid_mesh.getTexCoords().addAll(0, 0); // --> T[]
        pyramid_mesh.getTexCoords().addAll(1.0F, 0); // --> T[]
        pyramid_mesh.getTexCoords().addAll(0.515F, 0.02F); // --> T[]
        pyramid_mesh.getTexCoords().addAll(1.0F, 1.0F); // --> T[]
        pyramid_mesh.getTexCoords().addAll(0, 1.0F); // --> T[]

        float h = 150;                // Height
        float s = 300;                // Side
        pyramid_mesh.getPoints().addAll( // --> P[]
        //    X   Y    Z
              0,  0,   0,   // Point 0 - Top
              0,  h, -s/2,  // Point 1 - Front
            -s/2, h,   0,   // Point 2 - Left
              0,  h,  s/2,  // Point 3 - Back
             s/2, h,   0 ); // Point 4 - Right
        pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
        //  P,T   P,T   P,T
            0,2,  2,4,  1,3,          // Front left face
            0,2,  1,4,  4,3,          // Front right face
            0,2,  4,4,  3,3,          // Back right face
            0,2,  3,4,  2,3,          // Back left face
            3,2,  1,3,  2,0,          // Bottom rear face
            3,2,  4,1,  1,4   );      // Bottom front face
        pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);

        MeshView pyramid = new MeshView(pyramid_mesh);
        pyramid.setDrawMode(DrawMode.FILL);

        pyramid.setTranslateX(dx);
        pyramid.setTranslateY(dy-150-h);
        pyramid.setTranslateZ(dz);
        pyramid.setEffect(new Shadow());

        // Tous les éléments à éclairer
        Node[] allnodes = { sphere, cyl, pyramid, box };

        // Sources lumineuses
        Color light_color = Color.LIGHTGREY;

        AmbientLight light = new AmbientLight(light_color);
        light.setTranslateX(dx - 180);
        light.setTranslateY(dy - 90);
        light.setTranslateZ(dz - 120);
        light.getScope().addAll(allnodes);

        PointLight light2 = new PointLight(light_color);
        light2.setTranslateX(dx + 180);
        light2.setTranslateY(dy + 190);
        light2.setTranslateZ(dz + 180);
        light2.getScope().addAll(allnodes);

        // Nœud racine regroupant tous les éléments
        Group root = new Group(allnodes);
        root.getChildren().addAll(light, light2); // et les sources lumineuses

        Scene scene = new Scene(root, 600, 600, true); // true to use depth buffer
        scene.setFill(Color.BLACK);

        // Point de vue de la scène
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setNearClip(0.1);
        camera.setFarClip(10000.0);
        camera.getTransforms().addAll(rotate_x, rotate_y, translate);
        scene.setCamera(camera);

        stage.setTitle("3D JavaFX");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args)
    {
        Application.launch(args);
    }
}

L'apparence des objets sera améliorée dans le chapitre suivant.


Apparence 3D

L'apparence par défaut des objets de la scène construite au chapitre précédent est plutôt monotone :

Le but de ce chapitre est de montrer comment améliorer l'apparence des objets en leur donnant une couleur ou une texture.

L'apparence d'un objet (couleur, reflets, texture, ...) est défini par une instance de la classe abstraite Material. JavaFX fournit une classe concrète nommée PhongMaterial, nommée d'après le modèle d'ombrage de Phong.

Les méthodes de la classe PhongMaterial sont les suivantes :

setDiffuseColor(Color)
Définit la couleur principale.
setSpecularColor(Color)
Définit la couleur des reflets des sources lumineuses.
setSpecularPower(float)
Définit la puissance de réflexion.
PhongMaterial mat_blue = new PhongMaterial();
mat_blue.setDiffuseColor(Color.BLUE);
mat_blue.setSpecularColor(Color.LIGHTBLUE);
mat_blue.setSpecularPower(10.0);

On peut ensuite assigner l'apparence aux objets :

sphere.setMaterial(mat_blue);

Une texture est définie par une image pouvant définir la couleur de diffusion, de réflexion, d'illumination de l'objet.

Les méthodes de la classe PhongMaterial concernant les textures sont les suivantes :

setDiffuseMap(Image)
Définit la texture pour la couleur de diffusion.
setSpecularMap(Image)
Définit la texture pour la couleur de reflet.
setSelfIlluminationMap(Image)
Définit la texture pour l'intensité d'illumination.
setBumpMap(Image)
Définit la texture pour la granularité de l'objet.
La texture utilisée

L'exemple ci-dessous utilise l'image Red-brick-wall-texture-1.jpg disponible sur commons.

PhongMaterial mat_wall = new PhongMaterial();
//Image tex_im = new Image("file://///E:/Image/wall.png"); // Chemin local
Image tex_im = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Red-brick-wall-texture-1.jpg/640px-Red-brick-wall-texture-1.jpg");
PhongMaterial mat_wall = new PhongMaterial();
mat_wall.setDiffuseColor(Color.GRAY);
mat_wall.setSpecularColor(Color.WHITE);
mat_wall.setSpecularPower(8.0);
mat_wall.setDiffuseMap(tex_im);
Formes 3D avec couleurs et textures
package org.wikibooks.fr.javafx;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.*;

public class TestApparence3D extends Application
{
    private final Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
    private final Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
    private final Translate translate = new Translate(0, 0, -1000);

    @Override
    public void start(Stage stage)
    {
        Image tex_im = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Red-brick-wall-texture-1.jpg/640px-Red-brick-wall-texture-1.jpg");
        PhongMaterial mat_wall = new PhongMaterial();
        mat_wall.setDiffuseColor(Color.GRAY);
        mat_wall.setSpecularColor(Color.WHITE);
        mat_wall.setSpecularPower(8.0);
        mat_wall.setDiffuseMap(tex_im);

        PhongMaterial mat_blue = new PhongMaterial();
        mat_blue.setDiffuseColor(Color.BLUE);
        mat_blue.setSpecularColor(Color.LIGHTBLUE);
        mat_blue.setSpecularPower(10.0);

        PhongMaterial mat_red = new PhongMaterial();
        mat_red.setDiffuseColor(Color.RED);
        mat_red.setSpecularColor(Color.ORANGERED);
        mat_red.setSpecularPower(10.0);

        int dx = 0, dy = 0, dz = 0;
        Sphere sphere = new Sphere(100);
        sphere.setTranslateX(dx);
        sphere.setTranslateY(dy);
        sphere.setTranslateZ(dz);
        sphere.setCursor(Cursor.OPEN_HAND);
        sphere.setMaterial(mat_blue);

        Box box = new Box(100, 200, 100);
        box.setTranslateX(dx + 250);
        box.setTranslateY(dy + 220);
        box.setTranslateZ(dz);
        box.setCursor(Cursor.OPEN_HAND);
        box.setMaterial(mat_wall);

        Cylinder cyl = new Cylinder(40, 220);
        cyl.setTranslateX(dx);
        cyl.setTranslateY(dy + 220);
        cyl.setTranslateZ(dz);
        cyl.setCursor(Cursor.OPEN_HAND);
        cyl.setMaterial(mat_red);

        TriangleMesh pyramid_mesh = new TriangleMesh(); // VertexFormat.POINT_TEXCOORD => P,T

        // Texture : coordonnées indépendantes de la taille de la texture
        // 0.5 = 50%, 1.0 = 100%, 2.0 = 200% (texture répétée 2 fois), ...
        pyramid_mesh.getTexCoords().addAll( 0   , 0    ); // --> T[0]
        pyramid_mesh.getTexCoords().addAll( 1.0F, 0    ); // --> T[1]
        pyramid_mesh.getTexCoords().addAll( 0.5F, 0.0F ); // --> T[2]
        pyramid_mesh.getTexCoords().addAll( 1.0F, 1.0F ); // --> T[3]
        pyramid_mesh.getTexCoords().addAll( 0   , 1.0F ); // --> T[4]

        float h = 150;                    // Height
        float s = 300;                    // Side
        pyramid_mesh.getPoints().addAll( // --> P[]
        //    X   Y    Z
              0,  0,   0,   // Point 0 - Top
              0,  h, -s/2,  // Point 1 - Front
            -s/2, h,   0,   // Point 2 - Left
              0,  h,  s/2,  // Point 3 - Back
             s/2, h,   0 ); // Point 4 - Right
        pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
        //  P,T   P,T   P,T
            0,2,  2,4,  1,3,          // Front left face
            0,2,  1,4,  4,3,          // Front right face
            0,2,  4,4,  3,3,          // Back right face
            0,2,  3,4,  2,3,          // Back left face
            3,2,  1,3,  2,0,          // Bottom rear face
            3,2,  4,1,  1,4   );      // Bottom front face
        pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);

        MeshView pyramid = new MeshView(pyramid_mesh);
        pyramid.setDrawMode(DrawMode.FILL);

        pyramid.setMaterial(mat_wall);
        pyramid.setTranslateX(dx);
        pyramid.setTranslateY(dy-150-h);
        pyramid.setTranslateZ(dz);

        Node[] allnodes = { sphere, cyl, pyramid, box };
        Color light_color = Color.LIGHTGREY;
        AmbientLight light = new AmbientLight(light_color);
        light.setTranslateX(dx - 180);
        light.setTranslateY(dy - 90);
        light.setTranslateZ(dz - 120);
        light.getScope().addAll(allnodes);

        PointLight light2 = new PointLight(light_color);
        light2.setTranslateX(dx + 180);
        light2.setTranslateY(dy + 190);
        light2.setTranslateZ(dz + 180);
        light2.getScope().addAll(allnodes);

        Group root = new Group(allnodes);

        root.getChildren().addAll(light, light2);

        Scene scene = new Scene(root, 600, 600, true);

        scene.setFill(Color.BLACK);
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setNearClip(0.1);
        camera.setFarClip(10000.0);
        camera.getTransforms().addAll(rotate_x, rotate_y, translate);
        scene.setCamera(camera);

        stage.setTitle("3D JavaFX");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args)
    {
        Application.launch(args);
    }
}

La texture de la base carrée fait apparaître les deux triangles car les 2 faces adjacentes sont associées à deux triangles qui ne sont pas adjacent dans la texture :

Formes 3D avec couleurs et textures

En ajustant la texture comme dans le code ci-dessous, on obtient une texture uniforme pour la base carrée :

pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
//  P,T   P,T   P,T
    0,2,  2,4,  1,3,          // Front left face
    0,2,  1,4,  4,3,          // Front right face
    0,2,  4,4,  3,3,          // Back right face
    0,2,  3,4,  2,3,          // Back left face
    3,1,  1,4,  2,0,          // Bottom rear face
    3,1,  4,3,  1,4   );      // Bottom front face

Formes 3D avec couleurs et textures ajustées

Dans cette version, la caméra est fixe. Le chapitre suivant résout le problème en ajoutant la possibilité d'interagir avec la souris pour déplacer la caméra.


Interactions 3D

Ce chapitre explique comment ajouter de l'interaction à une scène 3D.

Le principe est le même avec les composants 2D d'une interface graphique : ajouter un listener à notifier de certains évènements.

Rotations et déplacements

[modifier | modifier le wikicode]

Le code des exemples précédents contient déjà les transformations appliquées à la caméra :

private final Rotate rotate_x = new Rotate(0, Rotate.X_AXIS); // axe horizontal
private final Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS); // axe vertical
private final Translate translate = new Translate(0, 0, -1000);

Il suffit d'ajouter des listeners pour modifier les paramètres de ces transformations selon les déplacements de la souris quand le bouton gauche est maintenu appuyé. La modification dépend des touches Shift et Ctrl appuyées lors du déplacement de la souris :

  • Si la touche Ctrl est appuyée; la caméra est déplacée dans le plan XY ;
  • Si la touche Shift est appuyée; la caméra est déplacée dans le plan ZY ;
  • Sinon, l'angle de rotation autour de l'axe horizontal est contrôlé par les mouvements verticaux de la souris et vice-versa.

Les listeners sont ajoutés à la scène.

scene.setOnMousePressed((MouseEvent me) -> {
    mouse_x = me.getSceneX();
    mouse_y = me.getSceneY();
    mouse_x_prev = mouse_x;
    mouse_y_prev = mouse_y;
});
scene.setOnMouseDragged((MouseEvent me) -> {
    mouse_x = me.getSceneX();
    mouse_y = me.getSceneY();
    if (me.isControlDown())
    {
        translate.setX(translate.getX() + (mouse_x - mouse_x_prev));
        translate.setY(translate.getY() + (mouse_y - mouse_y_prev));
    }
    else if (me.isShiftDown())
    {
        translate.setZ(translate.getZ() + (mouse_x - mouse_x_prev));
        translate.setY(translate.getY() + (mouse_y - mouse_y_prev));
    }
    else
    {
        rotate_x.setAngle(rotate_x.getAngle() - (mouse_y - mouse_y_prev));
        rotate_y.setAngle(rotate_y.getAngle() + (mouse_x - mouse_x_prev));
    }
    mouse_x_prev = mouse_x;
    mouse_y_prev = mouse_y;
});

La rotation est centrée sur la sphère, c'est à dire en (0,0,0), quel que soit la position de la caméra car il s'agit des deux premières transformations ajoutées à la caméra, avant le déplacement :

camera.getTransforms().addAll(rotate_x, rotate_y, translate);
package org.wikibooks.fr.javafx;

import javafx.application.*;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.transform.*;
import javafx.stage.*;

public class TestCameraMove extends Application
{
    private double mouse_x;
    private double mouse_y;
    private double mouse_x_prev;
    private double mouse_y_prev;

    private final Rotate rotate_x = new Rotate(0, Rotate.X_AXIS);
    private final Rotate rotate_y = new Rotate(0, Rotate.Y_AXIS);
    private final Translate translate = new Translate(0, 0, -1000);

    @Override
    public void start(Stage stage)
    {
        Image tex_im = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Red-brick-wall-texture-1.jpg/640px-Red-brick-wall-texture-1.jpg");
        PhongMaterial mat_wall = new PhongMaterial();
        mat_wall.setDiffuseColor(Color.GRAY);
        mat_wall.setSpecularColor(Color.WHITE);
        mat_wall.setSpecularPower(8.0);
        mat_wall.setDiffuseMap(tex_im);

        PhongMaterial mat_blue = new PhongMaterial();
        mat_blue.setDiffuseColor(Color.BLUE);
        mat_blue.setSpecularColor(Color.LIGHTBLUE);
        mat_blue.setSpecularPower(10.0);

        PhongMaterial mat_red = new PhongMaterial();
        mat_red.setDiffuseColor(Color.RED);
        mat_red.setSpecularColor(Color.ORANGERED);
        mat_red.setSpecularPower(10.0);

        int dx = 0, dy = 0, dz = 0;
        Sphere sphere = new Sphere(100);
        sphere.setTranslateX(dx);
        sphere.setTranslateY(dy);
        sphere.setTranslateZ(dz);
        sphere.setCursor(Cursor.OPEN_HAND);
        sphere.setMaterial(mat_blue);

        Box box = new Box(100, 200, 100);
        box.setTranslateX(dx + 250);
        box.setTranslateY(dy + 220);
        box.setTranslateZ(dz);
        box.setCursor(Cursor.OPEN_HAND);
        box.setMaterial(mat_wall);

        Cylinder cyl = new Cylinder(40, 220);
        cyl.setTranslateX(dx);
        cyl.setTranslateY(dy + 220);
        cyl.setTranslateZ(dz);
        cyl.setCursor(Cursor.OPEN_HAND);
        cyl.setMaterial(mat_red);

        TriangleMesh pyramid_mesh = new TriangleMesh(); // VertexFormat.POINT_TEXCOORD => P,T

        // Texture : coordonées indépendantes de la taille de la texture
        // 0.5 = 50%, 1.0 = 100%, 2.0 = 200% (texture répétée 2 fois), ...
        pyramid_mesh.getTexCoords().addAll(0, 0); // --> T[]
        pyramid_mesh.getTexCoords().addAll(2.0F, 0); // --> T[]
        pyramid_mesh.getTexCoords().addAll(1.0F, 0.0F); // --> T[]
        pyramid_mesh.getTexCoords().addAll(2.0F, 2.0F); // --> T[]
        pyramid_mesh.getTexCoords().addAll(0, 2.0F); // --> T[]

        float h = 150;                    // Height
        float s = 300;                    // Side
        pyramid_mesh.getPoints().addAll( // --> P[]
        //    X   Y    Z
              0,  0,   0,   // Point 0 - Top
              0,  h, -s/2,  // Point 1 - Front
            -s/2, h,   0,   // Point 2 - Left
              0,  h,  s/2,  // Point 3 - Back
             s/2, h,   0 ); // Point 4 - Right
        pyramid_mesh.getFaces().addAll( // --> F[] = index in P[],T[]
        //  P,T   P,T   P,T
            0,2,  2,4,  1,3,          // Front left face
            0,2,  1,4,  4,3,          // Front right face
            0,2,  4,4,  3,3,          // Back right face
            0,2,  3,4,  2,3,          // Back left face
            3,1,  1,4,  2,0,          // Bottom rear face
            3,1,  4,3,  1,4   );      // Bottom front face
        pyramid_mesh.getFaceSmoothingGroups().addAll(0,1,2,3,4,4);

        MeshView pyramid = new MeshView(pyramid_mesh);
        pyramid.setDrawMode(DrawMode.FILL);

        pyramid.setMaterial(mat_wall);
        pyramid.setTranslateX(dx);
        pyramid.setTranslateY(dy-150-h);
        pyramid.setTranslateZ(dz);

        Node[] allnodes = { sphere, cyl, pyramid, box };
        Color light_color = Color.LIGHTGREY;
        AmbientLight light = new AmbientLight(light_color);
        light.setTranslateX(dx - 180);
        light.setTranslateY(dy - 90);
        light.setTranslateZ(dz - 120);
        light.getScope().addAll(allnodes);

        PointLight light2 = new PointLight(light_color);
        light2.setTranslateX(dx + 180);
        light2.setTranslateY(dy + 190);
        light2.setTranslateZ(dz + 180);
        light2.getScope().addAll(allnodes);

        Group root = new Group(allnodes);

        root.getChildren().addAll(light, light2);

        Scene scene = new Scene(root, 600, 600, true);

        scene.setFill(Color.BLACK);
        PerspectiveCamera camera = new PerspectiveCamera(true);
        camera.setNearClip(0.1);
        camera.setFarClip(10000.0);
        camera.getTransforms().addAll(rotate_x, rotate_y, translate);
        scene.setCamera(camera);

        scene.setOnMousePressed((MouseEvent me) -> {
            mouse_x = me.getSceneX();
            mouse_y = me.getSceneY();
            mouse_x_prev = mouse_x;
            mouse_y_prev = mouse_y;
        });
        scene.setOnMouseDragged((MouseEvent me) -> {
            mouse_x = me.getSceneX();
            mouse_y = me.getSceneY();
            if (me.isControlDown())
            {
                translate.setX(translate.getX() + (mouse_x - mouse_x_prev));
                translate.setY(translate.getY() + (mouse_y - mouse_y_prev));
            }
            else if (me.isShiftDown())
            {
                translate.setZ(translate.getZ() + (mouse_x - mouse_x_prev));
                translate.setY(translate.getY() + (mouse_y - mouse_y_prev));
            }
            else
            {
                rotate_x.setAngle(rotate_x.getAngle() - (mouse_y - mouse_y_prev));
                rotate_y.setAngle(rotate_y.getAngle() + (mouse_x - mouse_x_prev));
            }
            mouse_x_prev = mouse_x;
            mouse_y_prev = mouse_y;
        });

        stage.setTitle("3D JavaFX");
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args)
    {
        Application.launch(args);
    }
}


Jouer une vidéo ou un son

JavaFX permet de jouer une vidéo ou un son en utilisant les classes du package javafx.scene.media.

Jouer un fichier

[modifier | modifier le wikicode]

Le code d'exemple ci-dessous montre comment jouer une vidéo ou un son à partir d'un fichier.

  • Si le fichier est une vidéo, elle est affichée dans la scène. Initialement en mode plein écran, la taille de la vidéo s'adapte à celle de la scène par binding des propriétés width et height.
  • Si le fichier est un son, le son est audible et la scène affiche la couleur de fond.
package org.wikibooks.fr;

import java.io.*;

import javafx.application.*;
import javafx.beans.binding.*;
import javafx.beans.property.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.media.*;
import javafx.scene.paint.*;
import javafx.stage.*;

public class MediaApplication extends Application
{
	public static void main(String[] args)
	{
		Application.launch(args);
	}

	protected MediaPlayer media_player;

	@Override
	public void start(Stage stage) throws Exception
	{
		// .mp3 / .m4a / .mpeg / ...
		String path = "/chemin/du/fichier.mp3";
		Media media = new Media(new File(path).toURI().toString());
		media_player = new MediaPlayer(media);
		MediaView viewer = new MediaView(media_player);

		// Ajuster automatiquement la vue vidéo à la taille de la scène :
		DoubleProperty width = viewer.fitWidthProperty();
		DoubleProperty height = viewer.fitHeightProperty();
		width.bind(Bindings.selectDouble(viewer.sceneProperty(), "width"));
		height.bind(Bindings.selectDouble(viewer.sceneProperty(), "height"));
		viewer.setPreserveRatio(true);

		StackPane root = new StackPane();
		root.getChildren().add(viewer);

		// Création de la scène :
		Scene scene = new Scene(root, 500, 500, Color.BLACK);
		stage.setScene(scene);
		stage.setTitle("Vidéo");
		stage.setFullScreen(true);
		stage.show();   
		media_player.play();
	}
}

Comme le fichier est joué de manière asynchrone en tâche de fond, la référence au MediaPlayer doit être conservée pour que le garbage collector ne supprime pas le media player, sinon cela provoquerait un arrêt du fichier au bout d'un moment aléatoire. Dans le code précédent, la référence au MediaPlayer est conservée par l'objet viewer. Il est possible de se passer d'objet viewer (instance de la classe MediaView), pour jouer le son seulement, auquel cas il faut conserver la référence au MediaPlayer dans la classe.


Scene Builder

Scene Builder est un outil interactif de conception d'interface graphique pour JavaFX. Il permet de créer des interfaces utilisateurs rapidement et sans avoir besoin de coder ; il en résulte des fichiers au format FXML qui sont ensuite chargés par le programme pour afficher une interface graphique à l'utilisateur.

Développé initialement par Oracle et sous le nom JavaFX Scene Builder, son code source a été publié en open source à partir de sa version 2.0.

Depuis, le logiciel est principalement développé et soutenu par la société Gluon.

Le logiciel s'utilise avec la technique du glisser/déposer : un panneau latéral situé à gauche de la fenêtre permet à l'utilisateur de sélectionner et positionner un composant dans la hiérarchie des composants ou sur la vue centrale affichant le rendu.

Un panneau latéral positionné à droite de la fenêtre permet quant à lui de définir les caractéristiques du composant sélectionné par l'utilisateur.

Les interfaces sont enregistrées dans des fichiers au format FXML, qui sont ensuite lus et chargés en mémoire par le programme, puis affichés à l'écran.

FXML est un format de données textuel, dérivé du format XML, qui permet de décrire une interface utilisateur pour des applications conçues avec JavaFX.

Il s'agit d'une alternative à la conception d'interfaces réalisée par l'écriture de lignes de codes, en découplant l'interface graphique du code source qui le contrôle.

Cette technologie a été intégrée dans JavaFX à partir de sa version 2.0, pour remplacer JavaFX Script.

GFDL GFDL Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture.