Thursday, May 31, 2012

Communication between JavaFX and JavaScript inside WebView, using JSObject

The former articles demonstrate how to "Execute JavaScript in WebView from Java code" and "implement OnAlert EventHandler of WebEngine".

In order to call JavaFX from Javascript in WebView, we can create a JSObject in JavaFX, and then calling it's setMember() method to make it known to Javascript. After that, Javascript can call it's public methods and access it's public field.

Communication between JavaFX and JavaScript inside WebView


JavaFX code:
package javafxweb;

import java.net.URL;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

/**
 *
 * @web http://java-buddy.blogspot.com/
 */
public class JavaFXWeb extends Application {
    
    private Scene scene;
    MyBrowser myBrowser;
    
    Label labelFromJavascript;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }
    
    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("java-buddy.blogspot.com");
        
        myBrowser = new MyBrowser();
        scene = new Scene(myBrowser, 640, 480);
        
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    class MyBrowser extends Region{
        
        HBox toolbar;
        VBox toolbox;
        
        WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();
        
        public MyBrowser(){
            
            final URL urlHello = getClass().getResource("hello.html");
            webEngine.load(urlHello.toExternalForm());
            
            webEngine.getLoadWorker().stateProperty().addListener(
                    new ChangeListener<State>(){
                        
                        @Override
                        public void changed(ObservableValue<? extends State> ov, State oldState, State newState) {
                            if(newState == State.SUCCEEDED){
                                JSObject window = (JSObject)webEngine.executeScript("window");
                                window.setMember("app", new JavaApplication());
                            }
                        }
                    });
            
            
            JSObject window = (JSObject)webEngine.executeScript("window");
            window.setMember("app", new JavaApplication());
            
            final TextField textField = new TextField ();
            textField.setPromptText("Hello! Who are?");
            
            Button buttonEnter = new Button("Enter");
            buttonEnter.setOnAction(new EventHandler<ActionEvent>(){
                
                @Override
                public void handle(ActionEvent arg0) {
                    webEngine.executeScript( " updateHello(' " + textField.getText() + " ') " );
                }
            });
            
            Button buttonClear = new Button("Clear");
            buttonClear.setOnAction(new EventHandler<ActionEvent>(){
                
                @Override
                public void handle(ActionEvent arg0) {
                    webEngine.executeScript( "clearHello()" );
                }
            });
            
            toolbar = new HBox();
            toolbar.setPadding(new Insets(10, 10, 10, 10));
            toolbar.setSpacing(10);
            toolbar.setStyle("-fx-background-color: #336699");
            toolbar.getChildren().addAll(textField, buttonEnter, buttonClear);
            
            toolbox = new VBox();
            labelFromJavascript = new Label();
            toolbox.getChildren().addAll(toolbar, labelFromJavascript);
            labelFromJavascript.setText("Wait");
            
            getChildren().add(toolbox);
            getChildren().add(webView);
            
        }
        
        @Override
        protected void layoutChildren(){
            double w = getWidth();
            double h = getHeight();
            double toolboxHeight = toolbox.prefHeight(w);
            layoutInArea(webView, 0, 0, w, h-toolboxHeight, 0, HPos.CENTER, VPos.CENTER);
            layoutInArea(toolbox, 0, h-toolboxHeight, w, toolboxHeight, 0, HPos.CENTER, VPos.CENTER);
        }
        
    }
    
    public class JavaApplication {
        public void callFromJavascript(String msg) {
            labelFromJavascript.setText("Click from Javascript: " + msg);
        }
    }
    
}


hello.html
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset=utf-8>
<title>Hello Java-Buddy!</title>
 
<script>

function updateHello(user){
  document.getElementById("helloprompt").innerHTML="Hello: " + user;
}
 
function clearHello(user){
  document.getElementById("helloprompt").innerHTML="Hello <a href='http://java-buddy.blogspot.com/'>Java-Buddy</a>";
}

function callJavaFX(){
    var msg = document.getElementById("textto").value;
    app.callFromJavascript(msg);
}
</script>
 
</head>
<body>
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxGfYYk9MPmsy4Fb5yBONPauX59KlswhZvZL_XoWgvPMp2SwHsLorHIo4aWZJsUc9oynuzwstK5MfzjmjmNevnJFjJt1HIhcl-iRH3dnK2twWSeX6MdjSbAz4nyMzaNQE1p-Pajqfg3KKN/s150/duke_44x80.png"/>
<p id="helloprompt">Hello <a href="http://java-buddy.blogspot.com/">Java-Buddy</a></p>
<p><textarea id="textto"> </textarea></p>
<p><input type="button" value="Click to send message to Javascript" onclick="callJavaFX()"></p>
</body>
</html>


5 comments:

  1. I am unable to get the value from JavaScript.. i.e. when i am clicking on get value from Java script i am not getting the value...

    ReplyDelete
  2. TNX BROV... :D :D u solvd my Day

    ReplyDelete
  3. Hey...... This is Not Working In Java 8.0_121, sometimes is working , but some times is not
    can you please help me to resolve this.

    ReplyDelete
    Replies
    1. See https://stackoverflow.com/questions/41903154/javafx-webview-callback-from-javascript-failing-after-garbage-collection

      'new JavaApplication()' should be saved in a field.

      Delete