Skip to content
Advertisement

JavaFX invoking Java methods using JavaScript

My application is completely styled in a web document form (HTML, CSS & JavaScript), and I’m only using JavaFX WebView to load it as a normal resource.

I would like to invoke a method from one of my classes (a Java code) using JavaScript.

Like for example, a simple Hello World to the console:

public class Hello {
  public void world() {
    System.out.println("Hello World!");
  }
}

How can I invoke the world() method in this case?

So my code from the page is something like this:

<!-- onlick action from a button calls hello() function -->
<button onclick="hello();" value="Invoke"></button>

<script>
  function hello() {
    /* CODE WHICH INVOKE A JAVA METHOD */
  }
</script>

Any way to achieve this?


UPDATE

Notice: For those people who were looking for a complete and simple example about how to achieve this, you can test all the following codes written below.

I’ve finally achieved my goal, thanks to sir @Oshan_Mendis’ answer. This example is based from this tutorial from Oracle docs: 6 Making Upcalls from JavaScript to JavaFX.

But here, I’ll be using my own code, the main goal is to call a method from Java code using JavaScript from the HTML page.

File contents:

Controller.java   /* Controller class for WebView */
Hello.java        /* Class in which method(s) will be invoked */
Main.java         /* Main class (launches the application) */
main.fxml         /* Main layout (WebView) */
index.html        /* Main layout web page content */

1. Creating the Main-Class (Main.java)

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {
  @Override
  public void start(Stage stage) throws Exception {
    /* The root layout of the application, an FXML contains the WebView layout. */
    Parent root = FXMLLoader.load(Main.class.getResource("/main.fxml"));
    Scene scene = new Scene(root);
    stage.setScene(scene);
    stage.show();
  }
  public static void main(String[] args) {
    launch(args);
  }
}

2. Preparing the Main layout (main.fxml)

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

<?import javafx.scene.web.WebView?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx/8"
      xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="Controller"  <!-- The controller class for this layout -->
      prefHeight="400.0"
      prefWidth="300.0">
  <children>
    <!-- Given the webView ID to initiate the web page -->
    <WebView fx:id="webView" />
  </children>
</VBox>

3. Setting up the web page (Controller.java)

import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;

public class Controller implements Initializable {
  private WebEngine webEngine;
  @FXML private WebView webView;
  @Override
  public void initialize(URL location, ResourceBundle resources) {
    /* Load the web page URL (location of the resource) */
    URL url = Controller.class.getResource("/index.html");
    webEngine = webView.getEngine();
    webEngine.load(url.toExternalForm());
    /* Set the State listener as well as the name of the JavaScript object and its
     * corresponding Java object (the class in which methods will be invoked) that
     * will serve as the bridge for the two objects.
     */
    webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
      @Override
      public void changed(ObservableValue<? extends State> observableValue, State oldState, State newState) {
        if (newState == State.SUCCEEDED) {
          JSObject window = (JSObject) webEngine.executeScript("window");
          /* The two objects are named using the setMember() method. */
          window.setMember("invoke", new Hello());
        }
      }
    });
  }
}

4. Preferred class and its method to invoke (Hello.java)

public class Hello {
  public void world() {
    System.out.println("Hello World!");
  }
}

5. Main layout web page content (index.html)

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
      function helloWorld() {
        /* JavaScript object name and the method to invoke */
        invoke.world();
      }
    </script>
  </head>
  <body>
    <!-- onlick event calls helloWorld() function -->
    <button onclick="helloWorld()">INVOKE</button>
  </body>
</html>

Note: You can perform other mouse-related events other than onclick event in this case like: onmouseenter, onmouseover, onmousemove, onmouseup, etc.. But I’m not really sure if these are the only supported events of invoking methods.

Advertisement

Answer

This is well explained in the Java API Documentation under Calling back to Java from JavaScript

public class JavaApplication {
    public void exit() {
        Platform.exit();
    }
}
...
JavaApplication javaApp = new JavaApplication();
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", javaApp);

You can then refer to the object and the method from your HTML page:

<a href="" onclick="app.exit()">Click here to exit application</a>
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement