FAQ JavaFXConsultez toutes les FAQ

Nombre d'auteurs : 4, nombre de questions : 507, dernière mise à jour : 2 novembre 2016  Ajouter une question

 

Cette FAQ a été réalisée à partir des questions fréquemment posées sur le forum JavaFX de http://java.developpez.com ainsi que l'expérience personnelle des auteurs.

Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes. Les auteurs font leur maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur, ou que vous souhaitez nous aider en devenant rédacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.


SommaireSceneGraphWeb (10)
précédent sommaire suivant
 

Pour tester si le rendu des pages web est supporté par votre plateforme, vous pouvez invoquer la méthode isSupported() de la classe javafx.application.Platform en lui passant en paramètre la valeur javafx.application.ConditionalFeature.WEB :

Code Java : Sélectionner tout
Platform.isSupported(ConditionalFeature.WEB);

À l'heure actuelle (JDK8_u20), le rendu des pages web n'est pas disponible sur la version ARM du JDK destinée aux plateformes embarquées (ex. : Raspberry Pi, etc.).

Mis à jour le 16 septembre 2014 bouye

Pour afficher une page web, vous devez inclure dans votre scène un nœud graphique de type javafx.scene.web.WebView. Ce nœud est un nœud graphique comme un autre, il peut donc être déplacé, modifié et subir des effets ou transformations comme n'importe quel autre nœud SceneGraph.

Par exemple :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main extends Application { 
  
    @Override 
    public void start(Stage primaryStage) { 
        final WebView webView = new WebView(); 
        final Scene scene = new Scene(webView, 350, 300); 
        primaryStage.setTitle("Test de WebView"); 
        primaryStage.setScene(scene); 
        primaryStage.show(); 
    } 
  
    public static void main(String[] args) { 
        launch(args); 
    } 
}

Ici, bien sûr l'affichage reste vierge puisque nous n'avons chargé aucune page web.

WebView est en fait une vue sur un objet de type javafx.scene.web.WebEngine, un moteur de page web, qui se chargera d'afficher une page web, de maintenir et manipuler son DOM, d’interpréter du code JavaScript ou encore de permettre la communication dans les deux sens entre Java et JavaScript. WebEngine est basé sur WebKit.

Mis à jour le 15 mars 2015 bouye

Il est possible de récupérer une référence sur le WebEngine en invoquant la méthode getEngine() de la classe WebView.

Par exemple :

Code Java : Sélectionner tout
final WebEngine webEngine = webView.getEngine();

Mis à jour le 16 septembre 2014 bouye

Il suffit d'invoquer la méthode load() de la classe WebEngine pour charger une URL de la page web.

La page web peut provenir de plusieurs sources :

  • la page web peut être empaquetée dans une bibliothèque ou une application et récupérée via le mécanisme des ressources ou des ClassLoader.

    Code Java : Sélectionner tout
    1
    2
    final URL url = getClass().getResource("page.html"); 
    webView.getEngine().load(url.toExternalForm());

    Dans ce cas, la page et toutes ses ressources locales (images, sons, bibliothèques JavaScript, etc. spécifiées par des chemins d’accès relatifs) seront chargées directement depuis les fichiers JAR ;
  • la page web peut être sur un disque local et peut être référencées par les classes File, Path, URI ou URL.

    Code Java : Sélectionner tout
    1
    2
    final String pageURI= new File("page.html").toURI().toString(); 
    webView.getEngine().load(pageURI);

    Dans ce cas, la page et toutes ses ressources locales (images, sons, bibliothèques JavaScript, etc. spécifiées par des chemins d’accès relatifs) seront chargées directement depuis le répertoire parent sur le disque ;
  • la page web peut être hébergée sur un site web distant et on peut y accéder par une String ou une URL contenant son chemin d’accès.

    Code Java : Sélectionner tout
    webView.getEngine().load("http://www.developpez.com");

    Cette instruction chargera la page de garde du site de Développez dans la vue.



    Dans ce cas, la page et toutes ses ressources locales (images, sons, bibliothèques JavaScript, etc. spécifiées par des chemins d’accès relatifs) seront chargées directement depuis le site d’hébergement distant.


Le chargement de la page est asynchrone : la méthode retourne immédiatement et la page sera chargée ultérieurement en fonction de son poids et de la vitesse de connexion ainsi que de sa complexité.

Mis à jour le 16 septembre 2014 bouye

Pour savoir si une page est complètement chargée, ou même si son chargement échoue, il suffit d'observer le changement d’état de la tâche qui effectue le chargement. Le WebEngine dispose en effet de la propriété loadWorker qui permet d’accéder à la tâche qui gère le chargement de la page. Cette tâche dispose d'une propriété state dont la valeur évolue avec le temps.

Par exemple, si avant de charger notre page web nous faisons :

Code Java : Sélectionner tout
1
2
3
webView.getEngine().getLoadWorker().stateProperty().addListener((ObservableValue<? extends Worker.State> observableValue, Worker.State oldValue, Worker.State newValue) -> { 
    System.out.printf("%s -> %s", oldValue, newValue).println(); 
});

Nous pouvons désormais suivre l'avancement du chargement de la page.

Ainsi, en chargeant la page de garde du site de Développez, nous aurons une sortie similaire à :

Code : Sélectionner tout
1
2
3
READY -> SCHEDULED 
SCHEDULED -> RUNNING 
RUNNING -> SUCCEEDED

Mis à jour le 16 septembre 2014 bouye

Oui, il est possible d'afficher une page contenant du contenu au format HTML5 ou des bibliothèques tierces JavaScript (ex. : angular.js). Il faudra bien sûr vérifier que la bibliothèque fonctionne correctement quand elle est utilisée dans cet environnement.

Mis à jour le 1er octobre 2014 bouye

Non, il n'est pas possible d'afficher une page web contenant du flash ou n'importe quel type de technologies qui nécessiteraient un plugin externe dans le navigateur.

Mis à jour le 1er octobre 2014 bouye

Il est possible d'interagir avec le contenu de la page web depuis Java en invoquant la méthode executeScript() de la classe WebEngine. Cette méthode permet d’exécuter du code au format JavaScript et le cas échéant peut retourner son résultat.

Par exemple :

Code Java : Sélectionner tout
webEngine.executeScript("history.back()");

Cette méthode invoque la fonction back() de l'historique de navigation et chargera la page précédente.

Il est aussi possible de récupérer des variables contenues dans le document. Pour les types de base, la conversion sera automatique d'entre Java et JavaScript (integer deviendra Integer, string deviendra String, etc.).

Les objets JavaScript plus complexes seront empaquetés sous le type netscape.javascript.JSObject. Il est possible d'invoquer les méthodes habituelles de la nouvelle API LiveConnect introduite dans le JDK6 sur les objets de ce type.

Par exemple :

Code Java : Sélectionner tout
1
2
final JSObject history = (JSObject) webEngine.executeScript("history"); 
history.call("back");

Ce bout de code a un effet identique au précédent : nous demandons à l'historique de navigation de charger la page précédente. Cependant, ici, nous manipulons directement l'objet historique depuis le code Java.

Mis à jour le 16 septembre 2014 bouye

Il est tout à fait possible d’interagir depuis la page web avec notre programme écrit en Java.

Commençons par définir un formulaire simple qui définit deux champs de saisie, un pour le prénom et un autre pour le nom d'une personne.

Code HTML : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html> 
<html> 
    <head> 
        <title>Formulaire de saisie</title> 
        <meta charset="UTF-8"> 
        <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
    </head> 
    <body> 
        <form> 
            Prénom : <input type="text" name="firstname" onkeyup="java.setFirstName(this.value)"><br> 
            Nom : <input type="text" name="lastname" onkeyup="java.setLastName(this.value)"> 
        </form> 
    </body> 
</html>

Sur chacun de ces champs de saisie, nous avons défini un callback qui appelle, en JavaScript, une méthode sur un objet nommé « java » en passant en paramètre la valeur de notre champ. Or, pour le moment, notre document ne contient aucune variable nommée ainsi.

Nous allons nous en charger… depuis le code Java en passant, via le WebEngine, un objet, écrit en Java, qui sera attaché au document web sous le nom de « java ». Cet objet formera un pont entre les deux environnements et permettra ainsi le dialogue.

Prenons le code suivant :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Main extends Application { 
  
    public static class Pont { 
  
        public void setFirstName(String value) { 
            System.out.printf("Prénom : %s", value).println(); 
        } 
  
        public void setLastName(String value) { 
            System.out.printf("Nom : %s", value).println(); 
        } 
    } 
  
    // Cet objet sert de pont entre Java et JavaScript. 
    private final Pont pont = new Pont(); 
  
    @Override 
    public void start(Stage primaryStage) { 
        final WebView webView = new WebView(); 
        // Chargement du formulaire. 
        webView.getEngine().load(getClass().getResource("Form.html").toExternalForm()); 
        // On passe maintenant l'objet java au formulaire. 
        final JSObject jsobj = (JSObject) webView.getEngine().executeScript("window"); 
        jsobj.setMember("java", pont); 
        final Scene scene = new Scene(webView, 350, 300); 
        primaryStage.setTitle("Test de chargement de WebView"); 
        primaryStage.setScene(scene); 
        primaryStage.show(); 
    } 
  
    public static void main(String[] args) { 
        launch(args); 
    } 
}

Nous avons défini une nouvelle classe Pont disposant des méthodes qui seront appelées depuis le code JavaScript. Nous récupérons, via le WebEngine, un objet objet de type JSObject qui représente la « fenêtre web » du document. Nous attachons ensuite notre objet de type Pont sur cette référence en lui donnant le nom de variable « java ».

Désormais, notre formulaire web fonctionne comme prévu : lorsque l'utilisateur saisit un caractère dans un des deux champs, la méthode appropriée est invoquée dans l'objet de type Pont. Nous pouvons ainsi faire remonter des valeurs depuis notre page web vers notre code Java.

Veuillez noter qu'il est très important de conserver, dans votre code Java, une référence vers cet objet pont sous peine de voir le garbage collector le libérer de manière anticipée ce qui empêcherai toute communication ultérieure entre le code JavaScript et le code Java. En effet, le WebEngine ne conserve qu'une référence faible sur les objets qui lui sont passés et ce pour éviter des fuites mémoires.

Mis à jour le 2 novembre 2016 bouye

L'exemple suivant montre comment interfacer notre application avec un service en ligne, ici l'API Maps de Google qui permet d'interagir avec Google Maps. Vous pouvez bien sur l'adapter pour interface d'autres services Web tels que OpenStreetMap, Leaflet, etc.

Nous allons commencer par inclure dans notre projet un fichier Connector.html qui va afficher une carte Google Maps dans une page web et définir sur le document quelques fonctions écrites en JavaScript que nous allons pouvoir appeler depuis notre code Java :

Code HTML : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!DOCTYPE html> 
<html> 
    <head> 
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> 
        <style type="text/css"> 
            html { height: 100% } 
            body { height: 100%; margin: 0px; padding: 0px } 
            #map_canvas { height: 100%; background-color: #666970; } 
        </style> 
        <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> 
        <script type="text/javascript"> 
            function initialize() { 
                var latlng = new google.maps.LatLng(0, 0); 
                var myOptions = { 
                    zoom: 3, 
                    center: latlng, 
                    mapTypeId: google.maps.MapTypeId.ROADMAP, 
                    disableDoubleClickZoom: false, 
                    keyboardShortcuts: true, 
                    scrollwheel: true, 
                    draggable: true, 
                    disableDefaultUI: false, // Completly disable all controls. 
                    mapTypeControl: false, // Allow to change map type. 
                    overviewMapControl: false, // Small window of overview. 
                    panControl: false, // Disc used to pan the map. 
                    rotateControl: false, // Scale slider? 
                    navigationControl: false, // Scale slider? 
                    streetViewControl: false, // Place a streetview camera. 
                    scaleControl: false, // Scale slider? 
                    zoomControl: false, // Scale slider? 
                    backgroundColor: "#666970", 
                }; 
  
                document.geocoder = new google.maps.Geocoder(); 
                document.map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); 
  
                document.zoomIn = function zoomIn() { 
                    var zoomLevel = document.map.getZoom(); 
                    if (zoomLevel <= 20) 
                        document.map.setZoom(zoomLevel + 1); 
                } 
  
                document.zoomOut = function zoomOut() { 
                    var zoomLevel = document.map.getZoom(); 
                    if (zoomLevel > 0) 
                        document.map.setZoom(zoomLevel - 1); 
                } 
  
                document.setMapTypeRoad = function setMapTypeRoad() { 
                    document.map.setMapTypeId(google.maps.MapTypeId.ROADMAP); 
                } 
                document.setMapTypeSatellite = function setMapTypeSatellite() { 
                    document.map.setMapTypeId(google.maps.MapTypeId.SATELLITE); 
                } 
                document.setMapTypeHybrid = function setMapTypeHybrid() { 
                    document.map.setMapTypeId(google.maps.MapTypeId.HYBRID); 
                } 
                document.setMapTypeTerrain = function setMapTypeTerrain() { 
                    document.map.setMapTypeId(google.maps.MapTypeId.TERRAIN); 
                } 
  
                document.goToLocation = function goToLocation(searchString) { 
                    document.geocoder.geocode({'address': searchString}, function (results, status) { 
                        if (status == google.maps.GeocoderStatus.OK) { 
                            document.map.setCenter(results[0].geometry.location); 
                        } else { 
                            alert("Geocode was not successful for the following reason: " + status); 
                        } 
                    }); 
                } 
            } 
        </script> 
    </head> 
    <body onload="initialize()"> 
        <div id="map_canvas" style="width:100%; height:100%"></div> 
    </body> 
</html>

Nous pouvons tester que cette page se charge correctement dans un navigateur Web avant de passer à l’étape suivante

Nous allons maintenant créer une interface en JavaFX qui charge ce connecteur dans une WebView. Nous allons également ajouter des contrôles qui vont interagir avec le WebEngine pour invoquer les fonctions écrites en JavaScript. Le code parait long, mais c'est principalement à cause du montage de l'UI :

