JavaFX edit WebView in FXML document

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-10 10:52:29

问题


My goal is to be able to put html content into a WebView object in the fxml document using the controller class. My FXML document has other objects in it like buttons and images, I want the WebView just to be part of the GUI. I can put content inside a TextArea in the FXML document using the controller class. Doing this for WebView is a bit more tricky because it requires a WebEngine to go along with it. I know how to launch a WebView by itself without am FXML doc but does anyone know if my goal is achievable?

This is my attempt in the controller class, but I get an invocation target exception:

public class FXMLDocumentController implements Initializable {

    @FXML
    private Label label;
    WebEngine engine;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }

    //access WebView in FXML document
    @FXML WebView mywebview; //mywebview is the fxid
    public void displayWeb() {
        engine = mywebview.getEngine();
        final String hellohtml = "chang.htm"; //HTML file to view in web view
        URL urlHello = getClass().getResource(hellohtml);
        engine.load(urlHello.toExternalForm());
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        displayWeb();
    }     

}

回答1:


This is a kind of neat concept (at least if I understood what you are asking :-). I like this. Kudos for thinking of it.

In re-reading your question, I may have completely misunderstood it... if so, I guess you can disregard my answer.

Specifying HTML content inline in FXML

Unfortunately, WebView is final, so you can't just extend WebView to add content loading methods to an element which can be specified in FXML.

The solution is to provide a small wrapper class around the WebView that FXML can instantiate and set content into. I chose to have the wrapper class inherit StackPane, so that the wrapper is a Node and can be instantiated in FXML wherever you might want to use a Node.

You might be able to use a Builder class rather than a wrapper class like I have, but documentation on doing that for FXML is pretty scarce to nonexistent, so I didn't try it.

As a convenience, wrap the embedded html content in a CDATA construct. Then you don't have to escape all the html characters and can leave <, >, and others as is rather than re-encoding those characters as &lt;, &gt;, etc.

embeddedwebview/embedded-webview.fxml

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.VBox?>
<?import embeddedwebview.EmbeddedWebView?>
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
      prefHeight="150.0" prefWidth="220.0">
  <padding>
    <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
  </padding>
  <EmbeddedWebView fx:id="embeddedWebView">
    <content>
      <![CDATA[
        <h3>Embedded WebView</h3>
        <p>HTML content inline in FXML</p>
      ]]>
    </content>
  </EmbeddedWebView>
</VBox>

embeddedwebview/EmbeddedWebViewApp.java

package embeddedwebview;

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

public class EmbeddedWebViewApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        FXMLLoader loader = new FXMLLoader(
                getClass().getResource(
                        "embedded-webview.fxml"
                )
        );
        Pane pane = loader.load();

        stage.setScene(new Scene(pane));
        stage.show();
    }

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

embeddedwebview/EmbeddedWebView.java

import javafx.scene.layout.StackPane;
import javafx.scene.web.WebView;

/**
 * A WebView which has getters and setters for content or a document url.
 *
 * Usage in FXML element is: 
 * 
 *   EITHER by specifying a url to a html document:
 * 
 *      <EmbeddedWebView fx:id="embeddedWebView" url="/embeddedwebview/embedded.html">
 *          
 *   OR by specifying CDATA escaped html content:
 *   
 *     <EmbeddedWebView fx:id="embeddedWebView">
 *         <content>
 *             <![CDATA[
 *                 <h3>Embedded WebView</h3>
 *                 <p>HTML content inline in FXML</p>
 *             ]]>
 *         </content>
 *     </EmbeddedWebView>
 *
 */
public class EmbeddedWebView extends StackPane {

    final private WebView webView;

    // For space efficiency, an alternate implementation could just 
    // rely on the content in the WebView itself rather than 
    // duplicating the content here, but it was simple to implement with a duplicate. 
    private String content;

    private String url;

    public EmbeddedWebView() {
        webView = new WebView();
        getChildren().add(webView);
    }

    public String getContent() {
        return content;
    }

    /**
     * Loads html content directly into the webview.
     * @param content a html content string to load into the webview.
     */
    public void setContent(String content) {
        this.content = content;
        webView.getEngine().loadContent(content);
    }

    public String getUrl() {
        return url;
    }

    /**
     * Loads content into the WebView from a given url.
     * The allowed url types are http, https and file.
     *
     * Additionally, content can be loaded from a classpath resource.
     * To be loaded from the classpath, the url must start with a / character
     * and specify the full resource path to the html
     * (i.e., relative resource path specifiers are not allowed).
     *
     * @param url the location of the html document to be loaded.
     */
    public void setUrl(String url) {
        if ( url == null || ! (url.startsWith("/") || url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:"))) {
            throw new IllegalArgumentException("url must start with one of http: file: https: or /");
        }

        this.url = url;

        if (url.startsWith("/")) {
            webView.getEngine().load(
                    EmbeddedWebView.class.getResource(url).toExternalForm()
            );
        } else {
            webView.getEngine().load(
                    url
            );
        }
    }
}

Alternate usage referencing a html document.

Replace the EmbeddedWebView element in the above fxml with the following:

<EmbeddedWebView fx:id="embeddedWebView" url="/embeddedwebview/embedded.html"/>

embeddedwebview/embedded.html

<!doctype html>

<html lang="en">
<head>
    <meta charset="utf-8">
</head>

<body>
    <h3>Embedded WebView</h3>
    <p>HTML content inline in FXML</p>
</body>
</html>

So does the setUrl function set the global variabe url which getUrl returns?

The url member is local to the EmbeddedWebView rather than global is scope to the application, but yes, you have the general idea, setURL() is a setter and FXML looks it up by reflection to set the url in the EmbeddedWebView and you can latter retrieve the url out of the EmbeddedWebView from any class using the public getURL() function.



来源:https://stackoverflow.com/questions/29353547/javafx-edit-webview-in-fxml-document

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!