在 JavaFX webview 中加载一个新页面

Load a new page in JavaFX webview

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

一切正常,但我无法在系统中加载新页面。当我这样做时,Java 和新页面的 Java 脚本之间的通信似乎中断了。

这是我的代码:

** 兄弟 **

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);

    }
}

** 桥梁 **

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);
}
}

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

你的设计对我来说意义不大:将 window (jsobj) 对象作为 Bridge 的一部分对我来说更有意义class,而不是应用程序 class。由于 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 class:

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 文件,我在 class 路径根目录的 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>