Code Java : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class Main extends Application { 
  
    @Override 
    public void start(Stage primaryStage) { 
        final WebView webView = new WebView(); 
        AnchorPane.setTopAnchor(webView, 0d); 
        AnchorPane.setLeftAnchor(webView, 0d); 
        AnchorPane.setBottomAnchor(webView, 0d); 
        AnchorPane.setRightAnchor(webView, 0d); 
        // Barre sur le haut de l'écran : affichage. 
        final ToggleGroup displayGroup = new ToggleGroup(); 
        final ToggleButton roadToggle = new ToggleButton("Route"); 
        roadToggle.setDisable(true); 
        roadToggle.setToggleGroup(displayGroup); 
        roadToggle.setSelected(true); 
        final ToggleButton satelliteToggle = new ToggleButton("Satellite"); 
        satelliteToggle.setDisable(true); 
        satelliteToggle.setToggleGroup(displayGroup); 
        final ToggleButton hybrideToggle = new ToggleButton("Hybride"); 
        hybrideToggle.setDisable(true); 
        hybrideToggle.setToggleGroup(displayGroup); 
        final ToggleButton terrainToggle = new ToggleButton("Terrain"); 
        terrainToggle.setDisable(true); 
        terrainToggle.setToggleGroup(displayGroup); 
        displayGroup.selectedToggleProperty().addListener((ObservableValue<? extends Toggle> observableValue, Toggle oldValue, Toggle newValue) -> { 
            String script = null; 
            if (newValue == roadToggle) { 
                script = "document.setMapTypeRoad()"; 
            } else if (newValue == satelliteToggle) { 
                script = "document.setMapTypeSatellite()"; 
            } else if (newValue == hybrideToggle) { 
                script = "document.setMapTypeHybrid()"; 
            } else if (newValue == terrainToggle) { 
                script = "document.setMapTypeTerrain()"; 
            } 
            if (script != null) { 
                webView.getEngine().executeScript(script); 
            } 
        }); 
        final HBox topControls = new HBox(roadToggle, satelliteToggle, hybrideToggle, terrainToggle); 
        AnchorPane.setTopAnchor(topControls, 6d); 
        AnchorPane.setLeftAnchor(topControls, 50d); 
        // Barre sur la gauche de l'écran : zoom. 
        final Button zoomInButton = new Button("+"); 
        zoomInButton.setDisable(true); 
        zoomInButton.setStyle("-fx-background-radius: 20, 19, 18, 17;"); 
        zoomInButton.setPrefSize(28, 28); 
        zoomInButton.setOnAction(actionEvent -> webView.getEngine().executeScript("document.zoomIn();")); 
        final Button zoomOutButton = new Button("-"); 
        zoomOutButton.setDisable(true); 
        zoomOutButton.setStyle("-fx-background-radius: 20, 19, 18, 17;"); 
        zoomOutButton.setPrefSize(28, 28); 
        zoomOutButton.setOnAction(actionEvent -> webView.getEngine().executeScript("document.zoomOut();")); 
        final VBox leftControls = new VBox(zoomInButton, zoomOutButton); 
        leftControls.setSpacing(6); 
        AnchorPane.setTopAnchor(leftControls, 50d); 
        AnchorPane.setLeftAnchor(leftControls, 6d); 
        // Barre en bas de l'écran : recherche. 
        final TextField searchField = new TextField(); 
        searchField.setDisable(true); 
        searchField.setEditable(false); 
        HBox.setHgrow(searchField, Priority.ALWAYS); 
        final Button searchButton = new Button("Chercher"); 
        searchButton.setDisable(true); 
        searchButton.setOnAction(actionEvent -> { 
            final String toSearch = searchField.getText(); 
            if (toSearch != null && !toSearch.trim().isEmpty()) { 
                webView.getEngine().executeScript(String.format("document.goToLocation('%s');", toSearch.trim())); 
            } 
        }); 
        final HBox bottomControls = new HBox(searchField, searchButton); 
        bottomControls.setSpacing(6); 
        AnchorPane.setLeftAnchor(bottomControls, 75d); 
        AnchorPane.setBottomAnchor(bottomControls, 16d); 
        AnchorPane.setRightAnchor(bottomControls, 6d); 
        // Assemblage. 
        final AnchorPane root = new AnchorPane(); 
        root.getChildren().setAll(webView, leftControls, topControls, bottomControls); 
        final Scene scene = new Scene(root, 350, 300); 
        primaryStage.setTitle("Test de Google Map"); 
        primaryStage.setScene(scene); 
        primaryStage.show(); 
        // Chargement de la page. 
        webView.getEngine().getLoadWorker().stateProperty().addListener((ObservableValue<? extends Worker.State> observableValue, Worker.State oldValue, Worker.State newValue) -> { 
            final boolean disabled = newValue != Worker.State.SUCCEEDED; 
            zoomInButton.setDisable(disabled); 
            zoomOutButton.setDisable(disabled); 
            roadToggle.setDisable(disabled); 
            satelliteToggle.setDisable(disabled); 
            hybrideToggle.setDisable(disabled); 
            terrainToggle.setDisable(disabled); 
            searchField.setEditable(!disabled); 
            searchField.setDisable(disabled); 
            searchButton.setDisable(disabled); 
        }); 
        webView.getEngine().load(getClass().getResource("Connector.html").toExternalForm()); 
    } 
  
    public static void main(String[] args) { 
        launch(args); 
    } 
}

Ce qui nous donne le résultat suivant :



Une fois la page complètement chargée, les contrôles seront activés. Quand vous cliquez sur le bouton chargé du zoom avant, par exemple, ce dernier demande au WebEngine d'exécuter la fonction JavaScript document.zoomIn(). Le contenu de la page web se met alors à jour avec le nouvel affichage.

Mis à jour le 15 mars 2015 bouye

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Responsables bénévoles de la rubrique Java : Mickael Baron - Robin56 -