在JavaFX webview中加载一个新页面

Load a new page in JavaFX webview

本文关键字:一个 新页面 JavaFX webview 加载      更新时间:2023-09-26

我目前正在开发一个Java应用程序,该应用程序使用JavaFX webview来显示其UI(使用HTML/CSS)。

一切都很好,但我在系统中加载新页面时遇到麻烦。当我这样做时,Java和新页面的JavaScript之间的通信似乎被破坏了。

下面是我的代码:

**浏览器**

public class Browser extends Region {
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
public Browser() {
    //apply the styles
    getStyleClass().add("browser");
    // load the web page
    webEngine.load(some_url);
    JSObject jsobj = (JSObject) webEngine.executeScript("window");
    Bridge bridge = Bridge.getInstance();
    bridge.init(webEngine);
    jsobj.setMember("java", bridge);
    //add the web view to the scene
    getChildren().add(browser);
    }
}

** Bridge **

public class Bridge {
private static Bridge instance = null;
private WebEngine webEngine;
public Bridge () {
}
public static Bridge getInstance() {
    if(instance == null){
        instance = new Bridge();
    }
    return instance;
}
public void init(WebEngine webEngine) {
    if(this.webEngine == null) {
        this.webEngine = webEngine;
    }
}
public void btnStartSessionOnClick(String sessionName, String speakerNickname) {
    // Load the new page
    webEngine.load(some_other_url);
}
}

每当web引擎加载一个新页面时,它都会替换DOM,因此有一个不同的window对象。您定义的jsobj只设置一次,因此当加载新页面时,它将指向错误的对象。您需要在每次页面加载时重置此对象,您可以通过观察引擎的加载状态来完成。

你的设计对我来说不是很有意义:对我来说,把window (jsobj)对象作为Bridge类的一部分而不是应用程序类的一部分更有意义。因为Browser不是一个单例,它没有意义,使Bridge成为一个单例(如果你有多个web视图在你的应用程序,例如?)。

这是一个SSCCE:

package application;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewTest extends Application {
    @Override
    public void start(Stage primaryStage) {
        WebView webView = new WebView();
        WebEngine engine = webView.getEngine();
        Label output = new Label();
        Bridge bridge = new Bridge(engine);
        engine.load(getClass().getResource("/resources/First.html").toExternalForm());
        Button first = new Button("Load First");
        first.setOnAction(e -> engine.load(getClass().getResource("/resources/First.html").toExternalForm()));
        Button second = new Button("Load Second");
        second.setOnAction(e -> engine.load(getClass().getResource("/resources/Second.html").toExternalForm()));
        TextField textField = new TextField();
        Button button = new Button("Send");
        EventHandler<ActionEvent> handler = e -> {
            bridge.execute(result -> output.setText(result.toString()),
                    "showText", textField.getText());
            textField.setText("");
        };
        button.setOnAction(handler);
        textField.setOnAction(handler);
        HBox controls = new HBox(5, first, second, textField, button, new Label("Web page says: "), output);
        controls.setPadding(new Insets(10));
        BorderPane root = new BorderPane(webView, null, null, controls, null);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Bridge类:

package application;
import java.util.function.Consumer;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;
public class Bridge {

    private JSObject window ;
    public Bridge(WebEngine engine) {
        engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == State.SUCCEEDED) {
                window = (JSObject) engine.executeScript("window");
                window.setMember("application", this);
            }
        });
    }
    public void execute(Consumer<Object> callback, String function, Object... args) {
        callback.accept(window.call(function, args));
    }
}

和一些简单的测试HTML文件,我在类路径根目录下的resources文件夹中。

First.HTML:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>First</title>
<script>
    function showText(text) {
        document.getElementById("text").innerHTML = text;
        return text;
    }
</script>
</head>
<body>
    <p>This is the first page</p>
    <a href="Second.html">Go to the second page</a>
    <div id="text"></div>
</body>
</html>

和Second.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Second</title>
<script>
    function showText(text) {
        document.getElementById("text").innerHTML = text;
        return text;
    }
</script>
</head>
<body>
    <p>This is the second page</p>
    <a href="First.html">Go back to the first page</a>
    <div id="text"></div>
</body>
</html>