Руководство JavaFX WebView и WebEngine

1- JavaFX WebView

JavaFX WebView это минибраузер, так же называется встроенным браузером (embedded browser) в приложение  JavaFX. Этот браузер основан на WebKit, браузерный движок (browser engine) с открытым исходным кодом, поддерживает CSS, JavaScript, DOM и HTML5.
JavaFX WebView позволяет вам выполнять следующие задачи в приложении  JavaFX:
  • Изображает содержание HTML от локальных и удаленных URL
  • Собирает историю веб
  • Выполняет команды Javascript
  • Выполняет вызовы от Javascript до JavaFX
  • Управляет поп ап окна в веб
  • Применяет эффекты для встроенного браузера
В настоящей версии ( JavaFX 2.3) компонент  WebView поддерживает следующие свойства HTML5:
  • Canvas
  • Media Playback
  • Form controls (except for <input type="color"> )
  • Editable content
  • History maintenance
  • Support for the <meter> and <progress> tags.
  • Support for the <details> and <summary> tags.
  • DOM
  • SVG
  • Support for domain names written in national languages
Следующее изображение является структурой встренного браузера в JavaFX:

WebEngine

Класс  WebEngine предоставляет основную функцию веб страницы. Он поддерживает в интерктивности пользователя, как навигация ссылок и  submit form HTML, несмотря на то что он не интерактирует напрямую с пользователем. Класс  WebEngine обрабатывает веб страницу одновременно. Он поддерживает основные веб ствойства загрузки содержания  HTML и доступ в  DOM так же выполняет команды  JavaScript.

WebView

Класс  WebView расширен из класса  Node, он оборачивает объект  WebEngine и отображает содержание Html. Вы можете получить объект WebEngine из  WebView используя метод  getEngine().
// Create a WebView
WebView browser = new WebView();

// Get WebEngine via WebView
WebEngine webEngine = browser.getEngine();

// Load page
webEngine.load("http://eclipse.com");

2- Пример с WebView

Загрузить удаленный URL.
WebView browser = new WebView();
WebEngine webEngine = browser.getEngine();

String url = "https://eclipse.org";

// Load a page from remote url.
webEngine.load(url);
Помимо отображения данных из удаленного URL, вы так же можете отобразить содержание статистического HTML.
// A HTML text

String html = "<html><h1>Hello</h1><h2>Hello</h2></html>";

// Load content.
webEngine.loadContent(html);

// Or
webEngine.loadContent(html,"text/html");
Или загрузить содержание html из локального файла.
File file = new File("C:/test/a.html");
URL url= file.toURI().toURL();

// file:/C:/test/a.html
webEngine.load(url.toString());
Посмотрим полный пример с  WebView. Заметьте, что  WebView по умолчанию установил  ScrollPane, полосы прокрутки (scroll) появятся когда содержание веб больше, чем регион отображения.
WebViewDemo.java
package org.o7planning.javafx.webview;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;

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.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewDemo extends Application {

    @Override
    public void start(final Stage stage) {

        Button buttonURL = new Button("Load Page https://eclipse.org");
        Button buttonHtmlString = new Button("Load HTML String");
        Button buttonHtmlFile = new Button("Load File C:/test/a.html");

        final WebView browser = new WebView();
        final WebEngine webEngine = browser.getEngine();

        buttonURL.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                String url = "https://eclipse.org";
                // Load a page from remote url.
                webEngine.load(url);
            }
        });

        buttonHtmlString.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                String html = "<html><h1>Hello</h1><h2>Hello</h2></html>";
                // Load HTML String
                webEngine.loadContent(html);
            }
        });
        buttonHtmlFile.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                try {
                    File file = new File("C:/test/a.html");
                    URL url = file.toURI().toURL();
                    // file:/C:/test/a.html
                    System.out.println("Local URL: " + url.toString());
                    webEngine.load(url.toString());
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                }

            }
        });

        VBox root = new VBox();
        root.setPadding(new Insets(5));
        root.setSpacing(5);
        root.getChildren().addAll(buttonURL, buttonHtmlString, buttonHtmlFile, browser);

        Scene scene = new Scene(root);

        stage.setTitle("JavaFX WebView (o7planning.org)");
        stage.setScene(scene);
        stage.setWidth(450);
        stage.setHeight(300);

        stage.show();
    }

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

}

3- Пример WebView с ProgressBar

Чтобы загрузить вебсайт на браузер требуется определенное время. Иногда вам нужно использовать ProgressBar чтобы отобразить процент загружения вебсайта.
WebViewWithProgressDemo.java
package org.o7planning.javafx.webview;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewWithProgressDemo extends Application {

   @Override
   public void start(final Stage stage) {

       TextField addressBar = new TextField();
       addressBar.setText("https://eclipse.org");
       Button goButton = new Button("Go!");
       Label stateLabel = new Label();

       stateLabel.setTextFill(Color.RED);
       ProgressBar progressBar = new ProgressBar();

       final WebView browser = new WebView();
       final WebEngine webEngine = browser.getEngine();

       // A Worker load the page
       Worker<Void> worker = webEngine.getLoadWorker();

        // Listening to the status of worker
       worker.stateProperty().addListener(new ChangeListener<State>() {

           @Override
           public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
               stateLabel.setText("Loading state: " + newValue.toString());
               if (newValue == Worker.State.SUCCEEDED) {
                   stage.setTitle(webEngine.getLocation());
                   stateLabel.setText("Finish!");
               }
           }
       });

       // Bind the progress property of ProgressBar
       // with progress property of Worker
       progressBar.progressProperty().bind(worker.progressProperty());

       goButton.setOnAction(new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               String url = addressBar.getText();
               // Load the page.
               webEngine.load(url);
           }
       });
       //

       VBox root = new VBox();
       root.getChildren().addAll(addressBar, goButton, stateLabel, progressBar, browser);

       Scene scene = new Scene(root);

       stage.setTitle("JavaFX WebView (o7planning.org)");
       stage.setScene(scene);
       stage.setWidth(450);
       stage.setHeight(300);

       stage.show();
   }

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

}

4- Вызов Javascript из JavaFX

После того как  WebView загрузил вебсайт, вы можете интерактировать с вебсайтом из JavaFX. В примере ниже пользователь нажимает на  Button в приложении  JavaFX он вызывает функцию  Javascript отображенной страницы на   WebView.
// Enable Javascript.
webEngine.setJavaScriptEnabled(true);

// Call a JavaScript function of the current page
webEngine.executeScript("changeBgColor();");A
WebViewExecuteJsDemo.java
package org.o7planning.javafx.webview;

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.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewExecuteJsDemo extends Application {

   // A HTML Content with a javascript function.
   private static String HTML_STRING = //
           "<html>"//
                   + "<head> " //
                   + "  <script language='javascript'> " //
                   + "     function changeBgColor()  { "//
                   + "       var color= document.getElementById('color').value; "//
                   + "       document.body.style.backgroundColor= color; " //
                   + "     } " //
                   + "  </script> "//
                   + "</head> "//
                   + "<body> "//
                   + "   <h2>This is Html content</h2> "//
                   + "   <b>Enter Color:</b> "//
                   + "   <input id='color' value='yellow' /> "//
                   + "   <button onclick='changeBgColor();'>Change Bg Color</button> "//
                   + "</body> "//
                   + "</html> "//
   ;

   @Override
   public void start(final Stage stage) {

       Button button = new Button("Execute Javascript (Call from JavaFX)");

       final WebView browser = new WebView();
       final WebEngine webEngine = browser.getEngine();

       // Enable Javascript.
       webEngine.setJavaScriptEnabled(true);

       webEngine.loadContent(HTML_STRING);

       button.setOnAction(new EventHandler<ActionEvent>() {

           @Override
           public void handle(ActionEvent event) {
               // Call a JavaScript function of the current page
               webEngine.executeScript("changeBgColor();");
           }
       });

       VBox root = new VBox();
       root.setPadding(new Insets(5));
       root.setSpacing(5);
       root.getChildren().addAll(button, browser);

       Scene scene = new Scene(root);

       stage.setTitle("JavaFX WebView (o7planning.org)");
       stage.setScene(scene);
       stage.setWidth(450);
       stage.setHeight(300);

       stage.show();
   }

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

}
Вы так же можете войти в объекты Javascript через объекты  Java. Большинство объектов  Javascript обернуты  netscape.javascript.JSObject.  Методы  JSObject:
public Object call(String methodName, Object... args);

public Object eval(String s);

public Object getMember(String name);

public void setMember(String name, Object value);

public void removeMember(String name);

public Object getSlot(int index);

public void setSlot(int index, Object value);
Пример:
// Back Browser History

webEngine.executeScript("history.back()");

// Or
// Get the object representing the history of JavaScript objects JSObject.
JSObject history = (JSObject) webEngine.executeScript("history");

// Call 'back' method, without parameter.
history.call("back");
Особенный случай, когда при вызове JavaScript возвращается объект  DOM ( DOM Node). В данном случае, результат обернут объектом  JSObject он выполняет интерфейс  org.w3c.dom.Node.
Element p = (Element) ebEngine.executeScript("document.getElementById('para')");

p.setAttribute("style", "font-weight: bold");

5- Создание вызова Upcalls от JavaScript до JavaFX

Выше, вы могли вызвать функцию Javascript страницы отображенной на  WebView из  JavaFX. В обратном, вы так же можете создать вызовы Upcalls от  Javascript до  JavaFX.
Со стороны  JavaFX, вам нужно создать объект интерфейса (лобого класса) и удостовериться, что он знаком для JavaScript через вызов  JSObject.setMember(). После выполнения этого, вы можете вызвать методы public или поля public объекта Java от  Javascript.
// A Bridge class and must a public class
public class Bridge {

    public void showTime() {
        System.out.println("Show Time");

        label.setText("Now is: " + df.format(new Date()));
    }
}

// Get window object of page.
JSObject jsobj = (JSObject) webEngine.executeScript("window");

// Set member cho đối tượng 'window'
jsobj.setMember("myJavaMember", new Bridge());
В странице HTML:
<!-- In HTML -->

<button onclick='myJavaMember.showTime();'>Call To JavaFX</button>
Смотреть полный пример:
WebViewUpCallsDemo.java
package org.o7planning.javafx.webview;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;

public class WebViewUpCallsDemo extends Application {

    private DateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

    private Label label;

    // A Bridge class and must a public class
    public class Bridge {

        public void showTime() {
            System.out.println("Show Time");

            label.setText("Now is: " + df.format(new Date()));
        }
    }

    // A HTML Content with a javascript function.
    private static String HTML_STRING = //
            "<html>"//
                    + "<head> " //
                    + "  <script language='javascript'> " //
                    + "     function callToJavaFX()  { "//
                    + "        myJavaMember.showTime(); " //
                    + "     } " //
                    + "  </script> "//
                    + "</head> "//
                    + "<body> "//
                    + "   <h2>This is Html content</h2> "//
                    + "   <button onclick='callToJavaFX();'>Call To JavaFX</button> "//
                    + "</body> "//
                    + "</html> "//
    ;

    @Override
    public void start(final Stage stage) {

        label = new Label("-");

        final WebView browser = new WebView();
        final WebEngine webEngine = browser.getEngine();

        // Enable Javascript.
        webEngine.setJavaScriptEnabled(true);

        // A Worker load the page
        Worker<Void> worker = webEngine.getLoadWorker();

        // Listening to the status of worker
        worker.stateProperty().addListener(new ChangeListener<State>() {

            @Override
            public void changed(ObservableValue<? extends State> observable, //
                    State oldValue, State newValue) {

                // When load successed.
                if (newValue == Worker.State.SUCCEEDED) {
                    // Get window object of page.
                    JSObject jsobj = (JSObject) webEngine.executeScript("window");

                    // Set member for 'window' object.
                    // In Javascript access: window.myJavaMember....
                    jsobj.setMember("myJavaMember", new Bridge());
                }
            }
        });

        // Load HTML content.
        // Tải nội dung HTML
        webEngine.loadContent(HTML_STRING);

        VBox root = new VBox();
        root.setPadding(new Insets(5));
        root.setSpacing(5);
        root.getChildren().addAll(label, browser);

        Scene scene = new Scene(root);

        stage.setTitle("JavaFX WebView (o7planning.org)");
        stage.setScene(scene);
        stage.setWidth(450);
        stage.setHeight(300);

        stage.show();
    }

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

}