JavaFX редактировать WebView в документе FXML

1

Моя цель - разместить html-содержимое в объекте WebView в документе fxml, используя класс контроллера. У моего документа FXML есть другие объекты в нем, как кнопки и изображения, я хочу, чтобы WebView просто был частью графического интерфейса. Я могу разместить контент внутри TextArea в документе FXML, используя класс контроллера. Сделать это для WebView немного сложнее, потому что для этого требуется, чтобы WebEngine согласился с этим. Я знаю, как запустить WebView самостоятельно без документа FXML, но кто-нибудь знает, возможна ли моя цель?

Это моя попытка в классе контроллера, но я получаю исключение целевой задачи:

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

}
  • 0
    Пожалуйста, опубликуйте трассировку стека из исключения и определите строку, где происходит исключение.
Теги:
javafx
webview
javafx-webengine

1 ответ

1
Лучший ответ

Это своего рода аккуратная концепция (по крайней мере, если я понял, о чем вы спрашиваете :-). Мне это нравится. Престижность за это.

Перечитывая свой вопрос, я, возможно, совершенно не понял его... если так, я думаю, вы можете игнорировать мой ответ.

Указание HTML-контента в формате FXML

К сожалению, WebView является окончательным, поэтому вы не можете просто расширить WebView, чтобы добавить методы загрузки контента к элементу, который может быть указан в FXML.

Решение состоит в том, чтобы обеспечить небольшой класс оболочки вокруг WebView, который FXML может создавать и устанавливать в контент. Я решил, что класс оболочки наследует StackPane, так что оболочка является узлом и может быть создана в FXML везде, где вы, возможно, захотите использовать узел.

Возможно, вы сможете использовать класс Builder, а не класс-оболочку, как я, но документация по этому поводу для FXML довольно скудна до несуществующей, поэтому я не пробовал.

Для удобства оберните встроенный контент html в конструкцию CDATA. Тогда вам не нужно избегать всех символов html и оставить <, > и другие, а не перекодировать эти символы как &lt; , &gt; , и т.д.

Изображение 174551

embeddedwebview/погруженного 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
            );
        }
    }
}

Альтернативное использование ссылок на html-документ.

Замените элемент EmbeddedWebView в приведенном выше файле fxml следующим образом:

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

Таким образом, функция setUrl задает глобальный переменный url, который возвращает getUrl?

Элемент url является локальным для EmbeddedWebView, а не глобальным - это область приложения, но да, у вас есть общая идея, setURL() - это сеттер, и FXML ищет его отражением для установки URL-адреса в EmbeddedWebView, и вы можете извлеките url из EmbeddedWebView из любого класса, используя общедоступную функцию getURL().

  • 0
    Вау, @jewelsea, это потрясающая реализация и именно то, что я искал. Спасибо, что сделали все возможное! Так устанавливает ли функция setUrl глобальный URL-адрес переменной, который возвращает getUrl? Это очень креативно.
  • 0
    Обновленный ответ на дополнительный вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню