Gustavo Muratalla Gustavo Muratalla - 5 months ago 19
Java Question

How to view a web page with FX:ID JavaFx

I was wondering I built a GUI java Scene Builder But I want it to Display a website in a VBOX I have. This runs perfectly fine no doubt about it. But there is something wrong some how it will not display the website in the FX:ID VBOX that i have made. I am just using google as an example. I am building a Game Launcher for a Game I have made and I do not like the way Swing or AWT looks like so I switched to JavaFx. I kinda now how to use it but I can figure out how to connect my VBOX with my code. Thank You for your help. This is my code so far

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class Main extends Application {

@FXML public VBox VBOX;

@Override
public void start(Stage primaryStage) throws Exception{
Parent main = FXMLLoader.load(getClass().getResource("launcher.fxml"));

primaryStage.setTitle("Dev Launcher");
primaryStage.setWidth(900);
primaryStage.setHeight(560);

Scene scene = new Scene(new Group());
VBOX = new VBox();
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
webEngine.load("http://google.com");

VBOX.getChildren().addAll(browser);
scene.setRoot(main);

primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}

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


Heres the FXML File

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.web.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefWidth="900.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<bottom>
<ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<items>
<GridPane alignment="CENTER" prefHeight="57.0" prefWidth="883.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="375.2518005371094" minWidth="10.0" prefWidth="154.2821502685547" />
<ColumnConstraints halignment="CENTER" hgrow="ALWAYS" maxWidth="550.5108489990234" minWidth="0.0" prefWidth="481.0280303955078" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="173.30714416503906" minWidth="10.0" prefWidth="154.2821502685547" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button mnemonicParsing="false" prefHeight="57.0" prefWidth="286.0" text="Play" GridPane.columnIndex="1" GridPane.halignment="CENTER">
<font>
<Font name="System Bold" size="13.0" />
</font>
</Button>
<Button mnemonicParsing="false" prefHeight="30.0" prefWidth="85.0" text="Website" GridPane.halignment="RIGHT" GridPane.valignment="CENTER" />
<Button mnemonicParsing="false" prefHeight="30.0" prefWidth="85.0" text="About" GridPane.valignment="CENTER" />
<Label text="Version: " GridPane.columnIndex="2" GridPane.halignment="LEFT" GridPane.hgrow="SOMETIMES" GridPane.valignment="CENTER" GridPane.vgrow="SOMETIMES" />
<Label prefWidth="120.0" text="0.0.1" GridPane.columnIndex="2" GridPane.halignment="RIGHT" GridPane.valignment="CENTER" GridPane.vgrow="SOMETIMES">
<GridPane.margin>
<Insets left="-100.0" />
</GridPane.margin>
</Label>
</children>
</GridPane>
</items>
</ToolBar>
</bottom>
<center>
<TabPane nodeOrientation="LEFT_TO_RIGHT" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
<tabs>
<Tab text="Update Notes">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<ScrollPane layoutX="1.0" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<content>
<AnchorPane prefHeight="450.0" prefWidth="862.0">
<children>
<VBox fx:id="VBOX" alignment="CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</content>
</ScrollPane>
</children>
</AnchorPane>
</content>
</Tab>
<Tab text="Game Log">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</center>
</BorderPane>


I don't have much for the Controller class:

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;

import java.io.IOException;

public class Controller {

@FXML private VBox VBOX;

}

Answer

All your application needs to do is maintain the application lifecycle (e.g. launch the application, execute the start method load up the initial scene into the stage, show the stage and stop the application as required). That is quite enough for it to do, other logic can be delegated to other classes in your program (such as Controllers). This delegation is eased by the fact that the controllers are things which an FXMLLoader creates and the FXMLLoader is smart enough to create the underlying nodes for the scene graph and wire them into the controller automatically.

So, you do not need to (and should not) set an object marked @FXML to a new value. E.g., declaring: @FXML public VBox VBOX;, then later calling: VBOX = new VBox(); is wrong.

You also should not make have @FXML fields in your Application as that makes no sense. @FXML only makes sense within a controller. The application isn't usually (and should not be) a controller. If it were it would cause issues because, usually, the FXMLLoader will create a new Controller when you load new FXML. It does not make sense to have more than one application instance in your application, one created by the java launcher and another by the FXMLLoader (it will just monumentally confuse you). See JavaFX controller class not working for more info on this.

Keeping that in mind, your code can be refactored as below.

sample.Main

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.Stage;

public class Main extends Application {    
    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent main = FXMLLoader.load(getClass().getResource("launcher.fxml"));

        primaryStage.setTitle("Dev Launcher");
        primaryStage.setWidth(900);
        primaryStage.setHeight(560);

        Scene scene = new Scene(main);

        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();
    }

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

sample.Controller

package sample;

import javafx.fxml.FXML;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;

public class Controller {
    @FXML private VBox vbox;

    public void initialize() {
        final WebView browser = new WebView();
        final WebEngine webEngine = browser.getEngine();
        webEngine.load("http://google.com");

        vbox.getChildren().addAll(browser);
    }
}

And your FXML file also should reside in the sample directory. Note in the above code, I changed the name of the VBOX variable to vbox to better fit standard Java naming conventions. For it to work, the variable name must match the fx:id specifier in your FXML (so change fx:id="VBOX" to fx:id="vbox"). Also the controller needs to be set in the FXML fx:controller="sample.Controller" (which you already have).